Skip to content

Commit 021eb43

Browse files
committed
Merge branch 'dspace-cris-2023_02_x' into main-cris
# Conflicts: # src/config/default-app-config.ts # src/environments/environment.test.ts
2 parents 00aa481 + ff3d4c8 commit 021eb43

22 files changed

+317
-185
lines changed

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
"build:stats": "ng build --stats-json",
1818
"build:ci": "ng config cli.cache.environment ci && yarn run build:ssr",
1919
"build:prod": "cross-env NODE_ENV=production yarn run build:ssr",
20-
"build:ssr": "npm run ng-high-memory -- build --configuration production && ng run dspace-angular:server:production",
21-
"ng-high-memory": "node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng",
20+
"build:ssr": "npm run ng-high-memory -- build --configuration production && npm run ng-high-memory -- run dspace-angular:server:production",
21+
"ng-high-memory": "node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng",
2222
"test": "npm run ng-high-memory -- test --source-map=true --watch=false --configuration test",
2323
"test:watch": "nodemon --exec \"npm run ng-high-memory -- test --source-map=true --watch=true --configuration test\"",
2424
"test:headless": "npm run ng-high-memory -- test --source-map=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage",

src/app/core/json-patch/builder/json-patch-operations-builder.ts

+12-25
Original file line numberDiff line numberDiff line change
@@ -110,27 +110,25 @@ export class JsonPatchOperationsBuilder {
110110
operationValue = [];
111111
value.forEach((entry) => {
112112
if ((typeof entry === 'object')) {
113-
if (securityLevel != null) {
113+
if (isNotEmpty(securityLevel)) {
114114
operationValue.push(this.prepareObjectValue(entry, securityLevel));
115115
} else {
116116
operationValue.push(this.prepareObjectValue(entry));
117117
}
118-
119118
} else {
120119
operationValue.push(new FormFieldMetadataValueObject(entry, null, securityLevel));
121120
}
122121
});
123122
} else if (typeof value === 'object') {
124-
if (securityLevel != null) {
123+
if (isNotEmpty(securityLevel)) {
125124
operationValue = this.prepareObjectValue(value, securityLevel);
126125
} else {
127126
operationValue = this.prepareObjectValue(value);
128127
}
129-
130128
} else {
131129
// add the possibility to add security level when value is string
132130
// in this case security level is set on metadata value
133-
if (securityLevel != null) {
131+
if (isNotEmpty(securityLevel)) {
134132
operationValue = new FormFieldMetadataValueObject(value, null, securityLevel);
135133
} else {
136134
operationValue = new FormFieldMetadataValueObject(value, null);
@@ -143,21 +141,21 @@ export class JsonPatchOperationsBuilder {
143141
}
144142

145143
protected prepareObjectValue(value: any, securityLevel = null) {
146-
let operationValue = Object.create({});
144+
let operationValue = Object.create({});
147145
if (isEmpty(value) || value instanceof FormFieldMetadataValueObject) {
148-
if (securityLevel != null) {
149-
operationValue = {...value, securityLevel: securityLevel};
150-
} else {
146+
if (isNotEmpty(securityLevel)) {
147+
operationValue = { ...value, securityLevel: securityLevel };
148+
} else {
151149
operationValue = value;
152150
}
153151
} else if (value instanceof Date) {
154152
if (securityLevel != null) {
155-
operationValue = new FormFieldMetadataValueObject(dateToISOFormat(value), null, securityLevel);
153+
operationValue = new FormFieldMetadataValueObject(dateToISOFormat(value), null, securityLevel);
156154
} else {
157155
operationValue = new FormFieldMetadataValueObject(dateToISOFormat(value));
158156
}
159157
} else if (value instanceof VocabularyEntry) {
160-
operationValue = this.prepareAuthorityValue(value);
158+
operationValue = new FormFieldMetadataValueObject(value.value, null, value.securityLevel, value.authority);
161159
} else if (value instanceof FormFieldLanguageValueObject) {
162160
operationValue = new FormFieldMetadataValueObject(value.value, value.language, securityLevel);
163161
} else if (value.hasOwnProperty('authority')) {
@@ -170,11 +168,10 @@ export class JsonPatchOperationsBuilder {
170168
Object.keys(value)
171169
.forEach((key) => {
172170
if (typeof value[key] === 'object') {
173-
if (securityLevel != null) {
174-
operationValue[key] = this.prepareObjectValue(value[key], securityLevel);
171+
if (isNotEmpty(securityLevel)) {
172+
operationValue[key] = this.prepareObjectValue(value[key], securityLevel);
175173
} else {
176-
operationValue[key] = this.prepareObjectValue(value[key]);
177-
174+
operationValue[key] = this.prepareObjectValue(value[key]);
178175
}
179176
} else {
180177
operationValue[key] = value[key];
@@ -184,14 +181,4 @@ export class JsonPatchOperationsBuilder {
184181
return operationValue;
185182
}
186183

187-
protected prepareAuthorityValue(value: any): FormFieldMetadataValueObject {
188-
let operationValue: FormFieldMetadataValueObject;
189-
if (isNotEmpty(value.authority)) {
190-
operationValue = new FormFieldMetadataValueObject(value.value, value.language, value.securityLevel, value.authority);
191-
} else {
192-
operationValue = new FormFieldMetadataValueObject(value.value, value.language, value.securityLevel,);
193-
}
194-
return operationValue;
195-
}
196-
197184
}

src/app/core/submission/vocabularies/models/vocabulary-entry-detail.model.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
import { Observable } from 'rxjs';
12
import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
23

34
import { HALLink } from '../../../shared/hal-link.model';
45
import { VOCABULARY_ENTRY_DETAIL } from './vocabularies.resource-type';
5-
import { typedObject } from '../../../cache/builders/build-decorators';
6+
import { link, typedObject } from '../../../cache/builders/build-decorators';
67
import { VocabularyEntry } from './vocabulary-entry.model';
8+
import { RemoteData } from '../../../data/remote-data';
9+
import { PaginatedList } from '../../../data/paginated-list.model';
710

811
/**
912
* Model class for a VocabularyEntryDetail
@@ -33,7 +36,21 @@ export class VocabularyEntryDetail extends VocabularyEntry {
3336
self: HALLink;
3437
vocabulary: HALLink;
3538
parent: HALLink;
36-
children
39+
children: HALLink;
3740
};
3841

42+
/**
43+
* The submitter for this SubmissionObject
44+
* Will be undefined unless the submitter {@link HALLink} has been resolved.
45+
*/
46+
@link(VOCABULARY_ENTRY_DETAIL)
47+
parent?: Observable<RemoteData<VocabularyEntryDetail>>;
48+
49+
/**
50+
* The submitter for this SubmissionObject
51+
* Will be undefined unless the submitter {@link HALLink} has been resolved.
52+
*/
53+
@link(VOCABULARY_ENTRY_DETAIL, true)
54+
children?: Observable<RemoteData<PaginatedList<VocabularyEntryDetail>>>;
55+
3956
}

src/app/core/submission/vocabularies/models/vocabulary-entry.model.ts

-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@ export class VocabularyEntry extends ListableObject {
5050
@autoserialize
5151
securityLevel: number;
5252

53-
54-
5553
/**
5654
* A string representing the kind of vocabulary entry
5755
*/

src/app/core/submission/vocabularies/vocabulary-entry-details.data.service.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,29 @@ export class VocabularyEntryDetailsDataService extends IdentifiableDataService<V
5454
* requested after the response becomes stale
5555
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
5656
* {@link HALLink}s should be automatically resolved
57-
* @return {Observable<RemoteData<PaginatedList<T>>>}
57+
* @return {Observable<RemoteData<PaginatedList<VocabularyEntryDetail>>>}
5858
* Return an observable that emits object list
5959
*/
6060
public findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<VocabularyEntryDetail>[]): Observable<RemoteData<PaginatedList<VocabularyEntryDetail>>> {
6161
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
6262
}
6363

64+
/**
65+
* Returns an observable of {@link RemoteData} of an object, based on its ID, with a list of
66+
* {@link FollowLinkConfig}, to automatically resolve {@link HALLink}s of the object
67+
* @param id ID of object we want to retrieve
68+
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
69+
* no valid cached version. Defaults to true
70+
* @param reRequestOnStale Whether or not the request should automatically be re-
71+
* requested after the response becomes stale
72+
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
73+
* {@link HALLink}s should be automatically resolved
74+
*/
75+
findById(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<VocabularyEntryDetail>[]): Observable<RemoteData<VocabularyEntryDetail>> {
76+
const href$ = this.getIDHrefObs(id, ...linksToFollow);
77+
return this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
78+
}
79+
6480
/**
6581
* Make a new FindListRequest with given search method
6682
*

src/app/core/submission/vocabularies/vocabulary.service.spec.ts

+37-3
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,33 @@ describe('VocabularyService', () => {
9292
type: 'vocabularyEntry'
9393
};
9494

95+
const entryDetailRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/${hierarchicalVocabulary.id}:testValue`;
96+
const entryDetailParentRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/${hierarchicalVocabulary.id}:testValue/parent`;
97+
const entryDetailChildrenRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/${hierarchicalVocabulary.id}:testValue/children`;
98+
99+
const vocabularyEntryDetail: any = {
100+
authority: 'authorityId',
101+
display: 'test',
102+
value: 'test',
103+
otherInformation: {
104+
id: 'authorityId',
105+
hasChildren: 'true',
106+
note: 'Familjeforskning'
107+
},
108+
type: 'vocabularyEntryDetail',
109+
_links: {
110+
self: {
111+
href: entryDetailRequestURL
112+
},
113+
parent: {
114+
href: entryDetailParentRequestURL
115+
},
116+
children: {
117+
href: entryDetailChildrenRequestURL
118+
}
119+
}
120+
};
121+
95122
const vocabularyEntryParentDetail: any = {
96123
authority: 'authorityId2',
97124
display: 'testParent',
@@ -164,9 +191,7 @@ describe('VocabularyService', () => {
164191
const endpointURL = `https://rest.api/rest/api/submission/vocabularies`;
165192
const requestURL = `https://rest.api/rest/api/submission/vocabularies/${vocabulary.id}`;
166193
const entryDetailEndpointURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails`;
167-
const entryDetailRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/${hierarchicalVocabulary.id}:testValue`;
168-
const entryDetailParentRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/${hierarchicalVocabulary.id}:testValue/parent`;
169-
const entryDetailChildrenRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/${hierarchicalVocabulary.id}:testValue/children`;
194+
170195
const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a';
171196
const vocabularyId = 'types';
172197
const metadata = 'dc.type';
@@ -193,6 +218,7 @@ describe('VocabularyService', () => {
193218
const vocabularyRD = createSuccessfulRemoteDataObject(vocabulary);
194219
const vocabularyRD$ = createSuccessfulRemoteDataObject$(vocabulary);
195220
const vocabularyEntriesRD = createSuccessfulRemoteDataObject$(paginatedListEntries);
221+
const vocabularyEntryDetailRD$ = createSuccessfulRemoteDataObject$(vocabularyEntryDetail);
196222
const vocabularyEntryDetailParentRD = createSuccessfulRemoteDataObject(vocabularyEntryParentDetail);
197223
const vocabularyEntryChildrenRD = createSuccessfulRemoteDataObject(childrenPaginatedList);
198224
const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList);
@@ -485,6 +511,10 @@ describe('VocabularyService', () => {
485511
});
486512

487513
describe('getEntryDetailParent', () => {
514+
beforeEach(() => {
515+
(service as any).vocabularyEntryDetailDataService.findById.and.returnValue(vocabularyEntryDetailRD$);
516+
});
517+
488518
it('should proxy the call to vocabularyDataService.getEntryDetailParent', () => {
489519
scheduler.schedule(() => service.getEntryDetailParent('testValue', hierarchicalVocabulary.id).subscribe());
490520
scheduler.flush();
@@ -502,6 +532,10 @@ describe('VocabularyService', () => {
502532
});
503533

504534
describe('getEntryDetailChildren', () => {
535+
beforeEach(() => {
536+
(service as any).vocabularyEntryDetailDataService.findById.and.returnValue(vocabularyEntryDetailRD$);
537+
});
538+
505539
it('should proxy the call to vocabularyDataService.getEntryDetailChildren', () => {
506540
const options: VocabularyFindOptions = new VocabularyFindOptions(
507541
null,

src/app/core/submission/vocabularies/vocabulary.service.ts

+37-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Injectable } from '@angular/core';
2-
import { Observable } from 'rxjs';
2+
import { Observable, of } from 'rxjs';
33
import { first, map, mergeMap, switchMap } from 'rxjs/operators';
44
import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
55
import { RequestService } from '../../data/request.service';
@@ -8,7 +8,11 @@ import { PaginatedList } from '../../data/paginated-list.model';
88
import { Vocabulary } from './models/vocabulary.model';
99
import { VocabularyEntry } from './models/vocabulary-entry.model';
1010
import { hasValue, isNotEmpty } from '../../../shared/empty.util';
11-
import { getFirstSucceededRemoteDataPayload, getFirstSucceededRemoteListPayload } from '../../shared/operators';
11+
import {
12+
getFirstCompletedRemoteData,
13+
getFirstSucceededRemoteDataPayload,
14+
getFirstSucceededRemoteListPayload
15+
} from '../../shared/operators';
1216
import { VocabularyFindOptions } from './models/vocabulary-find-options.model';
1317
import { VocabularyEntryDetail } from './models/vocabulary-entry-detail.model';
1418
import { RequestParam } from '../../cache/models/request-param.model';
@@ -17,6 +21,7 @@ import { PageInfo } from '../../shared/page-info.model';
1721
import { FindListOptions } from '../../data/find-list-options.model';
1822
import { VocabularyEntryDetailsDataService } from './vocabulary-entry-details.data.service';
1923
import { VocabularyDataService } from './vocabulary.data.service';
24+
import { createFailedRemoteDataObject } from '../../../shared/remote-data.utils';
2025

2126
/**
2227
* A service responsible for fetching/sending data from/to the REST API on the vocabularies endpoint
@@ -249,7 +254,7 @@ export class VocabularyService {
249254
searchVocabularyByMetadataAndCollection(vocabularyOptions: VocabularyOptions, ...linksToFollow: FollowLinkConfig<Vocabulary>[]): Observable<RemoteData<Vocabulary>> {
250255
const options: VocabularyFindOptions = new VocabularyFindOptions(vocabularyOptions.scope, vocabularyOptions.metadata);
251256

252-
return this.vocabularyDataService.getSearchByHref(this.searchByMetadataAndCollectionMethod, options).pipe(
257+
return this.vocabularyDataService.getSearchByHref(this.searchByMetadataAndCollectionMethod, options, ...linksToFollow).pipe(
253258
first((href: string) => hasValue(href)),
254259
mergeMap((href: string) => this.vocabularyDataService.findByHref(href))
255260
);
@@ -309,11 +314,20 @@ export class VocabularyService {
309314
* Return an observable that emits a PaginatedList of VocabularyEntryDetail
310315
*/
311316
getEntryDetailParent(value: string, name: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<VocabularyEntryDetail>[]): Observable<RemoteData<VocabularyEntryDetail>> {
312-
const linkPath = `${name}:${value}/parent`;
313-
314-
return this.vocabularyEntryDetailDataService.getBrowseEndpoint().pipe(
315-
map((href: string) => `${href}/${linkPath}`),
316-
mergeMap((href) => this.vocabularyEntryDetailDataService.findByHref(href, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow))
317+
return this.findEntryDetailById(value, name, useCachedVersionIfAvailable, reRequestOnStale, true, ...linksToFollow).pipe(
318+
getFirstCompletedRemoteData(),
319+
switchMap((entryRD: RemoteData<VocabularyEntryDetail>) => {
320+
if (entryRD.hasSucceeded) {
321+
return this.vocabularyEntryDetailDataService.findByHref(
322+
entryRD.payload._links.parent.href,
323+
useCachedVersionIfAvailable,
324+
reRequestOnStale,
325+
...linksToFollow
326+
);
327+
} else {
328+
return of(createFailedRemoteDataObject<VocabularyEntryDetail>(entryRD.errorMessage));
329+
}
330+
})
317331
);
318332
}
319333

@@ -344,9 +358,21 @@ export class VocabularyService {
344358
pageInfo.currentPage
345359
);
346360

347-
return this.vocabularyEntryDetailDataService.getBrowseEndpoint().pipe(
348-
map(href => `${href}/${name}:${value}/children`),
349-
switchMap(href => this.vocabularyEntryDetailDataService.findListByHref(href, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow))
361+
return this.findEntryDetailById(value, name, useCachedVersionIfAvailable, reRequestOnStale, true, ...linksToFollow).pipe(
362+
getFirstCompletedRemoteData(),
363+
switchMap((entryRD: RemoteData<VocabularyEntryDetail>) => {
364+
if (entryRD.hasSucceeded) {
365+
return this.vocabularyEntryDetailDataService.findListByHref(
366+
entryRD.payload._links.children.href,
367+
options,
368+
useCachedVersionIfAvailable,
369+
reRequestOnStale,
370+
...linksToFollow
371+
);
372+
} else {
373+
return of(createFailedRemoteDataObject<PaginatedList<VocabularyEntryDetail>>(entryRD.errorMessage));
374+
}
375+
})
350376
);
351377
}
352378

src/app/item-page/mirador-viewer/mirador-viewer.component.ts

+3
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ export class MiradorViewerComponent implements OnInit {
8787
if (this.notMobile) {
8888
viewerPath += '&notMobile=true';
8989
}
90+
if (environment.mirador.enableDownloadPlugin) {
91+
viewerPath += '&enableDownloadPlugin=true';
92+
}
9093

9194
// TODO: Should the query term be trusted here?
9295
return this.sanitizer.bypassSecurityTrustResourceUrl(viewerPath);

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

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { TestBed } from '@angular/core/testing';
22
import { LuckySearchService } from './lucky-search.service';
3-
import {SearchService} from '../core/shared/search/search.service';
4-
import {SearchServiceStub} from '../shared/testing/search-service.stub';
3+
import { SearchManager } from '../core/browse/search-manager';
4+
import { SearchServiceStub } from '../shared/testing/search-service.stub';
5+
56
describe('LuckySearchService', () => {
67
let service: LuckySearchService;
78
beforeEach(() => {
89
TestBed.configureTestingModule({
910
providers: [
10-
{provide: SearchService, useValue: new SearchServiceStub('/search')},
11+
{provide: SearchManager, useValue: new SearchServiceStub('/search')},
1112
],
1213
});
1314
service = TestBed.inject(LuckySearchService);

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import { PaginatedList } from '../core/data/paginated-list.model';
55
import { SearchResult } from '../shared/search/models/search-result.model';
66
import { DSpaceObject } from '../core/shared/dspace-object.model';
77
import { PaginatedSearchOptions } from '../shared/search/models/paginated-search-options.model';
8-
import { SearchService } from '../core/shared/search/search.service';
8+
import { SearchManager } from '../core/browse/search-manager';
99

1010
@Injectable({
1111
providedIn: 'root'
1212
})
1313
export class LuckySearchService {
1414

15-
constructor(private searchService: SearchService) {
15+
constructor(private searchService: SearchManager) {
1616
}
1717

1818
/**

0 commit comments

Comments
 (0)