Use the item's HALLink to get the access status

This commit is contained in:
nibou230
2022-05-05 10:21:55 -04:00
parent 8e03b28151
commit e77821eef0
9 changed files with 88 additions and 52 deletions

View File

@@ -164,6 +164,7 @@ import { CoreState } from './core-state.model';
import { GroupDataService } from './eperson/group-data.service'; import { GroupDataService } from './eperson/group-data.service';
import { SubmissionAccessesModel } from './config/models/config-submission-accesses.model'; import { SubmissionAccessesModel } from './config/models/config-submission-accesses.model';
import { AccessStatusObject } from '../shared/object-list/access-status-badge/access-status.model'; import { AccessStatusObject } from '../shared/object-list/access-status-badge/access-status.model';
import { AccessStatusDataService } from './data/access-status-data.service';
/** /**
* When not in production, endpoint responses can be mocked for testing purposes * When not in production, endpoint responses can be mocked for testing purposes
@@ -221,6 +222,7 @@ const PROVIDERS = [
MyDSpaceResponseParsingService, MyDSpaceResponseParsingService,
ServerResponseService, ServerResponseService,
BrowseService, BrowseService,
AccessStatusDataService,
SubmissionCcLicenseDataService, SubmissionCcLicenseDataService,
SubmissionCcLicenseUrlDataService, SubmissionCcLicenseUrlDataService,
SubmissionFormsConfigService, SubmissionFormsConfigService,

View File

@@ -0,0 +1,45 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { DataService } from './data.service';
import { RequestService } from './request.service';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { CoreState } from '../core-state.model';
import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model';
import { ACCESS_STATUS } from 'src/app/shared/object-list/access-status-badge/access-status.resource-type';
import { Observable } from 'rxjs';
import { RemoteData } from './remote-data';
import { Item } from '../shared/item.model';
@Injectable()
@dataService(ACCESS_STATUS)
export class AccessStatusDataService extends DataService<AccessStatusObject> {
protected linkPath = 'accessStatus';
constructor(
protected comparator: DefaultChangeAnalyzer<AccessStatusObject>,
protected halService: HALEndpointService,
protected http: HttpClient,
protected notificationsService: NotificationsService,
protected objectCache: ObjectCacheService,
protected rdbService: RemoteDataBuildService,
protected requestService: RequestService,
protected store: Store<CoreState>,
) {
super();
}
/**
* 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>> {
return this.findByHref(item._links.accessStatus.href);
}
}

View File

@@ -36,7 +36,6 @@ import { sendRequest } from '../shared/request.operators';
import { RestRequest } from './rest-request.model'; import { RestRequest } from './rest-request.model';
import { CoreState } from '../core-state.model'; import { CoreState } from '../core-state.model';
import { FindListOptions } from './find-list-options.model'; import { FindListOptions } from './find-list-options.model';
import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model';
@Injectable() @Injectable()
@dataService(ITEM) @dataService(ITEM)
@@ -292,33 +291,6 @@ export class ItemDataService extends DataService<Item> {
); );
} }
/**
* Get the endpoint for an item's access status
* @param itemId
*/
public getAccessStatusEndpoint(itemId: string): Observable<string> {
return this.halService.getEndpoint(this.linkPath).pipe(
switchMap((url: string) => this.halService.getEndpoint('accessStatus', `${url}/${itemId}`))
);
}
/**
* Get the the access status
* @param itemId
*/
public getAccessStatus(itemId: string): Observable<RemoteData<AccessStatusObject>> {
const hrefObs = this.getAccessStatusEndpoint(itemId);
hrefObs.pipe(
take(1)
).subscribe((href) => {
const request = new GetRequest(this.requestService.generateRequestId(), href);
this.requestService.send(request);
});
return this.rdbService.buildSingle<AccessStatusObject>(hrefObs);
}
/** /**
* Invalidate the cache of the item * Invalidate the cache of the item
* @param itemUUID * @param itemUUID

View File

@@ -21,6 +21,8 @@ import { Version } from './version.model';
import { VERSION } from './version.resource-type'; import { VERSION } from './version.resource-type';
import { BITSTREAM } from './bitstream.resource-type'; import { BITSTREAM } from './bitstream.resource-type';
import { Bitstream } from './bitstream.model'; import { Bitstream } from './bitstream.model';
import { ACCESS_STATUS } from 'src/app/shared/object-list/access-status-badge/access-status.resource-type';
import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model';
/** /**
* Class representing a DSpace Item * Class representing a DSpace Item
@@ -72,6 +74,7 @@ export class Item extends DSpaceObject implements ChildHALResource {
templateItemOf: HALLink; templateItemOf: HALLink;
version: HALLink; version: HALLink;
thumbnail: HALLink; thumbnail: HALLink;
accessStatus: HALLink;
self: HALLink; self: HALLink;
}; };
@@ -110,6 +113,13 @@ export class Item extends DSpaceObject implements ChildHALResource {
@link(BITSTREAM, false, 'thumbnail') @link(BITSTREAM, false, 'thumbnail')
thumbnail?: Observable<RemoteData<Bitstream>>; thumbnail?: Observable<RemoteData<Bitstream>>;
/**
* The access status for this Item
* Will be undefined unless the access status {@link HALLink} has been resolved.
*/
@link(ACCESS_STATUS)
accessStatus?: Observable<RemoteData<AccessStatusObject>>;
/** /**
* Method that returns as which type of object this object should be rendered * Method that returns as which type of object this object should be rendered
*/ */

View File

@@ -18,7 +18,7 @@
</span> </span>
<div class="card-body"> <div class="card-body">
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge> <ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
<ds-access-status-badge [uuid]="dso.uuid"></ds-access-status-badge> <ds-access-status-badge [item]="dso"></ds-access-status-badge>
<ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4"> <ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4">
<h4 class="card-title" [innerHTML]="firstMetadataValue('dc.title')"></h4> <h4 class="card-title" [innerHTML]="firstMetadataValue('dc.title')"></h4>
</ds-truncatable-part> </ds-truncatable-part>

View File

@@ -1,11 +1,11 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { catchError, map } from 'rxjs/operators'; import { catchError, map } from 'rxjs/operators';
import { Observable, of as observableOf } from 'rxjs'; import { Observable, of as observableOf } from 'rxjs';
import { getFirstCompletedRemoteData } from 'src/app/core/shared/operators';
import { ItemDataService } from 'src/app/core/data/item-data.service';
import { AccessStatusObject } from './access-status.model'; import { AccessStatusObject } from './access-status.model';
import { hasValue } from '../../empty.util'; import { hasValue } from '../../empty.util';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
import { Item } from 'src/app/core/shared/item.model';
import { AccessStatusDataService } from 'src/app/core/data/access-status-data.service';
@Component({ @Component({
selector: 'ds-access-status-badge', selector: 'ds-access-status-badge',
@@ -16,7 +16,7 @@ import { environment } from 'src/environments/environment';
*/ */
export class AccessStatusBadgeComponent { export class AccessStatusBadgeComponent {
@Input() uuid: string; @Input() item: Item;
accessStatus$: Observable<string>; accessStatus$: Observable<string>;
/** /**
@@ -27,16 +27,21 @@ export class AccessStatusBadgeComponent {
/** /**
* Initialize instance variables * Initialize instance variables
* *
* @param {ItemDataService} itemDataService * @param {AccessStatusDataService} accessStatusDataService
*/ */
constructor(private itemDataService: ItemDataService) { } constructor(private accessStatusDataService: AccessStatusDataService) { }
ngOnInit(): void { ngOnInit(): void {
this.showAccessStatus = environment.item.showAccessStatuses; this.showAccessStatus = environment.item.showAccessStatuses;
this.accessStatus$ = this.itemDataService if (!this.showAccessStatus || this.item == null) {
.getAccessStatus(this.uuid) // Do not show the badge if the feature is inactive or if the item is null.
.pipe( return;
getFirstCompletedRemoteData(), }
if (this.item.accessStatus == null) {
// In case the access status has not been loaded, do it individually.
this.item.accessStatus = this.accessStatusDataService.findAccessStatusFor(this.item);
}
this.accessStatus$ = this.item.accessStatus.pipe(
map((accessStatusRD) => { map((accessStatusRD) => {
if (accessStatusRD.statusCode !== 401 && hasValue(accessStatusRD.payload)) { if (accessStatusRD.statusCode !== 401 && hasValue(accessStatusRD.payload)) {
return accessStatusRD.payload; return accessStatusRD.payload;

View File

@@ -4,7 +4,7 @@
</ng-container> </ng-container>
<div class="d-flex"> <div class="d-flex">
<ds-type-badge [object]="item"></ds-type-badge> <ds-type-badge [object]="item"></ds-type-badge>
<ds-access-status-badge [uuid]="item.uuid" class="pl-1"></ds-access-status-badge> <ds-access-status-badge [item]="item" class="pl-1"></ds-access-status-badge>
</div> </div>
<ds-truncatable [id]="item.id"> <ds-truncatable [id]="item.id">
<h3 [innerHTML]="item.firstMetadataValue('dc.title') || ('mydspace.results.no-title' | translate)" [ngClass]="{'lead': true,'text-muted': !item.firstMetadataValue('dc.title')}"></h3> <h3 [innerHTML]="item.firstMetadataValue('dc.title') || ('mydspace.results.no-title' | translate)" [ngClass]="{'lead': true,'text-muted': !item.firstMetadataValue('dc.title')}"></h3>

View File

@@ -1,6 +1,6 @@
<div class="d-flex"> <div class="d-flex">
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge> <ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
<ds-access-status-badge [uuid]="dso.uuid" class="pl-1"></ds-access-status-badge> <ds-access-status-badge [item]="dso" class="pl-1"></ds-access-status-badge>
</div> </div>
<ds-truncatable [id]="dso.id" *ngIf="object !== undefined && object !== null"> <ds-truncatable [id]="dso.id" *ngIf="object !== undefined && object !== null">

View File

@@ -31,6 +31,7 @@ import { ViewMode } from '../../core/shared/view-mode.model';
import { SelectionConfig } from './search-results/search-results.component'; import { SelectionConfig } from './search-results/search-results.component';
import { ListableObject } from '../object-collection/shared/listable-object.model'; import { ListableObject } from '../object-collection/shared/listable-object.model';
import { CollectionElementLinkType } from '../object-collection/collection-element-link.type'; import { CollectionElementLinkType } from '../object-collection/collection-element-link.type';
import { environment } from 'src/environments/environment';
@Component({ @Component({
selector: 'ds-search', selector: 'ds-search',
@@ -355,7 +356,8 @@ export class SearchComponent implements OnInit {
undefined, undefined,
this.useCachedVersionIfAvailable, this.useCachedVersionIfAvailable,
true, true,
followLink<Item>('thumbnail', { isOptional: true }) followLink<Item>('thumbnail', { isOptional: true }),
followLink<Item>('accessStatus', { isOptional: true, shouldEmbed: environment.item.showAccessStatuses })
).pipe(getFirstCompletedRemoteData()) ).pipe(getFirstCompletedRemoteData())
.subscribe((results: RemoteData<SearchObjects<DSpaceObject>>) => { .subscribe((results: RemoteData<SearchObjects<DSpaceObject>>) => {
if (results.hasSucceeded && results.payload?.page?.length > 0) { if (results.hasSucceeded && results.payload?.page?.length > 0) {