Skip to content

Commit a7faf7d

Browse files
107671: Fix handle theme not working with canonical prefix https://hdl.handle.net/
1 parent ff0c1a2 commit a7faf7d

File tree

10 files changed

+348
-166
lines changed

10 files changed

+348
-166
lines changed

src/app/core/data/configuration-data.service.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable max-classes-per-file */
21
import { Injectable } from '@angular/core';
32
import { Observable } from 'rxjs';
43
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';

src/app/curation-form/curation-form.component.spec.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
1+
import { ComponentFixture, TestBed, waitForAsync, fakeAsync, flush } from '@angular/core/testing';
22
import { TranslateModule } from '@ngx-translate/core';
33
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
44
import { CurationFormComponent } from './curation-form.component';
@@ -16,6 +16,7 @@ import { ConfigurationDataService } from '../core/data/configuration-data.servic
1616
import { ConfigurationProperty } from '../core/shared/configuration-property.model';
1717
import { getProcessDetailRoute } from '../process-page/process-page-routing.paths';
1818
import { HandleService } from '../shared/handle.service';
19+
import { of as observableOf } from 'rxjs';
1920

2021
describe('CurationFormComponent', () => {
2122
let comp: CurationFormComponent;
@@ -54,7 +55,7 @@ describe('CurationFormComponent', () => {
5455
});
5556

5657
handleService = {
57-
normalizeHandle: (a) => a
58+
normalizeHandle: (a: string) => observableOf(a),
5859
} as any;
5960

6061
notificationsService = new NotificationsServiceStub();
@@ -151,12 +152,13 @@ describe('CurationFormComponent', () => {
151152
], []);
152153
});
153154

154-
it(`should show an error notification and return when an invalid dsoHandle is provided`, () => {
155+
it(`should show an error notification and return when an invalid dsoHandle is provided`, fakeAsync(() => {
155156
comp.dsoHandle = 'test-handle';
156-
spyOn(handleService, 'normalizeHandle').and.returnValue(null);
157+
spyOn(handleService, 'normalizeHandle').and.returnValue(observableOf(null));
157158
comp.submit();
159+
flush();
158160

159161
expect(notificationsService.error).toHaveBeenCalled();
160162
expect(scriptDataService.invoke).not.toHaveBeenCalled();
161-
});
163+
}));
162164
});
Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
1-
import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
1+
import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
22
import { ScriptDataService } from '../core/data/processes/script-data.service';
33
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
4-
import { getFirstCompletedRemoteData } from '../core/shared/operators';
5-
import { find, map } from 'rxjs/operators';
4+
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../core/shared/operators';
5+
import { map } from 'rxjs/operators';
66
import { NotificationsService } from '../shared/notifications/notifications.service';
77
import { TranslateService } from '@ngx-translate/core';
88
import { hasValue, isEmpty, isNotEmpty } from '../shared/empty.util';
99
import { RemoteData } from '../core/data/remote-data';
1010
import { Router } from '@angular/router';
11-
import { ProcessDataService } from '../core/data/processes/process-data.service';
1211
import { Process } from '../process-page/processes/process.model';
1312
import { ConfigurationDataService } from '../core/data/configuration-data.service';
1413
import { ConfigurationProperty } from '../core/shared/configuration-property.model';
15-
import { Observable } from 'rxjs';
14+
import { Observable, Subscription } from 'rxjs';
1615
import { getProcessDetailRoute } from '../process-page/process-page-routing.paths';
1716
import { HandleService } from '../shared/handle.service';
1817

1918
export const CURATION_CFG = 'plugin.named.org.dspace.curate.CurationTask';
19+
2020
/**
2121
* Component responsible for rendering the Curation Task form
2222
*/
2323
@Component({
2424
selector: 'ds-curation-form',
2525
templateUrl: './curation-form.component.html'
2626
})
27-
export class CurationFormComponent implements OnInit {
27+
export class CurationFormComponent implements OnDestroy, OnInit {
2828

2929
config: Observable<RemoteData<ConfigurationProperty>>;
3030
tasks: string[];
@@ -33,10 +33,11 @@ export class CurationFormComponent implements OnInit {
3333
@Input()
3434
dsoHandle: string;
3535

36+
subs: Subscription[] = [];
37+
3638
constructor(
3739
private scriptDataService: ScriptDataService,
3840
private configurationDataService: ConfigurationDataService,
39-
private processDataService: ProcessDataService,
4041
private notificationsService: NotificationsService,
4142
private translateService: TranslateService,
4243
private handleService: HandleService,
@@ -45,23 +46,26 @@ export class CurationFormComponent implements OnInit {
4546
) {
4647
}
4748

49+
ngOnDestroy(): void {
50+
this.subs.forEach((sub: Subscription) => sub.unsubscribe());
51+
}
52+
4853
ngOnInit(): void {
4954
this.form = new UntypedFormGroup({
5055
task: new UntypedFormControl(''),
5156
handle: new UntypedFormControl('')
5257
});
5358

5459
this.config = this.configurationDataService.findByPropertyName(CURATION_CFG);
55-
this.config.pipe(
56-
find((rd: RemoteData<ConfigurationProperty>) => rd.hasSucceeded),
57-
map((rd: RemoteData<ConfigurationProperty>) => rd.payload)
58-
).subscribe((configProperties) => {
60+
this.subs.push(this.config.pipe(
61+
getFirstSucceededRemoteDataPayload(),
62+
).subscribe((configProperties: ConfigurationProperty) => {
5963
this.tasks = configProperties.values
6064
.filter((value) => isNotEmpty(value) && value.includes('='))
6165
.map((value) => value.split('=')[1].trim());
6266
this.form.get('task').patchValue(this.tasks[0]);
6367
this.cdr.detectChanges();
64-
});
68+
}));
6569
}
6670

6771
/**
@@ -77,33 +81,41 @@ export class CurationFormComponent implements OnInit {
7781
*/
7882
submit() {
7983
const taskName = this.form.get('task').value;
80-
let handle;
84+
let handle$: Observable<string | null>;
8185
if (this.hasHandleValue()) {
82-
handle = this.handleService.normalizeHandle(this.dsoHandle);
83-
if (isEmpty(handle)) {
84-
this.notificationsService.error(this.translateService.get('curation.form.submit.error.head'),
85-
this.translateService.get('curation.form.submit.error.invalid-handle'));
86-
return;
87-
}
86+
handle$ = this.handleService.normalizeHandle(this.dsoHandle).pipe(
87+
map((handle: string | null) => {
88+
if (isEmpty(handle)) {
89+
this.notificationsService.error(this.translateService.get('curation.form.submit.error.head'),
90+
this.translateService.get('curation.form.submit.error.invalid-handle'));
91+
}
92+
return handle;
93+
}),
94+
);
8895
} else {
89-
handle = this.handleService.normalizeHandle(this.form.get('handle').value);
90-
if (isEmpty(handle)) {
91-
handle = 'all';
92-
}
96+
handle$ = this.handleService.normalizeHandle(this.form.get('handle').value).pipe(
97+
map((handle: string | null) => isEmpty(handle) ? 'all' : handle),
98+
);
9399
}
94100

95-
this.scriptDataService.invoke('curate', [
96-
{ name: '-t', value: taskName },
97-
{ name: '-i', value: handle },
98-
], []).pipe(getFirstCompletedRemoteData()).subscribe((rd: RemoteData<Process>) => {
99-
if (rd.hasSucceeded) {
100-
this.notificationsService.success(this.translateService.get('curation.form.submit.success.head'),
101-
this.translateService.get('curation.form.submit.success.content'));
102-
this.router.navigateByUrl(getProcessDetailRoute(rd.payload.processId));
103-
} else {
104-
this.notificationsService.error(this.translateService.get('curation.form.submit.error.head'),
105-
this.translateService.get('curation.form.submit.error.content'));
101+
this.subs.push(handle$.subscribe((handle: string) => {
102+
if (hasValue(handle)) {
103+
this.subs.push(this.scriptDataService.invoke('curate', [
104+
{ name: '-t', value: taskName },
105+
{ name: '-i', value: handle },
106+
], []).pipe(
107+
getFirstCompletedRemoteData(),
108+
).subscribe((rd: RemoteData<Process>) => {
109+
if (rd.hasSucceeded) {
110+
this.notificationsService.success(this.translateService.get('curation.form.submit.success.head'),
111+
this.translateService.get('curation.form.submit.success.content'));
112+
void this.router.navigateByUrl(getProcessDetailRoute(rd.payload.processId));
113+
} else {
114+
this.notificationsService.error(this.translateService.get('curation.form.submit.error.head'),
115+
this.translateService.get('curation.form.submit.error.content'));
116+
}
117+
}));
106118
}
107-
});
119+
}));
108120
}
109121
}
Lines changed: 58 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,79 @@
11
import { HandleService } from './handle.service';
2+
import { TestBed } from '@angular/core/testing';
3+
import { ConfigurationDataServiceStub } from './testing/configuration-data.service.stub';
4+
import { ConfigurationDataService } from '../core/data/configuration-data.service';
5+
import { of as observableOf } from 'rxjs';
26

37
describe('HandleService', () => {
48
let service: HandleService;
59

10+
let configurationService: ConfigurationDataServiceStub;
11+
612
beforeEach(() => {
7-
service = new HandleService();
13+
configurationService = new ConfigurationDataServiceStub();
14+
15+
TestBed.configureTestingModule({
16+
providers: [
17+
{ provide: ConfigurationDataService, useValue: configurationService },
18+
],
19+
});
20+
service = TestBed.inject(HandleService);
821
});
922

1023
describe(`normalizeHandle`, () => {
11-
it(`should simply return an already normalized handle`, () => {
12-
let input, output;
13-
14-
input = '123456789/123456';
15-
output = service.normalizeHandle(input);
16-
expect(output).toEqual(input);
24+
it('should normalize a handle url with custom conical prefix with trailing slash', (done: DoneFn) => {
25+
service.canonicalPrefix$ = observableOf('https://hdl.handle.net/');
1726

18-
input = '12.3456.789/123456';
19-
output = service.normalizeHandle(input);
20-
expect(output).toEqual(input);
27+
service.normalizeHandle('https://hdl.handle.net/123456789/123456').subscribe((handle: string | null) => {
28+
expect(handle).toBe('123456789/123456');
29+
done();
30+
});
2131
});
2232

23-
it(`should normalize a handle url`, () => {
24-
let input, output;
33+
it('should normalize a handle url with custom conical prefix without trailing slash', (done: DoneFn) => {
34+
service.canonicalPrefix$ = observableOf('https://hdl.handle.net');
2535

26-
input = 'https://hdl.handle.net/handle/123456789/123456';
27-
output = service.normalizeHandle(input);
28-
expect(output).toEqual('123456789/123456');
36+
service.normalizeHandle('https://hdl.handle.net/123456789/123456').subscribe((handle: string | null) => {
37+
expect(handle).toBe('123456789/123456');
38+
done();
39+
});
40+
});
41+
42+
describe('should simply return an already normalized handle', () => {
43+
it('123456789/123456', (done: DoneFn) => {
44+
service.normalizeHandle('123456789/123456').subscribe((handle: string | null) => {
45+
expect(handle).toBe('123456789/123456');
46+
done();
47+
});
48+
});
2949

30-
input = 'https://rest.api/server/handle/123456789/123456';
31-
output = service.normalizeHandle(input);
32-
expect(output).toEqual('123456789/123456');
50+
it('12.3456.789/123456', (done: DoneFn) => {
51+
service.normalizeHandle('12.3456.789/123456').subscribe((handle: string | null) => {
52+
expect(handle).toBe('12.3456.789/123456');
53+
done();
54+
});
55+
});
3356
});
3457

35-
it(`should return null if the input doesn't contain a handle`, () => {
36-
let input, output;
58+
it('should normalize handle urls starting with handle', (done: DoneFn) => {
59+
service.normalizeHandle('https://rest.api/server/handle/123456789/123456').subscribe((handle: string | null) => {
60+
expect(handle).toBe('123456789/123456');
61+
done();
62+
});
63+
});
3764

38-
input = 'https://hdl.handle.net/handle/123456789';
39-
output = service.normalizeHandle(input);
40-
expect(output).toBeNull();
65+
it('should return null if the input doesn\'t contain a valid handle', (done: DoneFn) => {
66+
service.normalizeHandle('https://hdl.handle.net/123456789').subscribe((handle: string | null) => {
67+
expect(handle).toBeNull();
68+
done();
69+
});
70+
});
4171

42-
input = 'something completely different';
43-
output = service.normalizeHandle(input);
44-
expect(output).toBeNull();
72+
it('should return null if the input doesn\'t contain a handle', (done: DoneFn) => {
73+
service.normalizeHandle('something completely different').subscribe((handle: string | null) => {
74+
expect(handle).toBeNull();
75+
done();
76+
});
4577
});
4678
});
4779
});

0 commit comments

Comments
 (0)