Skip to content

Commit 7f1e257

Browse files
Merged dspace-cris-2023_02_x into task/dspace-cris-2023_02_x/DSC-2734
2 parents 0c40d16 + d73e008 commit 7f1e257

79 files changed

Lines changed: 717 additions & 308 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cypress/e2e/my-dspace.cy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ describe('My DSpace page', () => {
119119
// Open the New Import dropdown
120120
cy.get('button[data-test="import-dropdown"]').click();
121121
// Click on the "Item" type in that dropdown
122-
cy.get('#importControlsDropdownMenu button[title="Equipment"]').click();
122+
cy.get('#importControlsDropdownMenu button[title="Funding"]').click();
123123

124124
// New URL should include /import-external, as we've moved to the import page
125125
cy.url().should('include', '/import-external');

cypress/support/e2e.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ before(() => {
5151
cy.task('saveRestBaseDomain', baseDomain);
5252

5353
});
54+
55+
// We might receive uncaught exceptions from external libraries (e.g. it happened before with a broken
56+
// version of the addToAny plugin). These should not cause our tests to fail, so we catch them here.
57+
Cypress.on('uncaught:exception', (err, runnable) => {
58+
// returning false here prevents Cypress from failing the test
59+
return false;
60+
});
5461
});
5562

5663
// Runs once before the first test in each "block"

src/app/core/submission/models/sherpa-policies-details.model.ts renamed to src/app/core/submission/models/opf-policies-details.model.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
/**
22
* An interface to represent an access condition.
33
*/
4-
export class SherpaPoliciesDetailsObject {
4+
export class JiscOpfPoliciesDetailsObject {
55

66
/**
7-
* The sherpa policies error
7+
* The Jisc Open Policy Finder policies error
88
*/
99
error: boolean;
1010

1111
/**
12-
* The sherpa policies journal details
12+
* The Jisc Open Policy Finder policies journal details
1313
*/
1414
journals: Journal[];
1515

1616
/**
17-
* The sherpa policies message
17+
* The Jisc Open Policy Finder policies message
1818
*/
1919
message: string;
2020

2121
/**
22-
* The sherpa policies metadata
22+
* The Jisc Open Policy Finder policies metadata
2323
*/
2424
metadata: Metadata;
2525

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { JiscOpfPoliciesDetailsObject } from './opf-policies-details.model';
2+
3+
/**
4+
* An interface to represent the submission's item accesses condition.
5+
*/
6+
export interface WorkspaceitemSectionJiscOpfPoliciesObject {
7+
8+
/**
9+
* The access condition id
10+
*/
11+
id: string;
12+
13+
/**
14+
* The Jisc Open Policy Finder policies retrievalTime
15+
*/
16+
retrievalTime: string;
17+
18+
/**
19+
* The Jisc Open Policy Finder policies details
20+
*/
21+
opfResponse: JiscOpfPoliciesDetailsObject;
22+
}

src/app/core/submission/models/workspaceitem-section-sherpa-policies.model.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/app/core/submission/models/workspaceitem-sections.model.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { WorkspaceitemSectionAccessesObject } from './workspaceitem-section-accesses.model';
22
import { WorkspaceitemSectionFormObject } from './workspaceitem-section-form.model';
33
import { WorkspaceitemSectionLicenseObject } from './workspaceitem-section-license.model';
4+
import { WorkspaceitemSectionJiscOpfPoliciesObject } from './workspaceitem-section-opf-policies.model';
45
import { WorkspaceitemSectionUploadObject } from './workspaceitem-section-upload.model';
56
import { WorkspaceitemSectionCcLicenseObject } from './workspaceitem-section-cc-license.model';
67
import { WorkspaceitemSectionIdentifiersObject } from './workspaceitem-section-identifiers.model';
7-
import { WorkspaceitemSectionSherpaPoliciesObject } from './workspaceitem-section-sherpa-policies.model';
88
import { WorkspaceitemSectionDetectDuplicateObject } from './workspaceitem-section-deduplication.model';
99
import { WorkspaceitemSectionCorrectionObject } from './workspaceitem-section-correction.model';
1010

@@ -25,7 +25,7 @@ export type WorkspaceitemSectionDataType
2525
| WorkspaceitemSectionLicenseObject
2626
| WorkspaceitemSectionCcLicenseObject
2727
| WorkspaceitemSectionAccessesObject
28-
| WorkspaceitemSectionSherpaPoliciesObject
28+
| WorkspaceitemSectionJiscOpfPoliciesObject
2929
| WorkspaceitemSectionIdentifiersObject
3030
| WorkspaceitemSectionDetectDuplicateObject
3131
| WorkspaceitemSectionCorrectionObject

src/app/lucky-search/search/lucky-search.component.spec.ts

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {getBitstreamDownloadRoute} from '../../app-routing-paths';
2525
import {PLATFORM_ID} from '@angular/core';
2626
import {NotificationsService} from '../../shared/notifications/notifications.service';
2727
import {NotificationsServiceStub} from '../../shared/testing/notifications-service.stub';
28+
import {APP_CONFIG} from '../../../config/app-config.interface';
2829

2930
describe('LuckySearchComponent', () => {
3031
let fixture: ComponentFixture<LuckySearchComponent>;
@@ -101,6 +102,7 @@ describe('LuckySearchComponent', () => {
101102
{provide: HardRedirectService, useValue: hardRedirectService},
102103
{provide: PLATFORM_ID, useValue: 'browser'},
103104
{provide: NotificationsService, useValue: new NotificationsServiceStub()},
105+
{provide: APP_CONFIG, useValue: {}},
104106
],
105107
})
106108
.compileComponents();
@@ -288,30 +290,83 @@ describe('LuckySearchComponent', () => {
288290
component = fixture.componentInstance;
289291
});
290292

291-
it('should not redirect when no bitstreams are found', () => {
292-
const item = Object.assign(new Item(), {uuid: 'item-uuid-1', name: 'Test item 1'});
293-
const data = createSuccessfulRemoteDataObject(createPaginatedList([
294-
{indexableObject: item, hitHighlights: {}}
295-
])) as any;
296-
component.resultsRD$.next(data);
297-
component.bitstreamFilters$.next([{metadataName: 'dc.title', metadataValue: 'Non-existent bitstream'}]);
298-
bitstreamDataService.findByItem.and.returnValue(createSuccessfulRemoteDataObject$(createPaginatedList([])));
299-
spyOn(component, 'redirect');
300-
fixture.detectChanges();
301-
expect(component.redirect).not.toHaveBeenCalled();
302-
});
293+
it('should not redirect when no bitstreams are found', () => {
294+
const item = Object.assign(new Item(), {uuid: 'item-uuid-1', name: 'Test item 1'});
295+
const data = createSuccessfulRemoteDataObject(createPaginatedList([
296+
{indexableObject: item, hitHighlights: {}}
297+
])) as any;
298+
component.resultsRD$.next(data);
299+
component.bitstreamFilters$.next([{metadataName: 'dc.title', metadataValue: 'Non-existent bitstream'}]);
300+
bitstreamDataService.findByItem.and.returnValue(createSuccessfulRemoteDataObject$(createPaginatedList([])));
301+
spyOn(component, 'redirect');
302+
fixture.detectChanges();
303+
expect(component.redirect).not.toHaveBeenCalled();
304+
});
303305

304-
it('should update showEmptySearchSection$ when no results are found', () => {
306+
it('should update showEmptySearchSection$ when no results are found', () => {
305307
fixture.detectChanges();
306-
const emptyResults = createSuccessfulRemoteDataObject(createPaginatedList([]));
308+
const emptyResults = createSuccessfulRemoteDataObject(createPaginatedList([]));
307309

308-
spyOn(component as any, 'getLuckySearchResults').and.returnValue(observableOf(emptyResults));
309-
spyOn(component as any, 'processSearchResults').and.returnValue(observableOf(emptyResults));
310+
spyOn(component as any, 'getLuckySearchResults').and.returnValue(observableOf(emptyResults));
311+
spyOn(component as any, 'processSearchResults').and.returnValue(observableOf(emptyResults));
310312

311-
component.getSearchResults();
313+
component.getSearchResults();
314+
315+
expect(component.showEmptySearchSection$.getValue()).toBe(true);
316+
});
312317

313-
expect(component.showEmptySearchSection$.getValue()).toBe(true);
314318
});
315319

320+
describe('', () => {
321+
beforeEach(() => {
322+
fixture = TestBed.createComponent(LuckySearchComponent);
323+
component = fixture.componentInstance;
324+
});
325+
326+
it('should return default code when no specific identifier is found', () => {
327+
// @ts-ignore: Accessing private method for testing
328+
component.appConfig = {
329+
luckySearchRedirects: {
330+
default: 301
331+
}
332+
} as any;
333+
// @ts-ignore: Accessing private method for testing
334+
component.currentFilter = {identifier: 'unknown'};
335+
336+
// @ts-ignore: Accessing private method for testing
337+
const result = component.getRedirectCode();
338+
339+
expect(result).toBe(301);
340+
});
341+
342+
it('should return 302 when default is not set and identifier is not found', () => {
343+
// @ts-ignore: Accessing private method for testing
344+
component.appConfig = {
345+
luckySearchRedirects: {}
346+
} as any;
347+
// @ts-ignore: Accessing private method for testing
348+
component.currentFilter = {identifier: 'unknown'};
349+
350+
// @ts-ignore: Accessing private method for testing
351+
const result = component.getRedirectCode();
352+
expect(result).toBe(302);
353+
});
354+
355+
it('should return specific code for known identifier', () => {
356+
// @ts-ignore: Accessing private method for testing
357+
component.appConfig = {
358+
luckySearchRedirects: {
359+
default: 302,
360+
'legacy-id': 301
361+
}
362+
};
363+
// @ts-ignore: Accessing private method for testing
364+
component.currentFilter = {identifier: 'legacy-id'};
365+
366+
// @ts-ignore: Accessing private method for testing
367+
const result = component.getRedirectCode();
368+
expect(result).toBe(301);
369+
});
370+
316371
});
317372
});

src/app/lucky-search/search/lucky-search.component.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {HardRedirectService} from '../../core/services/hard-redirect.service';
2525
import {isPlatformServer} from '@angular/common';
2626
import {NotificationsService} from '../../shared/notifications/notifications.service';
2727
import {TranslateService} from '@ngx-translate/core';
28+
import {APP_CONFIG, AppConfig} from '../../../config/app-config.interface';
2829

2930
@Component({
3031
selector: 'ds-lucky-search',
@@ -77,6 +78,7 @@ export class LuckySearchComponent implements OnInit {
7778
private hardRedirectService: HardRedirectService,
7879
private notificationService: NotificationsService,
7980
private translateService: TranslateService,
81+
@Inject(APP_CONFIG) private appConfig: AppConfig,
8082
) {}
8183

8284
ngOnInit(): void {
@@ -186,12 +188,21 @@ export class LuckySearchComponent implements OnInit {
186188

187189
public redirect(url: string): void {
188190
if (isPlatformServer(this.platformId)) {
189-
this.hardRedirectService.redirect(url, 302);
191+
this.hardRedirectService.redirect(url, this.getRedirectCode());
190192
} else {
191193
this.router.navigateByUrl(url, { replaceUrl: true });
192194
}
193195
}
194196

197+
private getRedirectCode(): number {
198+
let code: number = this.appConfig?.luckySearchRedirects?.default || 302;
199+
if (Object.keys(this.appConfig?.luckySearchRedirects).includes(this.currentFilter.identifier)) {
200+
code = this.appConfig.luckySearchRedirects[this.currentFilter.identifier];
201+
}
202+
203+
return code;
204+
}
205+
195206
private parseBitstreamFilters(queryParams: Params): MetadataFilter[] {
196207
const metadataName = queryParams?.bitstreamMetadata;
197208
const metadataValue = queryParams?.bitstreamValue;

src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-external-dropdown/my-dspace-new-external-dropdown.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@
2121
class="dropdown-menu p-0"
2222
id="importControlsDropdownMenu"
2323
aria-labelledby="dropdownImport">
24-
<ds-entity-dropdown [isSubmission]="false" (selectionChange)="openPage($event)"></ds-entity-dropdown>
24+
<ds-entity-dropdown [isSubmission]="false" [isImportFromExternalSource]="true" (selectionChange)="openPage($event)"></ds-entity-dropdown>
2525
</div>
2626
</div>

src/app/shared/entity-dropdown/entity-dropdown.component.spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,24 @@ describe('EntityDropdownComponent', () => {
136136
expect((component as any).entityTypeService.getAllAuthorizedRelationshipType).toHaveBeenCalled();
137137
});
138138

139+
it('should init component with entities list when isImportFromExternalSource is true', () => {
140+
component.isSubmission = false;
141+
component.isImportFromExternalSource = true;
142+
spyOn(component.subs, 'push');
143+
spyOn(component, 'resetPagination');
144+
spyOn(component, 'populateEntityList').and.callThrough();
145+
146+
scheduler.schedule(() => fixture.detectChanges());
147+
scheduler.flush();
148+
const elements = fixture.debugElement.queryAll(By.css('.entity-item'));
149+
150+
expect(elements.length).toEqual(5);
151+
expect(component.subs.push).toHaveBeenCalled();
152+
expect(component.resetPagination).toHaveBeenCalled();
153+
expect(component.populateEntityList).toHaveBeenCalled();
154+
expect((component as any).entityTypeService.getAllAuthorizedRelationshipTypeImport).toHaveBeenCalled();
155+
});
156+
139157
it('should trigger onSelect method when select a new entity from list', () => {
140158
scheduler.schedule(() => fixture.detectChanges());
141159
scheduler.flush();

0 commit comments

Comments
 (0)