mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
71806: Use server config for curation tasks
This commit is contained in:
@@ -160,6 +160,8 @@ import { SubmissionCcLicenseDataService } from './submission/submission-cc-licen
|
||||
import { SubmissionCcLicence } from './submission/models/submission-cc-license.model';
|
||||
import { SubmissionCcLicenceUrl } from './submission/models/submission-cc-license-url.model';
|
||||
import { SubmissionCcLicenseUrlDataService } from './submission/submission-cc-license-url-data.service';
|
||||
import { ConfigurationDataService } from './data/configuration-data.service';
|
||||
import { ConfigurationProperty } from './shared/configuration-property.model';
|
||||
|
||||
/**
|
||||
* When not in production, endpoint responses can be mocked for testing purposes
|
||||
@@ -245,6 +247,7 @@ const PROVIDERS = [
|
||||
UploaderService,
|
||||
FileService,
|
||||
DSpaceObjectDataService,
|
||||
ConfigurationDataService,
|
||||
DSOChangeAnalyzer,
|
||||
DefaultChangeAnalyzer,
|
||||
ArrayMoveChangeAnalyzer,
|
||||
@@ -350,7 +353,8 @@ export const models =
|
||||
TemplateItem,
|
||||
Feature,
|
||||
Authorization,
|
||||
Registration
|
||||
Registration,
|
||||
ConfigurationProperty
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
87
src/app/core/data/configuration-data.service.spec.ts
Normal file
87
src/app/core/data/configuration-data.service.spec.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { cold, getTestScheduler } from 'jasmine-marbles';
|
||||
import { TestScheduler } from 'rxjs/testing';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { FindByIDRequest } from './request.models';
|
||||
import { RequestService } from './request.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { ConfigurationDataService } from './configuration-data.service';
|
||||
import { ConfigurationProperty } from '../shared/configuration-property.model';
|
||||
|
||||
describe('ConfigurationDataService', () => {
|
||||
let scheduler: TestScheduler;
|
||||
let service: ConfigurationDataService;
|
||||
let halService: HALEndpointService;
|
||||
let requestService: RequestService;
|
||||
let rdbService: RemoteDataBuildService;
|
||||
let objectCache: ObjectCacheService;
|
||||
const testObject = {
|
||||
uuid: 'test-property',
|
||||
name: 'test-property',
|
||||
values: ['value-1', 'value-2']
|
||||
} as ConfigurationProperty;
|
||||
const configLink = 'https://rest.api/rest/api/config/properties';
|
||||
const requestURL = `https://rest.api/rest/api/config/properties/${testObject.name}`;
|
||||
const requestUUID = 'test-property';
|
||||
|
||||
beforeEach(() => {
|
||||
scheduler = getTestScheduler();
|
||||
|
||||
halService = jasmine.createSpyObj('halService', {
|
||||
getEndpoint: cold('a', {a: configLink})
|
||||
});
|
||||
requestService = jasmine.createSpyObj('requestService', {
|
||||
generateRequestId: requestUUID,
|
||||
configure: true
|
||||
});
|
||||
rdbService = jasmine.createSpyObj('rdbService', {
|
||||
buildSingle: cold('a', {
|
||||
a: {
|
||||
payload: testObject
|
||||
}
|
||||
})
|
||||
});
|
||||
objectCache = {} as ObjectCacheService;
|
||||
const notificationsService = {} as NotificationsService;
|
||||
const http = {} as HttpClient;
|
||||
const comparator = {} as any;
|
||||
|
||||
service = new ConfigurationDataService(
|
||||
requestService,
|
||||
rdbService,
|
||||
objectCache,
|
||||
halService,
|
||||
notificationsService,
|
||||
http,
|
||||
comparator
|
||||
);
|
||||
});
|
||||
|
||||
describe('findById', () => {
|
||||
it('should call HALEndpointService with the path to the properties endpoint', () => {
|
||||
scheduler.schedule(() => service.findByPropertyName(testObject.name));
|
||||
scheduler.flush();
|
||||
|
||||
expect(halService.getEndpoint).toHaveBeenCalledWith('properties');
|
||||
});
|
||||
|
||||
it('should configure the proper FindByIDRequest', () => {
|
||||
scheduler.schedule(() => service.findByPropertyName(testObject.name));
|
||||
scheduler.flush();
|
||||
|
||||
expect(requestService.configure).toHaveBeenCalledWith(new FindByIDRequest(requestUUID, requestURL, testObject.name));
|
||||
});
|
||||
|
||||
it('should return a RemoteData<ConfigurationProperty> for the object with the given name', () => {
|
||||
const result = service.findByPropertyName(testObject.name);
|
||||
const expected = cold('a', {
|
||||
a: {
|
||||
payload: testObject
|
||||
}
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
});
|
||||
});
|
66
src/app/core/data/configuration-data.service.ts
Normal file
66
src/app/core/data/configuration-data.service.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||
import { DSPACE_OBJECT } from '../shared/dspace-object.resource-type';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { DataService } from './data.service';
|
||||
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
|
||||
import { RemoteData } from './remote-data';
|
||||
import { RequestService } from './request.service';
|
||||
import { ConfigurationProperty } from '../shared/configuration-property.model';
|
||||
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
|
||||
import { CONFIG_PROPERTY } from '../shared/config-property.resource-type';
|
||||
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
class DataServiceImpl extends DataService<ConfigurationProperty> {
|
||||
protected linkPath = 'properties';
|
||||
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected http: HttpClient,
|
||||
protected comparator: DefaultChangeAnalyzer<ConfigurationProperty>) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@dataService(CONFIG_PROPERTY)
|
||||
/**
|
||||
* Data Service responsible for retrieving Configuration properties
|
||||
*/
|
||||
export class ConfigurationDataService {
|
||||
protected linkPath = 'properties';
|
||||
private dataService: DataServiceImpl;
|
||||
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected http: HttpClient,
|
||||
protected comparator: DefaultChangeAnalyzer<ConfigurationProperty>) {
|
||||
this.dataService = new DataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a configuration property by name
|
||||
* @param name
|
||||
*/
|
||||
findByPropertyName(name: string): Observable<RemoteData<ConfigurationProperty>> {
|
||||
return this.dataService.findById(name);
|
||||
}
|
||||
}
|
9
src/app/core/shared/config-property.resource-type.ts
Normal file
9
src/app/core/shared/config-property.resource-type.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { ResourceType } from './resource-type';
|
||||
|
||||
/**
|
||||
* The resource type for ConfigurationProperty
|
||||
*
|
||||
* Needs to be in a separate file to prevent circular
|
||||
* dependencies in webpack.
|
||||
*/
|
||||
export const CONFIG_PROPERTY = new ResourceType('property');
|
48
src/app/core/shared/configuration-property.model.ts
Normal file
48
src/app/core/shared/configuration-property.model.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { autoserialize, autoserializeAs, deserialize } from 'cerialize';
|
||||
import { typedObject } from '../cache/builders/build-decorators';
|
||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||
import { excludeFromEquals } from '../utilities/equals.decorators';
|
||||
import { HALLink } from './hal-link.model';
|
||||
import { ResourceType } from './resource-type';
|
||||
import { CONFIG_PROPERTY } from './config-property.resource-type';
|
||||
|
||||
/**
|
||||
* Model class for a Configuration Property
|
||||
*/
|
||||
@typedObject
|
||||
export class ConfigurationProperty implements CacheableObject {
|
||||
static type = CONFIG_PROPERTY;
|
||||
|
||||
/**
|
||||
* The object type
|
||||
*/
|
||||
@excludeFromEquals
|
||||
@autoserialize
|
||||
type: ResourceType;
|
||||
|
||||
/**
|
||||
* The uuid of the configuration property
|
||||
* The name is used as id for configuration properties
|
||||
*/
|
||||
@autoserializeAs(String, 'name')
|
||||
uuid: string;
|
||||
|
||||
/**
|
||||
* The name of the configuration property
|
||||
*/
|
||||
@autoserialize
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* The values of the configuration property
|
||||
*/
|
||||
@autoserialize
|
||||
values: string[];
|
||||
|
||||
/**
|
||||
* The links of the configuration property
|
||||
*/
|
||||
@deserialize
|
||||
_links: { self: HALLink };
|
||||
|
||||
}
|
@@ -4,8 +4,8 @@
|
||||
<div class="col-12 col-sm-6">
|
||||
<label class="font-weight-bold" for="task">{{'curation.form.task-select.label' |translate }}</label>
|
||||
<select id="task" formControlName="task" class="form-control">
|
||||
<option *ngFor="let task of tasks" [ngValue]="task">
|
||||
{{ task.label | translate }}
|
||||
<option *ngFor="let task of tasks" [value]="task">
|
||||
{{ 'curation-task.task.' + task + '.label' | translate }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
@@ -17,6 +17,8 @@ import { NotificationsService } from '../shared/notifications/notifications.serv
|
||||
import { Router } from '@angular/router';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { ConfigurationDataService } from '../core/data/configuration-data.service';
|
||||
import { ConfigurationProperty } from '../core/shared/configuration-property.model';
|
||||
|
||||
describe('CurationFormComponent', () => {
|
||||
let comp: CurationFormComponent;
|
||||
@@ -24,6 +26,7 @@ describe('CurationFormComponent', () => {
|
||||
|
||||
let scriptDataService: ScriptDataService;
|
||||
let processDataService: ProcessDataService;
|
||||
let configurationDataService: ConfigurationDataService;
|
||||
let authService: AuthService;
|
||||
let notificationsService;
|
||||
let router;
|
||||
@@ -49,6 +52,17 @@ describe('CurationFormComponent', () => {
|
||||
getAuthenticatedUserFromStore: observableOf(Object.assign(new EPerson(), {email: 'test@mail'}))
|
||||
});
|
||||
|
||||
configurationDataService = jasmine.createSpyObj('configurationDataService', {
|
||||
findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), {
|
||||
name: 'plugin.named.org.dspace.curate.CurationTask',
|
||||
values: [
|
||||
'org.dspace.ctask.general.ProfileFormats = profileformats',
|
||||
'org.dspace.ctask.general.RequiredMetadata = requiredmetadata',
|
||||
'org.dspace.ctask.general.MetadataValueLinkChecker = checklinks'
|
||||
]
|
||||
}))
|
||||
});
|
||||
|
||||
notificationsService = new NotificationsServiceStub();
|
||||
router = new RouterStub();
|
||||
|
||||
@@ -61,6 +75,7 @@ describe('CurationFormComponent', () => {
|
||||
{provide: AuthService, useValue: authService},
|
||||
{provide: NotificationsService, useValue: notificationsService},
|
||||
{provide: Router, useValue: router},
|
||||
{provide: ConfigurationDataService, useValue: configurationDataService},
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
}).compileComponents();
|
||||
|
@@ -1,12 +1,10 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { ScriptDataService } from '../core/data/processes/script-data.service';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { CurationTask } from '../../config/curation-task.interface';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { getResponseFromEntry } from '../core/shared/operators';
|
||||
import { DSOSuccessResponse } from '../core/cache/response.models';
|
||||
import { AuthService } from '../core/auth/auth.service';
|
||||
import { filter, switchMap, take } from 'rxjs/operators';
|
||||
import { filter, map, switchMap, take } from 'rxjs/operators';
|
||||
import { EPerson } from '../core/eperson/models/eperson.model';
|
||||
import { NotificationsService } from '../shared/notifications/notifications.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@@ -15,6 +13,12 @@ import { RemoteData } from '../core/data/remote-data';
|
||||
import { Router } from '@angular/router';
|
||||
import { ProcessDataService } from '../core/data/processes/process-data.service';
|
||||
import { Process } from '../process-page/processes/process.model';
|
||||
import { ConfigurationDataService } from '../core/data/configuration-data.service';
|
||||
import { ConfigurationProperty } from '../core/shared/configuration-property.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { find } from 'rxjs/internal/operators/find';
|
||||
|
||||
export const CURATION_CFG = 'plugin.named.org.dspace.curate.CurationTask';
|
||||
|
||||
/**
|
||||
* Component responsible for rendering the Curation Task form
|
||||
@@ -25,7 +29,8 @@ import { Process } from '../process-page/processes/process.model';
|
||||
})
|
||||
export class CurationFormComponent implements OnInit {
|
||||
|
||||
tasks: CurationTask[];
|
||||
config: Observable<RemoteData<ConfigurationProperty>>;
|
||||
tasks: string[];
|
||||
form: FormGroup;
|
||||
|
||||
@Input()
|
||||
@@ -33,6 +38,7 @@ export class CurationFormComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
private scriptDataService: ScriptDataService,
|
||||
private configurationDataService: ConfigurationDataService,
|
||||
private processDataService: ProcessDataService,
|
||||
private authService: AuthService,
|
||||
private notificationsService: NotificationsService,
|
||||
@@ -42,12 +48,19 @@ export class CurationFormComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.tasks = environment.curationTasks;
|
||||
|
||||
this.form = new FormGroup({
|
||||
task: new FormControl(this.tasks[0]),
|
||||
task: new FormControl(''),
|
||||
handle: new FormControl('')
|
||||
});
|
||||
|
||||
this.config = this.configurationDataService.findByPropertyName(CURATION_CFG);
|
||||
this.config.pipe(
|
||||
find((rd: RemoteData<ConfigurationProperty>) => rd.hasSucceeded),
|
||||
map((rd: RemoteData<ConfigurationProperty>) => rd.payload)
|
||||
).subscribe((configProperties) => {
|
||||
this.tasks = configProperties.values.map((value) => value.split('=')[1].trim());
|
||||
this.form.get('task').patchValue(this.tasks[0]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,7 +78,7 @@ export class CurationFormComponent implements OnInit {
|
||||
* Navigate to the process page on success
|
||||
*/
|
||||
submit() {
|
||||
const taskName = (this.form.get('task').value as CurationTask).name;
|
||||
const taskName = this.form.get('task').value;
|
||||
let handle;
|
||||
if (this.hasHandleValue()) {
|
||||
handle = this.dsoHandle;
|
||||
|
@@ -917,11 +917,17 @@
|
||||
|
||||
|
||||
|
||||
"curation-task.task.checklinks.label": "Check Links in Metadata",
|
||||
|
||||
"curation-task.task.noop.label": "NOOP",
|
||||
|
||||
"curation-task.task.profileformats.label": "Profile Bitstream Formats",
|
||||
|
||||
"curation-task.task.requiredmetadata.label": "Check for Required Metadata",
|
||||
|
||||
"curation-task.task.checklinks.label": "Check Links in Metadata",
|
||||
"curation-task.task.translate.label": "Microsoft Translator",
|
||||
|
||||
"curation-task.task.vscan.label": "Virus Scan",
|
||||
|
||||
|
||||
|
||||
|
@@ -1,9 +0,0 @@
|
||||
import { Config } from './config.interface';
|
||||
|
||||
/**
|
||||
* An interface to represent a curation task in the configuration. A CurationTask has a name and a label.
|
||||
*/
|
||||
export interface CurationTask extends Config {
|
||||
name: string;
|
||||
label: string;
|
||||
}
|
@@ -11,7 +11,6 @@ import { ItemPageConfig } from './item-page-config.interface';
|
||||
import { CollectionPageConfig } from './collection-page-config.interface';
|
||||
import { Theme } from './theme.inferface';
|
||||
import {AuthConfig} from './auth-config.interfaces';
|
||||
import { CurationTask } from './curation-task.interface';
|
||||
|
||||
export interface GlobalConfig extends Config {
|
||||
ui: ServerConfig;
|
||||
@@ -32,5 +31,4 @@ export interface GlobalConfig extends Config {
|
||||
item: ItemPageConfig;
|
||||
collection: CollectionPageConfig;
|
||||
theme: Theme;
|
||||
curationTasks: CurationTask[];
|
||||
}
|
||||
|
@@ -213,18 +213,4 @@ export const environment: GlobalConfig = {
|
||||
theme: {
|
||||
name: 'default',
|
||||
},
|
||||
curationTasks: [
|
||||
{
|
||||
name: 'profileformats',
|
||||
label: 'curation-task.task.profileformats.label'
|
||||
},
|
||||
{
|
||||
name: 'requiredmetadata',
|
||||
label: 'curation-task.task.requiredmetadata.label'
|
||||
},
|
||||
{
|
||||
name: 'checklinks',
|
||||
label: 'curation-task.task.checklinks.label'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
@@ -196,18 +196,4 @@ export const environment: Partial<GlobalConfig> = {
|
||||
theme: {
|
||||
name: 'default',
|
||||
},
|
||||
curationTasks: [
|
||||
{
|
||||
name: 'profileformats',
|
||||
label: 'curation-task.task.profileformats.label'
|
||||
},
|
||||
{
|
||||
name: 'requiredmetadata',
|
||||
label: 'curation-task.task.requiredmetadata.label'
|
||||
},
|
||||
{
|
||||
name: 'checklinks',
|
||||
label: 'curation-task.task.checklinks.label'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
Reference in New Issue
Block a user