[CST-11178] show allowed correction types

This commit is contained in:
Mykhaylo
2023-10-30 12:19:44 +01:00
parent ec82139256
commit 752cb4d4b0
7 changed files with 366 additions and 0 deletions

View File

@@ -132,3 +132,13 @@ export const SUBSCRIPTIONS_MODULE_PATH = 'subscriptions';
export function getSubscriptionsModuleRoute() { export function getSubscriptionsModuleRoute() {
return `/${SUBSCRIPTIONS_MODULE_PATH}`; return `/${SUBSCRIPTIONS_MODULE_PATH}`;
} }
export const EDIT_ITEM_PATH = 'edit-items';
export function getEditItemPageRoute() {
return `/${EDIT_ITEM_PATH}`;
}
export const CORRECTION_TYPE_PATH = 'corrections';
export function getCorrectionTypePageRoute(itemUuid: string, typeId: string) {
return `/items/${itemUuid}/${CORRECTION_TYPE_PATH}/${typeId}`;
}

View File

@@ -185,6 +185,8 @@ import { FlatBrowseDefinition } from './shared/flat-browse-definition.model';
import { ValueListBrowseDefinition } from './shared/value-list-browse-definition.model'; import { ValueListBrowseDefinition } from './shared/value-list-browse-definition.model';
import { NonHierarchicalBrowseDefinition } from './shared/non-hierarchical-browse-definition'; import { NonHierarchicalBrowseDefinition } from './shared/non-hierarchical-browse-definition';
import { BulkAccessConditionOptions } from './config/models/bulk-access-condition-options.model'; import { BulkAccessConditionOptions } from './config/models/bulk-access-condition-options.model';
import { CorrectionTypeMode } from './submission/models/correction-type-mode.model';
import { CorrectionTypeDataService } from './submission/correctiontype-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
@@ -308,6 +310,7 @@ const PROVIDERS = [
OrcidQueueDataService, OrcidQueueDataService,
OrcidHistoryDataService, OrcidHistoryDataService,
SupervisionOrderDataService SupervisionOrderDataService
CorrectionTypeDataService,
]; ];
/** /**
@@ -387,6 +390,7 @@ export const models =
Subscription, Subscription,
ItemRequest, ItemRequest,
BulkAccessConditionOptions BulkAccessConditionOptions
CorrectionTypeMode
]; ];
@NgModule({ @NgModule({

View File

@@ -0,0 +1,89 @@
import { Injectable } from '@angular/core';
import { dataService } from '../data/base/data-service.decorator';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { RequestService } from '../data/request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
import { SearchDataImpl } from '../data/base/search-data';
import { CorrectionTypeMode } from './models/correction-type-mode.model';
import { Observable, map } from 'rxjs';
import { RemoteData } from '../data/remote-data';
import { PaginatedList } from '../data/paginated-list.model';
import { FindListOptions } from '../data/find-list-options.model';
import { RequestParam } from '../cache/models/request-param.model';
import { getAllSucceededRemoteDataPayload, getPaginatedListPayload } from '../shared/operators';
/**
* A service that provides methods to make REST requests with correctiontypes endpoint.
*/
@Injectable()
@dataService(CorrectionTypeMode.type)
export class CorrectionTypeDataService extends IdentifiableDataService<CorrectionTypeMode> {
protected linkPath = 'correctiontypes';
protected searchByTopic = 'findByTopic';
protected searchFindByItem = 'findByItem';
private searchData: SearchDataImpl<CorrectionTypeMode>;
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
) {
super('correctiontypes', requestService, rdbService, objectCache, halService);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
}
/**
* Get the correction type by id
* @param id the id of the correction type
* @param useCachedVersionIfAvailable use the cached version if available
* @param reRequestOnStale re-request on stale
* @returns {Observable<RemoteData<CorrectionTypeMode>>} the correction type
*/
getCorrectionTypeById(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true): Observable<RemoteData<CorrectionTypeMode>> {
return this.findById(id, useCachedVersionIfAvailable, reRequestOnStale);
}
/**
* Search for the correction types for the item
* @param itemUuid the uuid of the item
* @param useCachedVersionIfAvailable use the cached version if available
* @returns the list of correction types for the item
*/
findByItem(itemUuid: string, useCachedVersionIfAvailable): Observable<RemoteData<PaginatedList<CorrectionTypeMode>>> {
const options = new FindListOptions();
options.searchParams = [new RequestParam('uuid', itemUuid)];
return this.searchData.searchBy(this.searchFindByItem, options, useCachedVersionIfAvailable);
}
/**
* Find the correction type for the topic
* @param topic the topic of the correction type to search for
* @param useCachedVersionIfAvailable use the cached version if available
* @param reRequestOnStale re-request on stale
* @returns the correction type for the topic
*/
findByTopic(topic: string, useCachedVersionIfAvailable = true, reRequestOnStale = true): Observable<CorrectionTypeMode> {
const options = new FindListOptions();
options.searchParams = [
{
fieldName: 'topic',
fieldValue: topic,
},
];
return this.searchData.searchBy(this.searchByTopic, options, useCachedVersionIfAvailable, reRequestOnStale).pipe(
getAllSucceededRemoteDataPayload(),
getPaginatedListPayload(),
map((list: CorrectionTypeMode[]) => {
return list[0];
})
);
}
}

View File

@@ -0,0 +1,35 @@
import { autoserialize, deserialize } from 'cerialize';
import { typedObject } from '../../cache/builders/build-decorators';
import { CacheableObject } from '../../cache/cacheable-object.model';
import { ResourceType } from '../../shared/resource-type';
import { excludeFromEquals } from '../../utilities/equals.decorators';
import { HALLink } from '../../shared/hal-link.model';
@typedObject
export class CorrectionTypeMode extends CacheableObject {
static type = new ResourceType('correctiontype');
/**
* The object type
*/
@excludeFromEquals
@autoserialize
type: ResourceType;
@autoserialize
id: string;
@autoserialize
topic: string;
@autoserialize
discoveryConfiguration: string;
@autoserialize
creationForm: string;
@deserialize
_links: {
self: HALLink;
};
}

View File

@@ -0,0 +1,7 @@
<ng-container *ngIf="(isAvailable() | async)">
<button ngbDropdownItem
*ngFor="let type of (getCorrectionTypes() | async)"
[routerLink]="[getTypeRoute(type.id)]"
[innerHTML]="('context-menu.actions.correction-type.btn.' + type.id | translate)">
</button>
</ng-container>

View File

@@ -0,0 +1,116 @@
import { Item } from './../../../core/shared/item.model';
import { DSpaceObject } from './../../../core/shared/dspace-object.model';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CorrectionTypeMenuComponent } from './correction-type-menu.component';
import { NotificationsServiceStub } from '../../testing/notifications-service.stub';
import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
import { createPaginatedList } from '../../testing/utils.test';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateLoaderMock } from '../../mocks/translate-loader.mock';
import { RouterTestingModule } from '@angular/router/testing';
import { CorrectionTypeDataService } from '../../../core/submission/correctiontype-data.service';
import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model';
import { NotificationsService } from '../../notifications/notifications.service';
import { By } from '@angular/platform-browser';
import { CorrectionTypeMode } from '../../../core/submission/models/correction-type-mode.model';
describe('CorrectionTypeMenuComponent', () => {
let component: CorrectionTypeMenuComponent;
let fixture: ComponentFixture<CorrectionTypeMenuComponent>;
let componentAsAny: any;
let correctionTypeService: any;
let dso: DSpaceObject;
const notificationService = new NotificationsServiceStub();
const correctionType: CorrectionTypeMode = Object.assign(new CorrectionTypeMode(), {
id: 'addpersonalpath',
creationForm:'manageRelation',
discoveryConfiguration: 'RELATION.PersonPath.Items',
topic: '/DSPACEUSERS/RELATIONADD/PERSONALPATH'
});
const correctionTypeObjRDList$ = createSuccessfulRemoteDataObject$(createPaginatedList([correctionType]));
beforeEach(async () => {
dso = Object.assign(new Item(), {
id: 'test-item',
_links: {
self: { href: 'test-item-selflink' }
}
});
correctionTypeService = jasmine.createSpyObj('CorrectionTypeDataService', {
findByItem: jasmine.createSpy('findByItem')
});
await TestBed.configureTestingModule({
declarations: [ CorrectionTypeMenuComponent ],
imports: [
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
}),
RouterTestingModule.withRoutes([])],
providers: [
{ provide: CorrectionTypeDataService, useValue: correctionTypeService },
{ provide: 'contextMenuObjectProvider', useValue: dso },
{ provide: 'contextMenuObjectTypeProvider', useValue: DSpaceObjectType.ITEM },
{ provide: NotificationsService, useValue: notificationService }
]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(CorrectionTypeMenuComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('when correction types are available', () => {
beforeEach(() => {
correctionTypeService.findByItem.and.returnValue(correctionTypeObjRDList$);
fixture = TestBed.createComponent(CorrectionTypeMenuComponent);
component = fixture.componentInstance;
componentAsAny = fixture.componentInstance;
component.contextMenuObject = dso;
fixture.detectChanges();
});
it('should init properly', () => {
expect(componentAsAny.correctionTypes$.value).toEqual([correctionType]);
});
it('should render a button', () => {
const link = fixture.debugElement.query(By.css('button'));
expect(link).not.toBeNull();
});
});
describe('when is no data are available', () => {
beforeEach(() => {
correctionTypeService.findByItem.and.returnValue(createSuccessfulRemoteDataObject$(createPaginatedList([])));
fixture = TestBed.createComponent(CorrectionTypeMenuComponent);
component = fixture.componentInstance;
componentAsAny = fixture.componentInstance;
component.contextMenuObject = dso;
fixture.detectChanges();
});
it('should init edit mode properly', () => {
expect(componentAsAny.correctionTypes$.value).toEqual([]);
});
it('should render a button', () => {
const link = fixture.debugElement.query(By.css('button'));
expect(link).toBeNull();
});
});
});

View File

@@ -0,0 +1,105 @@
import { getAllSucceededRemoteDataPayload, getPaginatedListPayload } from './../../../core/shared/operators';
import { CorrectionTypeDataService } from './../../../core/submission/correctiontype-data.service';
import { DSpaceObject } from './../../../core/shared/dspace-object.model';
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { ContextMenuEntryComponent } from '../context-menu-entry.component';
import { ContextMenuEntryType } from '../context-menu-entry-type';
import { BehaviorSubject, Observable, Subscription, map, startWith, tap } from 'rxjs';
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { CorrectionTypeMode } from '../../../core/submission/models/correction-type-mode.model';
import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model';
import { NotificationsService } from '../../notifications/notifications.service';
import { hasValue, isNotEmpty } from '../../empty.util';
import { rendersContextMenuEntriesForType } from '../context-menu.decorator';
import { getCorrectionTypePageRoute } from '../../../app-routing-paths';
@Component({
selector: 'ds-correction-type-menu',
templateUrl: './correction-type-menu.component.html',
})
@rendersContextMenuEntriesForType(DSpaceObjectType.ITEM)
export class CorrectionTypeMenuComponent extends ContextMenuEntryComponent implements OnInit, OnDestroy {
/**
* The menu entry type
*/
public static menuEntryType: ContextMenuEntryType = ContextMenuEntryType.CorrectionType;
/**
* A boolean representing if a request operation is pending
* @type {BehaviorSubject<boolean>}
*/
public processing$ = new BehaviorSubject<boolean>(false);
/**
* Reference to NgbModal
*/
public modalRef: NgbModalRef;
/**
* List of Edit Modes available on this item
* for the current user
*/
private correctionTypes$: BehaviorSubject<CorrectionTypeMode[]> = new BehaviorSubject<CorrectionTypeMode[]>([]);
/**
* Variable to track subscription and unsubscribe it onDestroy
*/
private sub: Subscription;
constructor(
@Inject('contextMenuObjectProvider') protected injectedContextMenuObject: DSpaceObject,
@Inject('contextMenuObjectTypeProvider') protected injectedContextMenuObjectType: DSpaceObjectType,
private correctionTypeService: CorrectionTypeDataService,
public notificationService: NotificationsService
) {
super(injectedContextMenuObject, injectedContextMenuObjectType, ContextMenuEntryType.EditSubmission);
}
ngOnInit(): void {
this.notificationService.claimedProfile.subscribe(() => {
this.getData();
});
}
/**
* Check if edit mode is available
*/
getCorrectionTypes(): Observable<CorrectionTypeMode[]> {
return this.correctionTypes$;
}
/**
* Check if edit mode is available
*/
isAvailable(): Observable<boolean> {
return this.correctionTypes$.asObservable().pipe(
map((type) => isNotEmpty(type) && type.length > 0)
);
}
getData(): void {
this.sub = this.correctionTypeService.findByItem(this.contextMenuObject.id, true).pipe(
tap((types) => console.log(types)),
getAllSucceededRemoteDataPayload(),
getPaginatedListPayload(),
startWith([])
).subscribe((types: CorrectionTypeMode[]) => {
console.log(types);
this.correctionTypes$.next(types);
});
}
getTypeRoute(id: string) {
return getCorrectionTypePageRoute(this.contextMenuObject.id, id);
}
/**
* Make sure the subscription is unsubscribed from when this component is destroyed
*/
ngOnDestroy(): void {
if (hasValue(this.sub)) {
this.sub.unsubscribe();
}
}
}