Skip to content

Commit

Permalink
Merge branch 'dspace-cris-2023_02_x' into main-cris
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/config/default-app-config.ts
#	src/environments/environment.test.ts
  • Loading branch information
atarix83 committed Feb 28, 2024
2 parents 00aa481 + ff3d4c8 commit 021eb43
Show file tree
Hide file tree
Showing 22 changed files with 317 additions and 185 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
"build:stats": "ng build --stats-json",
"build:ci": "ng config cli.cache.environment ci && yarn run build:ssr",
"build:prod": "cross-env NODE_ENV=production yarn run build:ssr",
"build:ssr": "npm run ng-high-memory -- build --configuration production && ng run dspace-angular:server:production",
"ng-high-memory": "node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng",
"build:ssr": "npm run ng-high-memory -- build --configuration production && npm run ng-high-memory -- run dspace-angular:server:production",
"ng-high-memory": "node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng",
"test": "npm run ng-high-memory -- test --source-map=true --watch=false --configuration test",
"test:watch": "nodemon --exec \"npm run ng-high-memory -- test --source-map=true --watch=true --configuration test\"",
"test:headless": "npm run ng-high-memory -- test --source-map=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage",
Expand Down
37 changes: 12 additions & 25 deletions src/app/core/json-patch/builder/json-patch-operations-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,27 +110,25 @@ export class JsonPatchOperationsBuilder {
operationValue = [];
value.forEach((entry) => {
if ((typeof entry === 'object')) {
if (securityLevel != null) {
if (isNotEmpty(securityLevel)) {
operationValue.push(this.prepareObjectValue(entry, securityLevel));
} else {
operationValue.push(this.prepareObjectValue(entry));
}

} else {
operationValue.push(new FormFieldMetadataValueObject(entry, null, securityLevel));
}
});
} else if (typeof value === 'object') {
if (securityLevel != null) {
if (isNotEmpty(securityLevel)) {
operationValue = this.prepareObjectValue(value, securityLevel);
} else {
operationValue = this.prepareObjectValue(value);
}

} else {
// add the possibility to add security level when value is string
// in this case security level is set on metadata value
if (securityLevel != null) {
if (isNotEmpty(securityLevel)) {
operationValue = new FormFieldMetadataValueObject(value, null, securityLevel);
} else {
operationValue = new FormFieldMetadataValueObject(value, null);
Expand All @@ -143,21 +141,21 @@ export class JsonPatchOperationsBuilder {
}

protected prepareObjectValue(value: any, securityLevel = null) {
let operationValue = Object.create({});
let operationValue = Object.create({});
if (isEmpty(value) || value instanceof FormFieldMetadataValueObject) {
if (securityLevel != null) {
operationValue = {...value, securityLevel: securityLevel};
} else {
if (isNotEmpty(securityLevel)) {
operationValue = { ...value, securityLevel: securityLevel };
} else {
operationValue = value;
}
} else if (value instanceof Date) {
if (securityLevel != null) {
operationValue = new FormFieldMetadataValueObject(dateToISOFormat(value), null, securityLevel);
operationValue = new FormFieldMetadataValueObject(dateToISOFormat(value), null, securityLevel);
} else {
operationValue = new FormFieldMetadataValueObject(dateToISOFormat(value));
}
} else if (value instanceof VocabularyEntry) {
operationValue = this.prepareAuthorityValue(value);
operationValue = new FormFieldMetadataValueObject(value.value, null, value.securityLevel, value.authority);
} else if (value instanceof FormFieldLanguageValueObject) {
operationValue = new FormFieldMetadataValueObject(value.value, value.language, securityLevel);
} else if (value.hasOwnProperty('authority')) {
Expand All @@ -170,11 +168,10 @@ export class JsonPatchOperationsBuilder {
Object.keys(value)
.forEach((key) => {
if (typeof value[key] === 'object') {
if (securityLevel != null) {
operationValue[key] = this.prepareObjectValue(value[key], securityLevel);
if (isNotEmpty(securityLevel)) {
operationValue[key] = this.prepareObjectValue(value[key], securityLevel);
} else {
operationValue[key] = this.prepareObjectValue(value[key]);

operationValue[key] = this.prepareObjectValue(value[key]);
}
} else {
operationValue[key] = value[key];
Expand All @@ -184,14 +181,4 @@ export class JsonPatchOperationsBuilder {
return operationValue;
}

protected prepareAuthorityValue(value: any): FormFieldMetadataValueObject {
let operationValue: FormFieldMetadataValueObject;
if (isNotEmpty(value.authority)) {
operationValue = new FormFieldMetadataValueObject(value.value, value.language, value.securityLevel, value.authority);
} else {
operationValue = new FormFieldMetadataValueObject(value.value, value.language, value.securityLevel,);
}
return operationValue;
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Observable } from 'rxjs';
import { autoserialize, deserialize, inheritSerialization } from 'cerialize';

import { HALLink } from '../../../shared/hal-link.model';
import { VOCABULARY_ENTRY_DETAIL } from './vocabularies.resource-type';
import { typedObject } from '../../../cache/builders/build-decorators';
import { link, typedObject } from '../../../cache/builders/build-decorators';
import { VocabularyEntry } from './vocabulary-entry.model';
import { RemoteData } from '../../../data/remote-data';
import { PaginatedList } from '../../../data/paginated-list.model';

/**
* Model class for a VocabularyEntryDetail
Expand Down Expand Up @@ -33,7 +36,21 @@ export class VocabularyEntryDetail extends VocabularyEntry {
self: HALLink;
vocabulary: HALLink;
parent: HALLink;
children
children: HALLink;
};

/**
* The submitter for this SubmissionObject
* Will be undefined unless the submitter {@link HALLink} has been resolved.
*/
@link(VOCABULARY_ENTRY_DETAIL)
parent?: Observable<RemoteData<VocabularyEntryDetail>>;

/**
* The submitter for this SubmissionObject
* Will be undefined unless the submitter {@link HALLink} has been resolved.
*/
@link(VOCABULARY_ENTRY_DETAIL, true)
children?: Observable<RemoteData<PaginatedList<VocabularyEntryDetail>>>;

}
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ export class VocabularyEntry extends ListableObject {
@autoserialize
securityLevel: number;



/**
* A string representing the kind of vocabulary entry
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,29 @@ export class VocabularyEntryDetailsDataService extends IdentifiableDataService<V
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
* @return {Observable<RemoteData<PaginatedList<T>>>}
* @return {Observable<RemoteData<PaginatedList<VocabularyEntryDetail>>>}
* Return an observable that emits object list
*/
public findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<VocabularyEntryDetail>[]): Observable<RemoteData<PaginatedList<VocabularyEntryDetail>>> {
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}

/**
* Returns an observable of {@link RemoteData} of an object, based on its ID, with a list of
* {@link FollowLinkConfig}, to automatically resolve {@link HALLink}s of the object
* @param id ID of object we want to retrieve
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findById(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<VocabularyEntryDetail>[]): Observable<RemoteData<VocabularyEntryDetail>> {
const href$ = this.getIDHrefObs(id, ...linksToFollow);
return this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}

/**
* Make a new FindListRequest with given search method
*
Expand Down
40 changes: 37 additions & 3 deletions src/app/core/submission/vocabularies/vocabulary.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,33 @@ describe('VocabularyService', () => {
type: 'vocabularyEntry'
};

const entryDetailRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/${hierarchicalVocabulary.id}:testValue`;
const entryDetailParentRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/${hierarchicalVocabulary.id}:testValue/parent`;
const entryDetailChildrenRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/${hierarchicalVocabulary.id}:testValue/children`;

const vocabularyEntryDetail: any = {
authority: 'authorityId',
display: 'test',
value: 'test',
otherInformation: {
id: 'authorityId',
hasChildren: 'true',
note: 'Familjeforskning'
},
type: 'vocabularyEntryDetail',
_links: {
self: {
href: entryDetailRequestURL
},
parent: {
href: entryDetailParentRequestURL
},
children: {
href: entryDetailChildrenRequestURL
}
}
};

const vocabularyEntryParentDetail: any = {
authority: 'authorityId2',
display: 'testParent',
Expand Down Expand Up @@ -164,9 +191,7 @@ describe('VocabularyService', () => {
const endpointURL = `https://rest.api/rest/api/submission/vocabularies`;
const requestURL = `https://rest.api/rest/api/submission/vocabularies/${vocabulary.id}`;
const entryDetailEndpointURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails`;
const entryDetailRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/${hierarchicalVocabulary.id}:testValue`;
const entryDetailParentRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/${hierarchicalVocabulary.id}:testValue/parent`;
const entryDetailChildrenRequestURL = `https://rest.api/rest/api/submission/vocabularyEntryDetails/${hierarchicalVocabulary.id}:testValue/children`;

const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a';
const vocabularyId = 'types';
const metadata = 'dc.type';
Expand All @@ -193,6 +218,7 @@ describe('VocabularyService', () => {
const vocabularyRD = createSuccessfulRemoteDataObject(vocabulary);
const vocabularyRD$ = createSuccessfulRemoteDataObject$(vocabulary);
const vocabularyEntriesRD = createSuccessfulRemoteDataObject$(paginatedListEntries);
const vocabularyEntryDetailRD$ = createSuccessfulRemoteDataObject$(vocabularyEntryDetail);
const vocabularyEntryDetailParentRD = createSuccessfulRemoteDataObject(vocabularyEntryParentDetail);
const vocabularyEntryChildrenRD = createSuccessfulRemoteDataObject(childrenPaginatedList);
const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList);
Expand Down Expand Up @@ -485,6 +511,10 @@ describe('VocabularyService', () => {
});

describe('getEntryDetailParent', () => {
beforeEach(() => {
(service as any).vocabularyEntryDetailDataService.findById.and.returnValue(vocabularyEntryDetailRD$);
});

it('should proxy the call to vocabularyDataService.getEntryDetailParent', () => {
scheduler.schedule(() => service.getEntryDetailParent('testValue', hierarchicalVocabulary.id).subscribe());
scheduler.flush();
Expand All @@ -502,6 +532,10 @@ describe('VocabularyService', () => {
});

describe('getEntryDetailChildren', () => {
beforeEach(() => {
(service as any).vocabularyEntryDetailDataService.findById.and.returnValue(vocabularyEntryDetailRD$);
});

it('should proxy the call to vocabularyDataService.getEntryDetailChildren', () => {
const options: VocabularyFindOptions = new VocabularyFindOptions(
null,
Expand Down
48 changes: 37 additions & 11 deletions src/app/core/submission/vocabularies/vocabulary.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Observable, of } from 'rxjs';
import { first, map, mergeMap, switchMap } from 'rxjs/operators';
import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
import { RequestService } from '../../data/request.service';
Expand All @@ -8,7 +8,11 @@ import { PaginatedList } from '../../data/paginated-list.model';
import { Vocabulary } from './models/vocabulary.model';
import { VocabularyEntry } from './models/vocabulary-entry.model';
import { hasValue, isNotEmpty } from '../../../shared/empty.util';
import { getFirstSucceededRemoteDataPayload, getFirstSucceededRemoteListPayload } from '../../shared/operators';
import {
getFirstCompletedRemoteData,
getFirstSucceededRemoteDataPayload,
getFirstSucceededRemoteListPayload
} from '../../shared/operators';
import { VocabularyFindOptions } from './models/vocabulary-find-options.model';
import { VocabularyEntryDetail } from './models/vocabulary-entry-detail.model';
import { RequestParam } from '../../cache/models/request-param.model';
Expand All @@ -17,6 +21,7 @@ import { PageInfo } from '../../shared/page-info.model';
import { FindListOptions } from '../../data/find-list-options.model';
import { VocabularyEntryDetailsDataService } from './vocabulary-entry-details.data.service';
import { VocabularyDataService } from './vocabulary.data.service';
import { createFailedRemoteDataObject } from '../../../shared/remote-data.utils';

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

return this.vocabularyDataService.getSearchByHref(this.searchByMetadataAndCollectionMethod, options).pipe(
return this.vocabularyDataService.getSearchByHref(this.searchByMetadataAndCollectionMethod, options, ...linksToFollow).pipe(
first((href: string) => hasValue(href)),
mergeMap((href: string) => this.vocabularyDataService.findByHref(href))
);
Expand Down Expand Up @@ -309,11 +314,20 @@ export class VocabularyService {
* Return an observable that emits a PaginatedList of VocabularyEntryDetail
*/
getEntryDetailParent(value: string, name: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<VocabularyEntryDetail>[]): Observable<RemoteData<VocabularyEntryDetail>> {
const linkPath = `${name}:${value}/parent`;

return this.vocabularyEntryDetailDataService.getBrowseEndpoint().pipe(
map((href: string) => `${href}/${linkPath}`),
mergeMap((href) => this.vocabularyEntryDetailDataService.findByHref(href, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow))
return this.findEntryDetailById(value, name, useCachedVersionIfAvailable, reRequestOnStale, true, ...linksToFollow).pipe(
getFirstCompletedRemoteData(),
switchMap((entryRD: RemoteData<VocabularyEntryDetail>) => {
if (entryRD.hasSucceeded) {
return this.vocabularyEntryDetailDataService.findByHref(
entryRD.payload._links.parent.href,
useCachedVersionIfAvailable,
reRequestOnStale,
...linksToFollow
);
} else {
return of(createFailedRemoteDataObject<VocabularyEntryDetail>(entryRD.errorMessage));
}
})
);
}

Expand Down Expand Up @@ -344,9 +358,21 @@ export class VocabularyService {
pageInfo.currentPage
);

return this.vocabularyEntryDetailDataService.getBrowseEndpoint().pipe(
map(href => `${href}/${name}:${value}/children`),
switchMap(href => this.vocabularyEntryDetailDataService.findListByHref(href, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow))
return this.findEntryDetailById(value, name, useCachedVersionIfAvailable, reRequestOnStale, true, ...linksToFollow).pipe(
getFirstCompletedRemoteData(),
switchMap((entryRD: RemoteData<VocabularyEntryDetail>) => {
if (entryRD.hasSucceeded) {
return this.vocabularyEntryDetailDataService.findListByHref(
entryRD.payload._links.children.href,
options,
useCachedVersionIfAvailable,
reRequestOnStale,
...linksToFollow
);
} else {
return of(createFailedRemoteDataObject<PaginatedList<VocabularyEntryDetail>>(entryRD.errorMessage));
}
})
);
}

Expand Down
3 changes: 3 additions & 0 deletions src/app/item-page/mirador-viewer/mirador-viewer.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ export class MiradorViewerComponent implements OnInit {
if (this.notMobile) {
viewerPath += '&notMobile=true';
}
if (environment.mirador.enableDownloadPlugin) {
viewerPath += '&enableDownloadPlugin=true';
}

// TODO: Should the query term be trusted here?
return this.sanitizer.bypassSecurityTrustResourceUrl(viewerPath);
Expand Down
7 changes: 4 additions & 3 deletions src/app/lucky-search/lucky-search.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { TestBed } from '@angular/core/testing';
import { LuckySearchService } from './lucky-search.service';
import {SearchService} from '../core/shared/search/search.service';
import {SearchServiceStub} from '../shared/testing/search-service.stub';
import { SearchManager } from '../core/browse/search-manager';
import { SearchServiceStub } from '../shared/testing/search-service.stub';

describe('LuckySearchService', () => {
let service: LuckySearchService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{provide: SearchService, useValue: new SearchServiceStub('/search')},
{provide: SearchManager, useValue: new SearchServiceStub('/search')},
],
});
service = TestBed.inject(LuckySearchService);
Expand Down
4 changes: 2 additions & 2 deletions src/app/lucky-search/lucky-search.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { PaginatedList } from '../core/data/paginated-list.model';
import { SearchResult } from '../shared/search/models/search-result.model';
import { DSpaceObject } from '../core/shared/dspace-object.model';
import { PaginatedSearchOptions } from '../shared/search/models/paginated-search-options.model';
import { SearchService } from '../core/shared/search/search.service';
import { SearchManager } from '../core/browse/search-manager';

@Injectable({
providedIn: 'root'
})
export class LuckySearchService {

constructor(private searchService: SearchService) {
constructor(private searchService: SearchManager) {
}

/**
Expand Down
Loading

0 comments on commit 021eb43

Please sign in to comment.