Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display the access status (embargo) for the bitstream #3882

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions config/config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,8 @@ item:
# Rounded to the nearest size in the list of selectable sizes on the
# settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'.
pageSize: 5
# Show the bitstream access status label
showAccessStatuses: false

# Community Page Config
community:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('ItemAdminSearchResultGridElementComponent', () => {
};

const mockAccessStatusDataService = {
findAccessStatusFor(item: Item): Observable<RemoteData<AccessStatusObject>> {
findItemAccessStatusFor(item: Item): Observable<RemoteData<AccessStatusObject>> {
return createSuccessfulRemoteDataObject$(new AccessStatusObject());
},
};
Expand Down
33 changes: 31 additions & 2 deletions src/app/core/data/access-status-data.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-servic
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { Bitstream } from '../shared/bitstream.model';
import { Item } from '../shared/item.model';
import { AccessStatusDataService } from './access-status-data.service';
import { RemoteData } from './remote-data';
Expand Down Expand Up @@ -41,16 +42,44 @@ describe('AccessStatusDataService', () => {
},
});

const bitstreamId = '3d4c730u-5a4b-438b-9686-be1d5b4a1c5a';
const mockBitstream: Bitstream = Object.assign(new Bitstream(), {
id: bitstreamId,
name: 'test-bitstream',
_links: {
accessStatus: {
href: `https://rest.api/core/bitstreams/${bitstreamId}/accessStatus`,
},
self: {
href: `https://rest.api/core/bitstreams/${bitstreamId}`,
},
},
});

describe('when the requests are successful', () => {
beforeEach(() => {
createService();
});

describe('when calling findAccessStatusFor', () => {
describe('when calling findItemAccessStatusFor', () => {
let contentSource$;

beforeEach(() => {
contentSource$ = service.findItemAccessStatusFor(mockItem);
});

it('should send a new GetRequest', fakeAsync(() => {
contentSource$.subscribe();
tick();
expect(requestService.send).toHaveBeenCalledWith(jasmine.any(GetRequest), true);
}));
});

describe('when calling findBitstreamAccessStatusFor', () => {
let contentSource$;

beforeEach(() => {
contentSource$ = service.findAccessStatusFor(mockItem);
contentSource$ = service.findBitstreamAccessStatusFor(mockBitstream);
});

it('should send a new GetRequest', fakeAsync(() => {
Expand Down
11 changes: 10 additions & 1 deletion src/app/core/data/access-status-data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AccessStatusObject } from 'src/app/shared/object-collection/shared/badg

import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { Bitstream } from '../shared/bitstream.model';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { Item } from '../shared/item.model';
import { BaseDataService } from './base/base-data.service';
Expand All @@ -29,7 +30,15 @@ export class AccessStatusDataService extends BaseDataService<AccessStatusObject>
* Returns {@link RemoteData} of {@link AccessStatusObject} that is the access status of the given item
* @param item Item we want the access status of
*/
findAccessStatusFor(item: Item): Observable<RemoteData<AccessStatusObject>> {
findItemAccessStatusFor(item: Item): Observable<RemoteData<AccessStatusObject>> {
return this.findByHref(item._links.accessStatus.href);
}

/**
* Returns {@link RemoteData} of {@link AccessStatusObject} that is the access status of the given bitstream
* @param bitstream Bitstream we want the access status of
*/
findBitstreamAccessStatusFor(bitstream: Bitstream): Observable<RemoteData<AccessStatusObject>> {
return this.findByHref(bitstream._links.accessStatus.href);
}
}
1 change: 0 additions & 1 deletion src/app/core/provide-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ export const models =
ResearcherProfile,
OrcidQueue,
OrcidHistory,
AccessStatusObject,
IdentifierData,
Subscription,
ItemRequest,
Expand Down
10 changes: 10 additions & 0 deletions src/app/core/shared/bitstream.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
inheritSerialization,
} from 'cerialize';
import { Observable } from 'rxjs';
import { AccessStatusObject } from 'src/app/shared/object-collection/shared/badges/access-status-badge/access-status.model';
import { ACCESS_STATUS } from 'src/app/shared/object-collection/shared/badges/access-status-badge/access-status.resource-type';

import {
link,
Expand Down Expand Up @@ -52,6 +54,7 @@ export class Bitstream extends DSpaceObject implements ChildHALResource {
format: HALLink;
content: HALLink;
thumbnail: HALLink;
accessStatus: HALLink;
};

/**
Expand All @@ -75,6 +78,13 @@ export class Bitstream extends DSpaceObject implements ChildHALResource {
@link(BUNDLE)
bundle?: Observable<RemoteData<Bundle>>;

/**
* The access status for this Bitstream
* Will be undefined unless the access status {@link HALLink} has been resolved.
*/
@link(ACCESS_STATUS)
accessStatus?: Observable<RemoteData<AccessStatusObject>>;

getParentLinkKey(): keyof this['_links'] {
return 'format';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<span role="img" *ngIf="(canDownload$ |async) !== true" [attr.aria-label]="'file-download-link.restricted' | translate" class="pr-1"><i class="fas fa-lock"></i></span>
<ng-container *ngTemplateOutlet="content"></ng-container>
</a>
<ds-embargo-badge [bitstream]="bitstream"></ds-embargo-badge>

<ng-template #content>
<ng-content></ng-content>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ import {
ActivatedRoute,
RouterLink,
} from '@angular/router';
import { Store } from '@ngrx/store';
import { TranslateModule } from '@ngx-translate/core';
import {
cold,
getTestScheduler,
} from 'jasmine-marbles';
import { of as observableOf } from 'rxjs';
import { APP_DATA_SERVICES_MAP } from 'src/config/app-config.interface';

import { getBitstreamModuleRoute } from '../../app-routing-paths';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
Expand All @@ -34,6 +37,7 @@ describe('FileDownloadLinkComponent', () => {

let bitstream: Bitstream;
let item: Item;
let storeMock: any;

function init() {
authorizationService = jasmine.createSpyObj('authorizationService', {
Expand All @@ -51,6 +55,11 @@ describe('FileDownloadLinkComponent', () => {
self: { href: 'obj-selflink' },
},
});
storeMock = jasmine.createSpyObj('store', {
dispatch: jasmine.createSpy('dispatch'),
select: jasmine.createSpy('select'),
pipe: observableOf(true),
});
}

function initTestbed() {
Expand All @@ -63,6 +72,8 @@ describe('FileDownloadLinkComponent', () => {
RouterLinkDirectiveStub,
{ provide: AuthorizationDataService, useValue: authorizationService },
{ provide: ActivatedRoute, useValue: new ActivatedRouteStub() },
{ provide: Store, useValue: storeMock },
{ provide: APP_DATA_SERVICES_MAP, useValue: {} },
],
})
.overrideComponent(FileDownloadLinkComponent, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@ import {
hasValue,
isNotEmpty,
} from '../empty.util';
import { ThemedEmbargoBadgeComponent } from '../object-collection/shared/badges/embargo-badge/themed-embargo-badge.component';

@Component({
selector: 'ds-base-file-download-link',
templateUrl: './file-download-link.component.html',
styleUrls: ['./file-download-link.component.scss'],
standalone: true,
imports: [RouterLink, NgClass, NgIf, NgTemplateOutlet, AsyncPipe, TranslateModule],
imports: [RouterLink, NgClass, NgIf, NgTemplateOutlet, AsyncPipe, TranslateModule, ThemedEmbargoBadgeComponent],
})
/**
* Component displaying a download link
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('ItemAccessStatusBadgeComponent', () => {
});

accessStatusDataService = jasmine.createSpyObj('accessStatusDataService', {
findAccessStatusFor: createSuccessfulRemoteDataObject$(unknownStatus),
findItemAccessStatusFor: createSuccessfulRemoteDataObject$(unknownStatus),
});

item = Object.assign(new Item(), {
Expand Down Expand Up @@ -97,7 +97,7 @@ describe('ItemAccessStatusBadgeComponent', () => {
});
});

describe('When the findAccessStatusFor method returns unknown', () => {
describe('When the findItemAccessStatusFor method returns unknown', () => {
beforeEach(waitForAsync(() => {
init();
initTestBed();
Expand All @@ -110,10 +110,10 @@ describe('ItemAccessStatusBadgeComponent', () => {
});
});

describe('When the findAccessStatusFor method returns metadata.only', () => {
describe('When the findItemAccessStatusFor method returns metadata.only', () => {
beforeEach(waitForAsync(() => {
init();
(accessStatusDataService.findAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(metadataOnlyStatus));
(accessStatusDataService.findItemAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(metadataOnlyStatus));
initTestBed();
}));
beforeEach(() => {
Expand All @@ -124,10 +124,10 @@ describe('ItemAccessStatusBadgeComponent', () => {
});
});

describe('When the findAccessStatusFor method returns open.access', () => {
describe('When the findItemAccessStatusFor method returns open.access', () => {
beforeEach(waitForAsync(() => {
init();
(accessStatusDataService.findAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(openAccessStatus));
(accessStatusDataService.findItemAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(openAccessStatus));
initTestBed();
}));
beforeEach(() => {
Expand All @@ -138,10 +138,10 @@ describe('ItemAccessStatusBadgeComponent', () => {
});
});

describe('When the findAccessStatusFor method returns embargo', () => {
describe('When the findItemAccessStatusFor method returns embargo', () => {
beforeEach(waitForAsync(() => {
init();
(accessStatusDataService.findAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(embargoStatus));
(accessStatusDataService.findItemAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(embargoStatus));
initTestBed();
}));
beforeEach(() => {
Expand All @@ -152,10 +152,10 @@ describe('ItemAccessStatusBadgeComponent', () => {
});
});

describe('When the findAccessStatusFor method returns restricted', () => {
describe('When the findItemAccessStatusFor method returns restricted', () => {
beforeEach(waitForAsync(() => {
init();
(accessStatusDataService.findAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(restrictedStatus));
(accessStatusDataService.findItemAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(restrictedStatus));
initTestBed();
}));
beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export class AccessStatusBadgeComponent implements OnDestroy, OnInit {
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.findAccessStatusFor(item);
item.accessStatus = this.accessStatusDataService.findItemAccessStatusFor(item);
}
this.accessStatus$ = item.accessStatus.pipe(
map((accessStatusRD) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ export class AccessStatusObject implements CacheableObject {
@autoserialize
status: string;

/**
* The embargo date value
*/
@autoserialize
embargoDate: string;

/**
* The {@link HALLink}s for this AccessStatusObject
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<ng-container *ngIf="showAccessStatus">
<div *ngIf="embargoDate$ | async as embargoDate">
<span [class]="'badge badge-secondary embargo-list-element-badge'">{{ 'embargo.listelement.badge' | translate: {date: embargoDate} }}</span>
</div>
</ng-container>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading
Loading