diff --git a/src/app/+admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.spec.ts b/src/app/+admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.spec.ts index 2f3f88fa70..6436f2d873 100644 --- a/src/app/+admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.spec.ts +++ b/src/app/+admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.spec.ts @@ -18,6 +18,7 @@ import { WorkflowItemSearchResult } from '../../../../../shared/object-collectio import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service'; import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils'; import { getMockLinkService } from '../../../../../shared/mocks/link-service.mock'; +import { of as observableOf } from 'rxjs'; describe('WorkflowItemAdminWorkflowGridElementComponent', () => { let component: WorkflowItemSearchResultAdminWorkflowGridElementComponent; @@ -50,7 +51,9 @@ describe('WorkflowItemAdminWorkflowGridElementComponent', () => { ], providers: [ { provide: LinkService, useValue: linkService }, - { provide: TruncatableService, useValue: {} }, + { provide: TruncatableService, useValue: { + isCollapsed: () => observableOf(true), + } }, { provide: BitstreamDataService, useValue: {} }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/+bitstream-page/bitstream-page.resolver.ts b/src/app/+bitstream-page/bitstream-page.resolver.ts index 8e9f64fcc1..9c85688c39 100644 --- a/src/app/+bitstream-page/bitstream-page.resolver.ts +++ b/src/app/+bitstream-page/bitstream-page.resolver.ts @@ -6,6 +6,7 @@ import { find } from 'rxjs/operators'; import { hasValue } from '../shared/empty.util'; import { Bitstream } from '../core/shared/bitstream.model'; import { BitstreamDataService } from '../core/data/bitstream-data.service'; +import {followLink, FollowLinkConfig} from '../shared/utils/follow-link-config.model'; /** * This class represents a resolver that requests a specific bitstream before the route is activated @@ -23,9 +24,20 @@ export class BitstreamPageResolver implements Resolve> { * or an error if something went wrong */ resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable> { - return this.bitstreamService.findById(route.params.id) + return this.bitstreamService.findById(route.params.id, ...this.followLinks) .pipe( find((RD) => hasValue(RD.error) || RD.hasSucceeded), ); } + /** + * Method that returns the follow links to already resolve + * The self links defined in this list are expected to be requested somewhere in the near future + * Requesting them as embeds will limit the number of requests + */ + get followLinks(): Array> { + return [ + followLink('bundle', undefined, true, followLink('item')), + followLink('format') + ]; + } } diff --git a/src/app/+bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts b/src/app/+bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts index c802622dc4..ce46c2a7b3 100644 --- a/src/app/+bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts +++ b/src/app/+bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts @@ -4,7 +4,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { RouterTestingModule } from '@angular/router/testing'; import { RemoteData } from '../../core/data/remote-data'; import { of as observableOf } from 'rxjs/internal/observable/of'; -import { ActivatedRoute } from '@angular/router'; +import {ActivatedRoute, Router} from '@angular/router'; import { DynamicFormControlModel, DynamicFormService } from '@ng-dynamic-forms/core'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { BitstreamDataService } from '../../core/data/bitstream-data.service'; @@ -22,6 +22,11 @@ import { PageInfo } from '../../core/shared/page-info.model'; import { FileSizePipe } from '../../shared/utils/file-size-pipe'; import { RestResponse } from '../../core/cache/response.models'; import { VarDirective } from '../../shared/utils/var.directive'; +import { + createSuccessfulRemoteDataObject$ +} from '../../shared/remote-data.utils'; +import {RouterStub} from '../../shared/testing/router.stub'; +import { getItemEditRoute } from '../../+item-page/item-page-routing-paths'; const infoNotification: INotification = new Notification('id', NotificationType.Info, 'info'); const warningNotification: INotification = new Notification('id', NotificationType.Warning, 'warning'); @@ -34,6 +39,8 @@ let bitstreamFormatService: BitstreamFormatDataService; let bitstream: Bitstream; let selectedFormat: BitstreamFormat; let allFormats: BitstreamFormat[]; +let router: Router; +let routerStub; describe('EditBitstreamPageComponent', () => { let comp: EditBitstreamPageComponent; @@ -105,7 +112,12 @@ describe('EditBitstreamPageComponent', () => { format: observableOf(new RemoteData(false, false, true, null, selectedFormat)), _links: { self: 'bitstream-selflink' - } + }, + bundle: createSuccessfulRemoteDataObject$({ + item: createSuccessfulRemoteDataObject$({ + uuid: 'some-uuid' + }) + }) }); bitstreamService = jasmine.createSpyObj('bitstreamService', { findById: observableOf(new RemoteData(false, false, true, null, bitstream)), @@ -118,6 +130,10 @@ describe('EditBitstreamPageComponent', () => { findAll: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), allFormats))) }); + const itemPageUrl = `fake-url/some-uuid`; + routerStub = Object.assign(new RouterStub(), { + url: `${itemPageUrl}` + }); TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), RouterTestingModule], declarations: [EditBitstreamPageComponent, FileSizePipe, VarDirective], @@ -127,6 +143,7 @@ describe('EditBitstreamPageComponent', () => { { provide: ActivatedRoute, useValue: { data: observableOf({ bitstream: new RemoteData(false, false, true, null, bitstream) }), snapshot: { queryParams: {} } } }, { provide: BitstreamDataService, useValue: bitstreamService }, { provide: BitstreamFormatDataService, useValue: bitstreamFormatService }, + { provide: Router, useValue: routerStub }, ChangeDetectorRef ], schemas: [NO_ERRORS_SCHEMA] @@ -138,6 +155,7 @@ describe('EditBitstreamPageComponent', () => { fixture = TestBed.createComponent(EditBitstreamPageComponent); comp = fixture.componentInstance; fixture.detectChanges(); + router = (comp as any).router; }); describe('on startup', () => { @@ -213,4 +231,25 @@ describe('EditBitstreamPageComponent', () => { }); }); }); + describe('when the cancel button is clicked', () => { + it('should call navigateToItemEditBitstreams method', () => { + spyOn(comp, 'navigateToItemEditBitstreams'); + comp.onCancel(); + expect(comp.navigateToItemEditBitstreams).toHaveBeenCalled(); + }); + }); + describe('when navigateToItemEditBitstreams is called, and the component has an itemId', () => { + it('should redirect to the item edit page on the bitstreams tab with the itemId from the component', () => { + comp.itemId = 'some-uuid1' + comp.navigateToItemEditBitstreams(); + expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditRoute('some-uuid1'), 'bitstreams']); + }); + }); + describe('when navigateToItemEditBitstreams is called, and the component does not have an itemId', () => { + it('should redirect to the item edit page on the bitstreams tab with the itemId from the bundle links ', () => { + comp.itemId = undefined; + comp.navigateToItemEditBitstreams(); + expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditRoute('some-uuid'), 'bitstreams']); + }); + }); }); diff --git a/src/app/+bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts b/src/app/+bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts index 3e8b686e48..ad64739dac 100644 --- a/src/app/+bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts +++ b/src/app/+bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; import { Bitstream } from '../../core/shared/bitstream.model'; import { ActivatedRoute, Router } from '@angular/router'; -import { filter, map, switchMap } from 'rxjs/operators'; +import { map, mergeMap, switchMap} from 'rxjs/operators'; import { combineLatest as observableCombineLatest, of as observableOf } from 'rxjs'; import { Subscription } from 'rxjs/internal/Subscription'; import { @@ -19,7 +19,7 @@ import { DynamicCustomSwitchModel } from '../../shared/form/builder/ds-dynamic-f import { cloneDeep } from 'lodash'; import { BitstreamDataService } from '../../core/data/bitstream-data.service'; import { - getAllSucceededRemoteData, getAllSucceededRemoteDataPayload, + getAllSucceededRemoteDataPayload, getFirstSucceededRemoteDataPayload, getRemoteDataPayload, getSucceededRemoteData @@ -35,8 +35,9 @@ import { Location } from '@angular/common'; import { Observable } from 'rxjs/internal/Observable'; import { RemoteData } from '../../core/data/remote-data'; import { PaginatedList } from '../../core/data/paginated-list'; -import { followLink } from '../../shared/utils/follow-link-config.model'; import { getItemEditRoute } from '../../+item-page/item-page-routing-paths'; +import {Bundle} from '../../core/shared/bundle.model'; +import {Item} from '../../core/shared/item.model'; @Component({ selector: 'ds-edit-bitstream-page', @@ -299,12 +300,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { const bitstream$ = this.bitstreamRD$.pipe( getSucceededRemoteData(), - getRemoteDataPayload(), - switchMap((bitstream: Bitstream) => this.bitstreamService.findById(bitstream.id, followLink('format')).pipe( - getAllSucceededRemoteData(), - getRemoteDataPayload(), - filter((bs: Bitstream) => hasValue(bs))) - ) + getRemoteDataPayload() ); const allFormats$ = this.bitstreamFormatsRD$.pipe( @@ -501,14 +497,18 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { } /** - * When the item ID is present, navigate back to the item's edit bitstreams page, otherwise go back to the previous - * page the user came from + * When the item ID is present, navigate back to the item's edit bitstreams page, + * otherwise retrieve the item ID based on the owning bundle's link */ navigateToItemEditBitstreams() { if (hasValue(this.itemId)) { this.router.navigate([getItemEditRoute(this.itemId), 'bitstreams']); } else { - this.location.back(); + this.bitstream.bundle.pipe(getFirstSucceededRemoteDataPayload(), + mergeMap((bundle: Bundle) => bundle.item.pipe(getFirstSucceededRemoteDataPayload(), map((item: Item) => item.uuid)))) + .subscribe((item) => { + this.router.navigate(([getItemEditRoute(item), 'bitstreams'])); + }); } } diff --git a/src/app/core/shared/bitstream.model.ts b/src/app/core/shared/bitstream.model.ts index ab9d1548b7..314818b482 100644 --- a/src/app/core/shared/bitstream.model.ts +++ b/src/app/core/shared/bitstream.model.ts @@ -8,6 +8,8 @@ import { BITSTREAM } from './bitstream.resource-type'; import { DSpaceObject } from './dspace-object.model'; import { HALLink } from './hal-link.model'; import { HALResource } from './hal-resource.model'; +import {BUNDLE} from './bundle.resource-type'; +import {Bundle} from './bundle.model'; @typedObject @inheritSerialization(DSpaceObject) @@ -57,4 +59,10 @@ export class Bitstream extends DSpaceObject implements HALResource { @link(BITSTREAM_FORMAT, false, 'format') format?: Observable>; + /** + * The owning bundle for this Bitstream + * Will be undefined unless the bundle{@link HALLink} has been resolved. + */ + @link(BUNDLE) + bundle?: Observable>; } diff --git a/src/app/core/shared/bundle.model.ts b/src/app/core/shared/bundle.model.ts index 1e5c14d486..c84b1f691f 100644 --- a/src/app/core/shared/bundle.model.ts +++ b/src/app/core/shared/bundle.model.ts @@ -10,6 +10,8 @@ import { RemoteData } from '../data/remote-data'; import { PaginatedList } from '../data/paginated-list'; import { BITSTREAM } from './bitstream.resource-type'; import { Bitstream } from './bitstream.model'; +import {ITEM} from './item.resource-type'; +import {Item} from './item.model'; @typedObject @inheritSerialization(DSpaceObject) @@ -24,6 +26,7 @@ export class Bundle extends DSpaceObject { self: HALLink; primaryBitstream: HALLink; bitstreams: HALLink; + item: HALLink; }; /** @@ -39,4 +42,11 @@ export class Bundle extends DSpaceObject { */ @link(BITSTREAM, true) bitstreams?: Observable>>; + + /** + * The owning item for this Bundle + * Will be undefined unless the Item{@link HALLink} has been resolved. + */ + @link(ITEM) + item?: Observable>; } diff --git a/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts index bcc5153745..6f60341c0f 100644 --- a/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts +++ b/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts @@ -33,9 +33,6 @@ export class SearchResultGridElementComponent, K exten protected bitstreamDataService: BitstreamDataService ) { super(); - if (hasValue(this.object)) { - this.isCollapsed$ = this.isCollapsed(); - } } /** @@ -44,6 +41,7 @@ export class SearchResultGridElementComponent, K exten ngOnInit(): void { if (hasValue(this.object)) { this.dso = this.object.indexableObject; + this.isCollapsed$ = this.isCollapsed(); } }