diff --git a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts index 9a8fb676f4..7a6b58dea2 100644 --- a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts +++ b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts @@ -10,7 +10,6 @@ import { TranslateModule } from '@ngx-translate/core'; import { Observable } from 'rxjs'; import { AuthService } from '../../../../../core/auth/auth.service'; -import { AccessStatusDataService } from '../../../../../core/data/access-status-data.service'; import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service'; import { AuthorizationDataService } from '../../../../../core/data/feature-authorization/authorization-data.service'; import { RemoteData } from '../../../../../core/data/remote-data'; @@ -22,7 +21,6 @@ import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { mockTruncatableService } from '../../../../../shared/mocks/mock-trucatable.service'; import { getMockThemeService } from '../../../../../shared/mocks/theme-service.mock'; import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type'; -import { AccessStatusObject } from '../../../../../shared/object-collection/shared/badges/access-status-badge/access-status.model'; import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model'; import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils'; import { AuthServiceStub } from '../../../../../shared/testing/auth-service.stub'; @@ -44,12 +42,6 @@ describe('ItemAdminSearchResultGridElementComponent', () => { }, }; - const mockAccessStatusDataService = { - findItemAccessStatusFor(item: Item): Observable> { - return createSuccessfulRemoteDataObject$(new AccessStatusObject()); - }, - }; - const mockThemeService = getMockThemeService(); function init() { @@ -74,7 +66,6 @@ describe('ItemAdminSearchResultGridElementComponent', () => { { provide: TruncatableService, useValue: mockTruncatableService }, { provide: BitstreamDataService, useValue: mockBitstreamDataService }, { provide: ThemeService, useValue: mockThemeService }, - { provide: AccessStatusDataService, useValue: mockAccessStatusDataService }, { provide: AuthService, useClass: AuthServiceStub }, { provide: FileService, useClass: FileServiceStub }, { provide: AuthorizationDataService, useClass: AuthorizationDataServiceStub }, diff --git a/src/app/core/shared/bitstream.model.ts b/src/app/core/shared/bitstream.model.ts index 889f9fd658..29a80069db 100644 --- a/src/app/core/shared/bitstream.model.ts +++ b/src/app/core/shared/bitstream.model.ts @@ -82,7 +82,7 @@ export class Bitstream extends DSpaceObject implements ChildHALResource { * The access status for this Bitstream * Will be undefined unless the access status {@link HALLink} has been resolved. */ - @link(ACCESS_STATUS) + @link(ACCESS_STATUS, false, 'accessStatus') accessStatus?: Observable>; getParentLinkKey(): keyof this['_links'] { diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index b13cf25f03..d6066f098c 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -130,7 +130,7 @@ export class Item extends DSpaceObject implements ChildHALResource, HandleObject * The access status for this Item * Will be undefined unless the access status {@link HALLink} has been resolved. */ - @link(ACCESS_STATUS) + @link(ACCESS_STATUS, false, 'accessStatus') accessStatus?: Observable>; /** diff --git a/src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.spec.ts b/src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.spec.ts index bad5b48996..6107796d62 100644 --- a/src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.spec.ts +++ b/src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.spec.ts @@ -6,9 +6,12 @@ import { } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { TranslateModule } from '@ngx-translate/core'; +import { LinkService } from 'src/app/core/cache/builders/link.service'; +import { getMockLinkService } from 'src/app/shared/mocks/link-service.mock'; +import { followLink } from 'src/app/shared/utils/follow-link-config.model'; +import { APP_DATA_SERVICES_MAP } from 'src/config/app-config.interface'; import { environment } from 'src/environments/environment'; -import { AccessStatusDataService } from '../../../../../core/data/access-status-data.service'; import { Bitstream } from '../../../../../core/shared/bitstream.model'; import { Item } from '../../../../../core/shared/item.model'; import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils'; @@ -26,7 +29,7 @@ describe('ItemAccessStatusBadgeComponent', () => { let embargoStatus: AccessStatusObject; let restrictedStatus: AccessStatusObject; - let accessStatusDataService: AccessStatusDataService; + let linkService; let item: Item; let bitstream: Bitstream; @@ -53,9 +56,7 @@ describe('ItemAccessStatusBadgeComponent', () => { status: 'restricted', }); - accessStatusDataService = jasmine.createSpyObj('accessStatusDataService', { - findItemAccessStatusFor: createSuccessfulRemoteDataObject$(unknownStatus), - }); + linkService = getMockLinkService(); item = Object.assign(new Item(), { uuid: 'item-uuid', @@ -73,7 +74,8 @@ describe('ItemAccessStatusBadgeComponent', () => { imports: [TranslateModule.forRoot(), AccessStatusBadgeComponent, TruncatePipe], schemas: [NO_ERRORS_SCHEMA], providers: [ - { provide: AccessStatusDataService, useValue: accessStatusDataService }, + { provide: LinkService, useValue: linkService }, + { provide: APP_DATA_SERVICES_MAP, useValue: {} }, ], }).compileComponents(); } @@ -111,12 +113,13 @@ describe('ItemAccessStatusBadgeComponent', () => { expect(badge.nativeElement.textContent).toEqual(``); } - describe('init with Item', () => { + describe('init with item', () => { beforeEach(waitForAsync(() => { init(); initTestBed(); })); beforeEach(() => { + item.accessStatus = createSuccessfulRemoteDataObject$(unknownStatus); initFixtureAndComponentWithItem(); }); it('should init the component', () => { @@ -124,12 +127,32 @@ describe('ItemAccessStatusBadgeComponent', () => { }); }); - describe('When the findItemAccessStatusFor method returns unknown with Item', () => { + describe('When the item have no accessStatus link', () => { beforeEach(waitForAsync(() => { init(); initTestBed(); })); beforeEach(() => { + item.accessStatus = null; + linkService.resolveLink.and.callFake((model: any) => { + item.accessStatus = createSuccessfulRemoteDataObject$(embargoStatus); + return model; + }); + initFixtureAndComponentWithItem(); + }); + it('should show the embargo badge', () => { + expect(linkService.resolveLink).toHaveBeenCalledWith(item, followLink('accessStatus')); + lookForAccessStatusBadgeForItem('embargo'); + }); + }); + + describe('When the item accessStatus link returns unknown', () => { + beforeEach(waitForAsync(() => { + init(); + initTestBed(); + })); + beforeEach(() => { + item.accessStatus = createSuccessfulRemoteDataObject$(unknownStatus); initFixtureAndComponentWithItem(); }); it('should show the unknown badge', () => { @@ -137,10 +160,10 @@ describe('ItemAccessStatusBadgeComponent', () => { }); }); - describe('When the findItemAccessStatusFor method returns metadata.only with Item', () => { + describe('When the item accessStatus link returns metadata.only', () => { beforeEach(waitForAsync(() => { init(); - (accessStatusDataService.findItemAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(metadataOnlyStatus)); + item.accessStatus = createSuccessfulRemoteDataObject$(metadataOnlyStatus); initTestBed(); })); beforeEach(() => { @@ -151,10 +174,10 @@ describe('ItemAccessStatusBadgeComponent', () => { }); }); - describe('When the findItemAccessStatusFor method returns open.access with Item', () => { + describe('When the item accessStatus link returns open.access', () => { beforeEach(waitForAsync(() => { init(); - (accessStatusDataService.findItemAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(openAccessStatus)); + item.accessStatus = createSuccessfulRemoteDataObject$(openAccessStatus); initTestBed(); })); beforeEach(() => { @@ -165,10 +188,10 @@ describe('ItemAccessStatusBadgeComponent', () => { }); }); - describe('When the findItemAccessStatusFor method returns embargo with Item', () => { + describe('When the item accessStatus link returns embargo', () => { beforeEach(waitForAsync(() => { init(); - (accessStatusDataService.findItemAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(embargoStatus)); + item.accessStatus = createSuccessfulRemoteDataObject$(embargoStatus); initTestBed(); })); beforeEach(() => { @@ -179,10 +202,10 @@ describe('ItemAccessStatusBadgeComponent', () => { }); }); - describe('When the findItemAccessStatusFor method returns restricted with Item', () => { + describe('When the item accessStatus link returns restricted', () => { beforeEach(waitForAsync(() => { init(); - (accessStatusDataService.findItemAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(restrictedStatus)); + item.accessStatus = createSuccessfulRemoteDataObject$(restrictedStatus); initTestBed(); })); beforeEach(() => { @@ -193,12 +216,13 @@ describe('ItemAccessStatusBadgeComponent', () => { }); }); - describe('init with Bitstream', () => { + describe('init with bitstream', () => { beforeEach(waitForAsync(() => { init(); initTestBed(); })); beforeEach(() => { + bitstream.accessStatus = createSuccessfulRemoteDataObject$(unknownStatus); initFixtureAndComponentWithBitstream(); }); it('should init the component', () => { @@ -206,20 +230,26 @@ describe('ItemAccessStatusBadgeComponent', () => { }); }); - describe('When the bitstream have no access status', () => { + describe('When the bitstream have no accessStatus link', () => { beforeEach(waitForAsync(() => { init(); initTestBed(); })); beforeEach(() => { + bitstream.accessStatus = null; + linkService.resolveLink.and.callFake((model: any) => { + bitstream.accessStatus = createSuccessfulRemoteDataObject$(embargoStatus); + return model; + }); initFixtureAndComponentWithBitstream(); }); - it('should not show the badge', () => { - lookForNoAccessStatusBadgeForBitstream(); + it('should show the badge', () => { + expect(linkService.resolveLink).toHaveBeenCalledWith(bitstream, followLink('accessStatus')); + lookForAccessStatusBadgeForBitstream(); }); }); - describe('When the bitstream have an access status with no embargo date', () => { + describe('When the bitstream have an accessStatus link with no embargo date', () => { beforeEach(waitForAsync(() => { init(); initTestBed(); @@ -233,7 +263,7 @@ describe('ItemAccessStatusBadgeComponent', () => { }); }); - describe('When the bitstream have an access status with an embargo date', () => { + describe('When the bitstream have an accessStatus link with an embargo date', () => { beforeEach(waitForAsync(() => { init(); initTestBed(); diff --git a/src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.ts b/src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.ts index c5e6709ffd..fc3ba0864a 100644 --- a/src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.ts +++ b/src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.ts @@ -15,12 +15,12 @@ import { catchError, map, } from 'rxjs/operators'; -import { AccessStatusDataService } from 'src/app/core/data/access-status-data.service'; +import { LinkService } from 'src/app/core/cache/builders/link.service'; import { Bitstream } from 'src/app/core/shared/bitstream.model'; import { getFirstSucceededRemoteDataPayload } from 'src/app/core/shared/operators'; +import { followLink } from 'src/app/shared/utils/follow-link-config.model'; import { environment } from 'src/environments/environment'; -import { DSpaceObject } from '../../../../../core/shared/dspace-object.model'; import { Item } from '../../../../../core/shared/item.model'; import { hasValue } from '../../../../empty.util'; import { AccessStatusObject } from './access-status.model'; @@ -37,7 +37,7 @@ import { AccessStatusObject } from './access-status.model'; */ export class AccessStatusBadgeComponent implements OnDestroy, OnInit { - @Input() object: DSpaceObject; + @Input() object: Item | Bitstream; accessStatus$: Observable; embargoDate$: Observable; @@ -60,14 +60,20 @@ export class AccessStatusBadgeComponent implements OnDestroy, OnInit { /** * Initialize instance variables * - * @param {AccessStatusDataService} accessStatusDataService + * @param {LinkService} linkService */ - constructor(private accessStatusDataService: AccessStatusDataService) { } + constructor( + private linkService: LinkService, + ) { } ngOnInit(): void { if (!hasValue(this.object)) { return; } + if (!hasValue(this.object.accessStatus)) { + // In case the access status has not been loaded, do it individually. + this.linkService.resolveLink(this.object, followLink('accessStatus')); + } switch ((this.object as any).type) { case Item.type.value: this.handleItem(); @@ -91,19 +97,8 @@ export class AccessStatusBadgeComponent implements OnDestroy, OnInit { // Do not show the badge if the feature is inactive. return; } - const item = this.object as Item; - if (item.accessStatus == null) { - // In case the access status has not been loaded, do it individually. - item.accessStatus = this.accessStatusDataService.findItemAccessStatusFor(item); - } - this.accessStatus$ = item.accessStatus.pipe( - map((accessStatusRD) => { - if (accessStatusRD.statusCode !== 401 && hasValue(accessStatusRD.payload)) { - return accessStatusRD.payload; - } else { - return []; - } - }), + this.accessStatus$ = this.object.accessStatus.pipe( + getFirstSucceededRemoteDataPayload(), map((accessStatus: AccessStatusObject) => hasValue(accessStatus.status) ? accessStatus.status : 'unknown'), map((status: string) => `access-status.${status.toLowerCase()}.listelement.badge`), catchError(() => observableOf('access-status.unknown.listelement.badge')), @@ -127,19 +122,9 @@ export class AccessStatusBadgeComponent implements OnDestroy, OnInit { // Do not show the badge if the feature is inactive. return; } - const bitstream = this.object as Bitstream; - if (bitstream.accessStatus == null) { - return; - } - this.embargoDate$ = bitstream.accessStatus.pipe( + this.embargoDate$ = this.object.accessStatus.pipe( getFirstSucceededRemoteDataPayload(), - map((accessStatus: AccessStatusObject) => { - if (hasValue(accessStatus.embargoDate)) { - return accessStatus.embargoDate; - } else { - return null; - } - }), + map((accessStatus: AccessStatusObject) => hasValue(accessStatus.embargoDate) ? accessStatus.embargoDate : null), catchError(() => observableOf(null)), ); this.subs.push(