From 5bc676f785a8c33c27e075eade9e478e4624ea7e Mon Sep 17 00:00:00 2001 From: PBobylev Date: Wed, 4 Sep 2024 19:46:49 +0500 Subject: [PATCH] MSEARCH-829: code fixes and test coverage --- NEWS.md | 3 +- README.md | 5 +- descriptors/ModuleDescriptor-template.json | 17 + .../ResourceEventBatchInterceptor.java | 1 + .../folio/search/service/IndexService.java | 13 +- .../LinkedDataAuthorityLccnProcessor.java | 8 +- ...inkedDataInstanceContributorProcessor.java | 17 +- .../LinkedDataInstanceNoteProcessor.java | 17 +- .../LinkedDataInstanceSortTitleProcessor.java | 17 +- .../LinkedDataInstanceTitleProcessor.java | 17 +- .../LinkedDataWorkContributorProcessor.java | 12 +- .../work/LinkedDataWorkNoteProcessor.java | 8 +- .../LinkedDataWorkSortTitleProcessor.java | 12 +- .../work/LinkedDataWorkTitleProcessor.java | 8 +- .../org/folio/search/utils/SearchUtils.java | 1 + src/main/resources/application.yml | 2 +- .../resources/model/linked_data_instance.json | 104 +++-- .../resources/model/linked_data_work.json | 43 +- .../common/linkedDataInstanceOnly.yaml | 5 +- .../common/linkedDataWorkOnly.yaml | 12 +- .../dto/linked-data/linkedDataAuthority.yaml | 2 +- .../dto/linked-data/linkedDataInstance.yaml | 6 +- .../dto/linked-data/linkedDataWork.yaml | 2 +- .../schemas/request/reindexRequest.yaml | 1 + .../SearchLinkedDataAuthorityIT.java | 10 +- .../SearchLinkedDataInstanceIT.java | 273 ++++++++++++ .../controller/SearchLinkedDataWorkIT.java | 396 ++++++++++-------- .../integration/KafkaMessageListenerTest.java | 35 ++ .../folio/search/sample/SampleLinkedData.java | 28 +- .../search/support/base/ApiEndpoints.java | 6 +- .../base/BaseConsortiumIntegrationTest.java | 6 +- .../support/base/BaseIntegrationTest.java | 76 ++-- .../search/utils/LinkedDataTestUtils.java | 143 +++++++ .../org/folio/search/utils/TestConstants.java | 9 +- src/test/resources/application.yml | 5 +- .../samples/linked-data/instance.json | 146 +++++++ .../samples/linked-data/instance2.json | 40 ++ .../resources/samples/linked-data/work.json | 101 +++-- .../resources/samples/linked-data/work2.json | 20 +- 39 files changed, 1264 insertions(+), 363 deletions(-) create mode 100644 src/test/java/org/folio/search/controller/SearchLinkedDataInstanceIT.java create mode 100644 src/test/java/org/folio/search/utils/LinkedDataTestUtils.java create mode 100644 src/test/resources/samples/linked-data/instance.json create mode 100644 src/test/resources/samples/linked-data/instance2.json diff --git a/NEWS.md b/NEWS.md index 238a095f6..770994da3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,7 +9,7 @@ * Provides `consortium-search v1.2` ### Features -* Implement indices recreation of linked-data-work and linked-data-authority ([MSEARCH-820](https://issues.folio.org/browse/MSEARCH-820)) +* Implement indices recreation of the Linked Data resources ([MSEARCH-820](https://issues.folio.org/browse/MSEARCH-820)) * Extension of mod-search consortium items/holdings API ([MSEARCH-788](https://issues.folio.org/browse/MSEARCH-788)) * Create location index and process location events ([MSEARCH-703](https://issues.folio.org/browse/MSEARCH-703)) * Implement reindexing of locations ([MSEARCH-702](https://issues.folio.org/browse/MSEARCH-702)) @@ -22,6 +22,7 @@ * Search consolidated items/holdings data in consortium ([MSEARCH-759](https://folio-org.atlassian.net/browse/MSEARCH-759)) * Create linked data work index and process linked data work events ([MSEARCH-781](https://folio-org.atlassian.net/browse/MSEARCH-781)) * Create linked data authority index and process linked data authority events ([MSEARCH-784](https://folio-org.atlassian.net/browse/MSEARCH-784)) +* Create linked data instance index and process linked data instance events ([MSEARCH-829](https://folio-org.atlassian.net/browse/MSEARCH-829)) * Allow Unified List of Inventory Locations in a Consortium to be fetched by member tenants ([MSEARCH-660](https://folio-org.atlassian.net/browse/MSEARCH-660)) * Implement Indexing of Campuses from Kafka ([MSEARCH-770](https://issues.folio.org/browse/MSEARCH-770)) * Extend response with additional Location fields for Inventory Locations in a Consortium endpoint ([MSEARCH-775](https://folio-org.atlassian.net/browse/MSEARCH-775)) diff --git a/README.md b/README.md index d83b76896..94774f585 100644 --- a/README.md +++ b/README.md @@ -335,11 +335,11 @@ x-okapi-token: [JWT_TOKEN] ``` * `resourceName` parameter is optional and equal to `instance` by default. Possible values: `instance`, `authority`, `locations`, - `linked-data-work`, `linked-data-authority`. Please note that `locations` reindex is synchronous. + `linked-data-instance`, `linked-data-work`, `linked-data-authority`. Please note that `locations` reindex is synchronous. * `recreateIndex` parameter is optional and equal to `false` by default. If it is equal to `true` then mod-search will drop existing indices for tenant and resource, creating them again. Executing request with this parameter equal to `true` in query will erase all the tenant data in mod-search. -* Please note that for `linked-data-work` and `linked-data-authority` resources the endpoint is used only for index recreation +* Please note that for `linked-data-instance`, `linked-data-work` and `linked-data-authority` resources the endpoint is used only for index recreation purpose and actual reindex operation is triggered through mod-linked-data. ### Monitoring reindex process @@ -421,6 +421,7 @@ Consortium feature on module enable is defined by 'centralTenantId' tenant param |:-------|:----------------------------------|:-------------------------------------------------------------------------------------| | GET | `/search/instances` | Search by instances and to this instance items and holding-records | | GET | `/search/authorities` | Search by authority records | +| GET | `/search/linked-data/instances` | Search linked data graph instance resource descriptions | | GET | `/search/linked-data/works` | Search linked data graph work resource descriptions | | GET | `/search/linked-data/authorities` | Search linked data graph authority resource descriptions | | GET | `/search/{recordType}/facets` | Get facets where recordType could be: instances, authorities, contributors, subjects | diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 056a18312..55a8678fc 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -105,6 +105,18 @@ "user-tenants.collection.get" ] }, + { + "methods": [ + "GET" + ], + "pathPattern": "/search/linked-data/instances", + "permissionsRequired": [ + "search.linked-data.instance.collection.get" + ], + "modulePermissions": [ + "user-tenants.collection.get" + ] + }, { "methods": [ "GET" @@ -639,6 +651,11 @@ "displayName": "Search - searches authorities by given query", "description": "Searches authorities by given query" }, + { + "permissionName": "search.linked-data.instance.collection.get", + "displayName": "Search - searches linked data instances by given query", + "description": "Searches linked data instances by given query" + }, { "permissionName": "search.linked-data.work.collection.get", "displayName": "Search - searches linked data works by given query", diff --git a/src/main/java/org/folio/search/integration/interceptor/ResourceEventBatchInterceptor.java b/src/main/java/org/folio/search/integration/interceptor/ResourceEventBatchInterceptor.java index e9f376ff4..f9b6c5db0 100644 --- a/src/main/java/org/folio/search/integration/interceptor/ResourceEventBatchInterceptor.java +++ b/src/main/java/org/folio/search/integration/interceptor/ResourceEventBatchInterceptor.java @@ -26,6 +26,7 @@ public class ResourceEventBatchInterceptor implements BatchInterceptor getResourceNamesToReindex(ReindexRequest reindexRequest) { var resourceDescription = resourceDescriptionService.find(resourceName); if (resourceDescription.isEmpty() || resourceDescription.get().getParent() != null - || !resourceDescription.get().isReindexSupported()) { + || ! resourceDescription.get().isReindexSupported()) { throw new RequestValidationException( "Reindex request contains invalid resource name", RESOURCE_NAME_PARAMETER, resourceName); } @@ -271,7 +272,7 @@ private void updateNumberOfReplicas(ObjectNode settings, Integer numberOfReplica private void updateRefreshInterval(ObjectNode settings, Integer refreshInt) { if (refreshInt != null && refreshInt != 0) { - settings.put("refresh_interval", refreshInt == -1 ? "-1" : refreshInt + "s"); + settings.put("refresh_interval", refreshInt == - 1 ? "-1" : refreshInt + "s"); } } @@ -294,6 +295,8 @@ private static String normalizeResourceName(String url) { } private boolean isLinkedDataResource(String resource) { - return LINKED_DATA_WORK_RESOURCE.equals(resource) || LINKED_DATA_AUTHORITY_RESOURCE.equals(resource); + return LINKED_DATA_INSTANCE_RESOURCE.equals(resource) + || LINKED_DATA_WORK_RESOURCE.equals(resource) + || LINKED_DATA_AUTHORITY_RESOURCE.equals(resource); } } diff --git a/src/main/java/org/folio/search/service/setter/linkeddata/authority/LinkedDataAuthorityLccnProcessor.java b/src/main/java/org/folio/search/service/setter/linkeddata/authority/LinkedDataAuthorityLccnProcessor.java index 90efa2e77..337ee017a 100644 --- a/src/main/java/org/folio/search/service/setter/linkeddata/authority/LinkedDataAuthorityLccnProcessor.java +++ b/src/main/java/org/folio/search/service/setter/linkeddata/authority/LinkedDataAuthorityLccnProcessor.java @@ -1,18 +1,22 @@ package org.folio.search.service.setter.linkeddata.authority; import java.util.Set; -import lombok.RequiredArgsConstructor; import org.folio.search.domain.dto.LinkedDataAuthority; +import org.folio.search.service.lccn.LccnNormalizer; import org.folio.search.service.setter.FieldProcessor; import org.folio.search.service.setter.linkeddata.common.LinkedDataLccnProcessor; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component -@RequiredArgsConstructor public class LinkedDataAuthorityLccnProcessor implements FieldProcessor> { private final LinkedDataLccnProcessor linkedDataLccnProcessor; + public LinkedDataAuthorityLccnProcessor(@Autowired LccnNormalizer lccnNormalizer) { + this.linkedDataLccnProcessor = new LinkedDataLccnProcessor(lccnNormalizer); + } + @Override public Set getFieldValue(LinkedDataAuthority linkedDataAuthority) { return linkedDataLccnProcessor.getFieldValue(linkedDataAuthority.getIdentifiers()); diff --git a/src/main/java/org/folio/search/service/setter/linkeddata/instance/LinkedDataInstanceContributorProcessor.java b/src/main/java/org/folio/search/service/setter/linkeddata/instance/LinkedDataInstanceContributorProcessor.java index 452f2a1a4..5e338a188 100644 --- a/src/main/java/org/folio/search/service/setter/linkeddata/instance/LinkedDataInstanceContributorProcessor.java +++ b/src/main/java/org/folio/search/service/setter/linkeddata/instance/LinkedDataInstanceContributorProcessor.java @@ -1,8 +1,14 @@ package org.folio.search.service.setter.linkeddata.instance; +import static java.util.Optional.ofNullable; + +import java.util.Collection; +import java.util.Objects; import java.util.Set; +import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import org.folio.search.domain.dto.LinkedDataInstance; +import org.folio.search.domain.dto.LinkedDataWorkOnly; import org.folio.search.service.setter.FieldProcessor; import org.folio.search.service.setter.linkeddata.common.LinkedDataContributorProcessor; import org.springframework.stereotype.Component; @@ -15,7 +21,16 @@ public class LinkedDataInstanceContributorProcessor implements FieldProcessor
  • getFieldValue(LinkedDataInstance linkedDataInstance) { - return linkedDataContributorProcessor.getFieldValue(linkedDataInstance.getContributors()); + var instanceContributors = ofNullable(linkedDataInstance.getContributors()) + .stream() + .flatMap(Collection::stream) + .filter(Objects::nonNull); + var workContributors = ofNullable(linkedDataInstance.getParentWork()) + .map(LinkedDataWorkOnly::getContributors) + .stream() + .flatMap(Collection::stream); + var contributors = Stream.concat(instanceContributors, workContributors).toList(); + return linkedDataContributorProcessor.getFieldValue(contributors); } } diff --git a/src/main/java/org/folio/search/service/setter/linkeddata/instance/LinkedDataInstanceNoteProcessor.java b/src/main/java/org/folio/search/service/setter/linkeddata/instance/LinkedDataInstanceNoteProcessor.java index d2d46efe5..a82328fa5 100644 --- a/src/main/java/org/folio/search/service/setter/linkeddata/instance/LinkedDataInstanceNoteProcessor.java +++ b/src/main/java/org/folio/search/service/setter/linkeddata/instance/LinkedDataInstanceNoteProcessor.java @@ -1,8 +1,14 @@ package org.folio.search.service.setter.linkeddata.instance; +import static java.util.Optional.ofNullable; + +import java.util.Collection; +import java.util.Objects; import java.util.Set; +import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import org.folio.search.domain.dto.LinkedDataInstance; +import org.folio.search.domain.dto.LinkedDataWorkOnly; import org.folio.search.service.setter.FieldProcessor; import org.folio.search.service.setter.linkeddata.common.LinkedDataNoteProcessor; import org.springframework.stereotype.Component; @@ -15,7 +21,16 @@ public class LinkedDataInstanceNoteProcessor implements FieldProcessor getFieldValue(LinkedDataInstance linkedDataInstance) { - return linkedDataNoteProcessor.getFieldValue(linkedDataInstance.getNotes()); + var instanceNotes = ofNullable(linkedDataInstance.getNotes()) + .stream() + .flatMap(Collection::stream) + .filter(Objects::nonNull); + var workNotes = ofNullable(linkedDataInstance.getParentWork()) + .map(LinkedDataWorkOnly::getNotes) + .stream() + .flatMap(Collection::stream); + var contributors = Stream.concat(instanceNotes, workNotes).toList(); + return linkedDataNoteProcessor.getFieldValue(contributors); } } diff --git a/src/main/java/org/folio/search/service/setter/linkeddata/instance/LinkedDataInstanceSortTitleProcessor.java b/src/main/java/org/folio/search/service/setter/linkeddata/instance/LinkedDataInstanceSortTitleProcessor.java index f2fcef990..472dbc2ae 100644 --- a/src/main/java/org/folio/search/service/setter/linkeddata/instance/LinkedDataInstanceSortTitleProcessor.java +++ b/src/main/java/org/folio/search/service/setter/linkeddata/instance/LinkedDataInstanceSortTitleProcessor.java @@ -1,7 +1,13 @@ package org.folio.search.service.setter.linkeddata.instance; +import static java.util.Optional.ofNullable; + +import java.util.Collection; +import java.util.Objects; +import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import org.folio.search.domain.dto.LinkedDataInstance; +import org.folio.search.domain.dto.LinkedDataWorkOnly; import org.folio.search.service.setter.FieldProcessor; import org.folio.search.service.setter.linkeddata.common.LinkedDataSortTitleProcessor; import org.springframework.stereotype.Component; @@ -14,7 +20,16 @@ public class LinkedDataInstanceSortTitleProcessor implements FieldProcessor getFieldValue(LinkedDataInstance linkedDataInstance) { - return linkedDataTitleProcessor.getFieldValue(linkedDataInstance.getTitles()); + var instanceTitles = ofNullable(linkedDataInstance.getTitles()) + .stream() + .flatMap(Collection::stream) + .filter(Objects::nonNull); + var workTitles = ofNullable(linkedDataInstance.getParentWork()) + .map(LinkedDataWorkOnly::getTitles) + .stream() + .flatMap(Collection::stream); + var titles = Stream.concat(instanceTitles, workTitles).toList(); + return linkedDataTitleProcessor.getFieldValue(titles); } } diff --git a/src/main/java/org/folio/search/service/setter/linkeddata/work/LinkedDataWorkContributorProcessor.java b/src/main/java/org/folio/search/service/setter/linkeddata/work/LinkedDataWorkContributorProcessor.java index 73165e57b..19b5040c2 100644 --- a/src/main/java/org/folio/search/service/setter/linkeddata/work/LinkedDataWorkContributorProcessor.java +++ b/src/main/java/org/folio/search/service/setter/linkeddata/work/LinkedDataWorkContributorProcessor.java @@ -21,9 +21,15 @@ public class LinkedDataWorkContributorProcessor implements FieldProcessor getFieldValue(LinkedDataWork linkedDataWork) { - var workContributors = ofNullable(linkedDataWork.getContributors()).stream().flatMap(Collection::stream); - var instanceContributors = ofNullable(linkedDataWork.getInstances()).stream().flatMap(Collection::stream) - .map(LinkedDataInstanceOnly::getContributors).filter(Objects::nonNull).flatMap(Collection::stream); + var workContributors = ofNullable(linkedDataWork.getContributors()) + .stream() + .flatMap(Collection::stream); + var instanceContributors = ofNullable(linkedDataWork.getInstances()) + .stream() + .flatMap(Collection::stream) + .map(LinkedDataInstanceOnly::getContributors) + .filter(Objects::nonNull) + .flatMap(Collection::stream); var contributors = Stream.concat(workContributors, instanceContributors).toList(); return linkedDataContributorProcessor.getFieldValue(contributors); } diff --git a/src/main/java/org/folio/search/service/setter/linkeddata/work/LinkedDataWorkNoteProcessor.java b/src/main/java/org/folio/search/service/setter/linkeddata/work/LinkedDataWorkNoteProcessor.java index da209ea32..3ff5dccc1 100644 --- a/src/main/java/org/folio/search/service/setter/linkeddata/work/LinkedDataWorkNoteProcessor.java +++ b/src/main/java/org/folio/search/service/setter/linkeddata/work/LinkedDataWorkNoteProcessor.java @@ -21,12 +21,14 @@ public class LinkedDataWorkNoteProcessor implements FieldProcessor getFieldValue(LinkedDataWork linkedDataWork) { - var workNotes = ofNullable(linkedDataWork.getNotes()).stream().flatMap(Collection::stream); - var instNotes = ofNullable(linkedDataWork.getInstances()).stream().flatMap(Collection::stream) + var workNotes = ofNullable(linkedDataWork.getNotes()) + .stream() + .flatMap(Collection::stream); + var instanceNotes = ofNullable(linkedDataWork.getInstances()).stream().flatMap(Collection::stream) .filter(Objects::nonNull) .map(LinkedDataInstanceOnly::getNotes) .flatMap(Collection::stream); - var notes = Stream.concat(workNotes, instNotes).toList(); + var notes = Stream.concat(workNotes, instanceNotes).toList(); return linkedDataNoteProcessor.getFieldValue(notes); } diff --git a/src/main/java/org/folio/search/service/setter/linkeddata/work/LinkedDataWorkSortTitleProcessor.java b/src/main/java/org/folio/search/service/setter/linkeddata/work/LinkedDataWorkSortTitleProcessor.java index 2f79c1d94..388f16931 100644 --- a/src/main/java/org/folio/search/service/setter/linkeddata/work/LinkedDataWorkSortTitleProcessor.java +++ b/src/main/java/org/folio/search/service/setter/linkeddata/work/LinkedDataWorkSortTitleProcessor.java @@ -1,12 +1,6 @@ package org.folio.search.service.setter.linkeddata.work; -import static java.util.Optional.ofNullable; - -import java.util.Collection; -import java.util.Objects; -import java.util.stream.Stream; import lombok.RequiredArgsConstructor; -import org.folio.search.domain.dto.LinkedDataInstanceOnly; import org.folio.search.domain.dto.LinkedDataWork; import org.folio.search.service.setter.FieldProcessor; import org.folio.search.service.setter.linkeddata.common.LinkedDataSortTitleProcessor; @@ -20,11 +14,7 @@ public class LinkedDataWorkSortTitleProcessor implements FieldProcessor getFieldValue(LinkedDataWork linkedDataWork) { - var workTitles = ofNullable(linkedDataWork.getTitles()).stream().flatMap(Collection::stream); - var instTitles = ofNullable(linkedDataWork.getInstances()).stream().flatMap(Collection::stream) + var workTitles = ofNullable(linkedDataWork.getTitles()) + .stream() + .flatMap(Collection::stream); + var instTitles = ofNullable(linkedDataWork.getInstances()) + .stream() + .flatMap(Collection::stream) .filter(Objects::nonNull) .map(LinkedDataInstanceOnly::getTitles) .flatMap(Collection::stream); diff --git a/src/main/java/org/folio/search/utils/SearchUtils.java b/src/main/java/org/folio/search/utils/SearchUtils.java index cd6faa19f..1196e5ee5 100644 --- a/src/main/java/org/folio/search/utils/SearchUtils.java +++ b/src/main/java/org/folio/search/utils/SearchUtils.java @@ -38,6 +38,7 @@ public class SearchUtils { public static final String CONTRIBUTOR_RESOURCE = getResourceName(Contributor.class); public static final String LOCATION_RESOURCE = "location"; public static final String CLASSIFICATION_TYPE_RESOURCE = "classification-type"; + public static final String LINKED_DATA_INSTANCE_RESOURCE = "linked-data-instance"; public static final String LINKED_DATA_WORK_RESOURCE = "linked-data-work"; public static final String LINKED_DATA_AUTHORITY_RESOURCE = "linked-data-authority"; public static final String CAMPUS_RESOURCE = "campus"; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3a2cdf70d..e2550ad60 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -139,7 +139,7 @@ folio: group-id: ${folio.environment}-mod-search-location-type-group linked-data: concurrency: ${KAFKA_LINKED_DATA_CONCURRENCY:1} - topic-pattern: (${folio.environment}\.)(.*\.)linked-data\.(work|authority) + topic-pattern: (${folio.environment}\.)(.*\.)linked-data\.(instance|work|authority) group-id: ${folio.environment}-mod-search-linked-data-group okapiUrl: ${okapi.url} logging: diff --git a/src/main/resources/model/linked_data_instance.json b/src/main/resources/model/linked_data_instance.json index 7caeb82e5..e2be9f8d3 100644 --- a/src/main/resources/model/linked_data_instance.json +++ b/src/main/resources/model/linked_data_instance.json @@ -5,7 +5,8 @@ "languageSourcePaths": [ "$.languages" ], "fields": { "id": { - "index": "keyword" + "index": "keyword", + "searchAliases": [ "keyword" ] }, "classifications": { "type": "object", @@ -33,29 +34,17 @@ } }, "editionStatements": { - "type": "object", - "properties": { - "value": { - "index": "whitespace" - } - } + "index": "whitespace" }, "format": { - "index": "keyword_lowercase" - }, - "hubAAPs": { - "searchAliases": [ "hub, hubAap, hubAAP" ], - "index": "keyword_lowercase" - }, - "languages": { - "searchAliases": [ "lang, language" ], - "index": "keyword_lowercase" + "index": "whitespace" }, "identifiers": { "type": "object", "properties": { "value": { - "index": "whitespace" + "index": "whitespace", + "searchAliases": [ "keyword" ] }, "type": { "index": "whitespace" @@ -81,13 +70,10 @@ }, "date": { "searchAliases": [ "publicationDate" ], - "index": "keyword" + "index": "date" } } }, - "subjects": { - "index": "whitespace" - }, "suppress": { "type": "object", "properties": { @@ -113,13 +99,81 @@ "index": "whitespace" } } + }, + "parentWork": { + "type": "object", + "properties": { + "id": { + "index": "keyword", + "searchAliases": [ "keyword" ] + }, + "classifications": { + "type": "object", + "properties": { + "number": { + "index": "whitespace" + }, + "source": { + "index": "whitespace" + } + } + }, + "contributors": { + "type": "object", + "properties": { + "name": { + "index": "whitespace" + }, + "type": { + "index": "whitespace" + }, + "isCreator": { + "index": "whitespace" + } + } + }, + "hubAAPs": { + "searchAliases": [ "hub", "keyword" ], + "index": "multilang" + }, + "languages": { + "searchAliases": [ "lang", "language" ], + "index": "keyword_lowercase" + }, + "notes": { + "type": "object", + "properties": { + "value": { + "index": "whitespace" + }, + "type": { + "index": "whitespace" + } + } + }, + "subjects": { + "index": "whitespace" + }, + "titles": { + "type": "object", + "properties": { + "value": { + "index": "whitespace" + }, + "type": { + "index": "whitespace" + } + } + } + } } }, "searchFields": { "contributor": { "type": "search", "index": "multilang", - "processor": "linkedDataInstanceContributorProcessor" + "processor": "linkedDataInstanceContributorProcessor", + "searchAliases": [ "keyword" ] }, "isbn": { "type": "search", @@ -136,12 +190,14 @@ "note": { "type": "search", "index": "multilang", - "processor": "linkedDataInstanceNoteProcessor" + "processor": "linkedDataInstanceNoteProcessor", + "searchAliases": [ "keyword" ] }, "title": { "type": "search", "index": "multilang", - "processor": "linkedDataInstanceTitleProcessor" + "processor": "linkedDataInstanceTitleProcessor", + "searchAliases": [ "keyword" ] }, "sort_title": { "searchTypes": "sort", diff --git a/src/main/resources/model/linked_data_work.json b/src/main/resources/model/linked_data_work.json index 91ecac271..0625303a6 100644 --- a/src/main/resources/model/linked_data_work.json +++ b/src/main/resources/model/linked_data_work.json @@ -5,7 +5,8 @@ "languageSourcePaths": [ "$.languages" ], "fields": { "id": { - "index": "whitespace" + "index": "whitespace", + "searchAliases": [ "keyword" ] }, "classifications": { "type": "object", @@ -22,7 +23,8 @@ "type": "object", "properties": { "name": { - "index": "whitespace" + "index": "whitespace", + "searchAliases": [ "workContributor" ] }, "type": { "index": "whitespace" @@ -33,18 +35,19 @@ } }, "hubAAPs": { - "searchAliases": [ "hub, hubAap, hubAAP" ], - "index": "keyword_lowercase" + "searchAliases": [ "hub", "keyword" ], + "index": "multilang" }, "languages": { - "searchAliases": [ "lang, language" ], + "searchAliases": [ "lang", "language" ], "index": "keyword_lowercase" }, "notes": { "type": "object", "properties": { "value": { - "index": "whitespace" + "index": "whitespace", + "searchAliases": [ "workNote" ] }, "type": { "index": "whitespace" @@ -58,7 +61,8 @@ "type": "object", "properties": { "value": { - "index": "whitespace" + "index": "whitespace", + "searchAliases": [ "workTitle" ] }, "type": { "index": "whitespace" @@ -69,7 +73,8 @@ "type": "object", "properties": { "id": { - "index": "keyword" + "index": "keyword", + "searchAliases": [ "keyword" ] }, "contributors": { "type": "object", @@ -86,21 +91,18 @@ } }, "editionStatements": { - "type": "object", - "properties": { - "value": { - "index": "whitespace" - } - } + "index": "whitespace" }, "format": { - "index": "keyword_lowercase" + "index": "whitespace", + "searchAliases": [ "format" ] }, "identifiers": { "type": "object", "properties": { "value": { - "index": "whitespace" + "index": "whitespace", + "searchAliases": [ "keyword" ] }, "type": { "index": "whitespace" @@ -163,7 +165,8 @@ "contributor": { "type": "search", "index": "multilang", - "processor": "linkedDataWorkContributorProcessor" + "processor": "linkedDataWorkContributorProcessor", + "searchAliases": [ "keyword" ] }, "isbn": { "type": "search", @@ -180,12 +183,14 @@ "note": { "type": "search", "index": "multilang", - "processor": "linkedDataWorkNoteProcessor" + "processor": "linkedDataWorkNoteProcessor", + "searchAliases": [ "keyword" ] }, "title": { "type": "search", "index": "multilang", - "processor": "linkedDataWorkTitleProcessor" + "processor": "linkedDataWorkTitleProcessor", + "searchAliases": [ "keyword" ] }, "sort_title": { "searchTypes": "sort", diff --git a/src/main/resources/swagger.api/schemas/dto/linked-data/common/linkedDataInstanceOnly.yaml b/src/main/resources/swagger.api/schemas/dto/linked-data/common/linkedDataInstanceOnly.yaml index 36d1c6dc4..3a274ad8b 100644 --- a/src/main/resources/swagger.api/schemas/dto/linked-data/common/linkedDataInstanceOnly.yaml +++ b/src/main/resources/swagger.api/schemas/dto/linked-data/common/linkedDataInstanceOnly.yaml @@ -13,10 +13,7 @@ properties: type: "array" description: "Edition statement array" items: - properties: - value: - type: "string" - description: "Edition statement value" + type: "string" format: type: string description: "Format of an Instance e.g. physical monographs vs. ebooks" diff --git a/src/main/resources/swagger.api/schemas/dto/linked-data/common/linkedDataWorkOnly.yaml b/src/main/resources/swagger.api/schemas/dto/linked-data/common/linkedDataWorkOnly.yaml index e09761d26..df26ec407 100644 --- a/src/main/resources/swagger.api/schemas/dto/linked-data/common/linkedDataWorkOnly.yaml +++ b/src/main/resources/swagger.api/schemas/dto/linked-data/common/linkedDataWorkOnly.yaml @@ -30,10 +30,7 @@ properties: type: "array" description: "Language array" items: - properties: - value: - type: "string" - description: "Language value" + type: "string" notes: type: "array" description: "Notes array" @@ -41,12 +38,9 @@ properties: $ref: "linkedDataNote.yaml" subjects: type: "array" - description: "Subject array" + description: "Subject label array" items: - properties: - value: - type: "string" - description: "Subject label" + type: "string" titles: type: "array" description: "Title array" diff --git a/src/main/resources/swagger.api/schemas/dto/linked-data/linkedDataAuthority.yaml b/src/main/resources/swagger.api/schemas/dto/linked-data/linkedDataAuthority.yaml index d75e30737..eb7327ad6 100644 --- a/src/main/resources/swagger.api/schemas/dto/linked-data/linkedDataAuthority.yaml +++ b/src/main/resources/swagger.api/schemas/dto/linked-data/linkedDataAuthority.yaml @@ -1,4 +1,4 @@ -description: "Linked Data Authority dto, contains Authority and Identifiers" +description: "Linked Data Authority search dto, contains Authority and Identifiers" type: "object" properties: id: diff --git a/src/main/resources/swagger.api/schemas/dto/linked-data/linkedDataInstance.yaml b/src/main/resources/swagger.api/schemas/dto/linked-data/linkedDataInstance.yaml index 0c2ff6d7c..8c6eadb1b 100644 --- a/src/main/resources/swagger.api/schemas/dto/linked-data/linkedDataInstance.yaml +++ b/src/main/resources/swagger.api/schemas/dto/linked-data/linkedDataInstance.yaml @@ -1,5 +1,7 @@ -description: "Linked Data Instance dto, contains Instance and it's Work's fields" +description: "Linked Data Instance search dto, contains Instance and it's Work's fields" type: "object" allOf: - $ref: "common/linkedDataInstanceOnly.yaml" - - $ref: "common/linkedDataWorkOnly.yaml" +properties: + parentWork: + $ref: "common/linkedDataWorkOnly.yaml" diff --git a/src/main/resources/swagger.api/schemas/dto/linked-data/linkedDataWork.yaml b/src/main/resources/swagger.api/schemas/dto/linked-data/linkedDataWork.yaml index 866b469ed..7130d1938 100644 --- a/src/main/resources/swagger.api/schemas/dto/linked-data/linkedDataWork.yaml +++ b/src/main/resources/swagger.api/schemas/dto/linked-data/linkedDataWork.yaml @@ -1,4 +1,4 @@ -description: "Linked Data Work dto, contains Work and linked Instances" +description: "Linked Data Work search dto, contains Work and linked Instances" type: "object" allOf: - $ref: "common/linkedDataWorkOnly.yaml" diff --git a/src/main/resources/swagger.api/schemas/request/reindexRequest.yaml b/src/main/resources/swagger.api/schemas/request/reindexRequest.yaml index 61f1c989b..384eb1d46 100644 --- a/src/main/resources/swagger.api/schemas/request/reindexRequest.yaml +++ b/src/main/resources/swagger.api/schemas/request/reindexRequest.yaml @@ -13,6 +13,7 @@ properties: - instance - authority - location + - linked-data-instance - linked-data-work - linked-data-authority indexSettings: diff --git a/src/test/java/org/folio/search/controller/SearchLinkedDataAuthorityIT.java b/src/test/java/org/folio/search/controller/SearchLinkedDataAuthorityIT.java index 60fde3291..2d48b3244 100644 --- a/src/test/java/org/folio/search/controller/SearchLinkedDataAuthorityIT.java +++ b/src/test/java/org/folio/search/controller/SearchLinkedDataAuthorityIT.java @@ -1,10 +1,12 @@ package org.folio.search.controller; +import static org.folio.search.sample.SampleLinkedData.getAuthorityConceptSampleAsMap; +import static org.folio.search.sample.SampleLinkedData.getAuthorityPersonSampleAsMap; +import static org.folio.search.utils.LinkedDataTestUtils.toTotalRecords; import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import org.folio.search.domain.dto.LinkedDataAuthority; -import org.folio.search.sample.SampleLinkedData; import org.folio.search.support.base.BaseIntegrationTest; import org.folio.spring.testing.type.IntegrationTest; import org.junit.jupiter.api.AfterAll; @@ -18,9 +20,7 @@ class SearchLinkedDataAuthorityIT extends BaseIntegrationTest { @BeforeAll static void prepare() { - setUpTenant(LinkedDataAuthority.class, 2, - SampleLinkedData.getAuthorityConceptSampleAsMap(), SampleLinkedData.getAuthorityPersonSampleAsMap() - ); + setUpTenant(LinkedDataAuthority.class, getAuthorityConceptSampleAsMap(), getAuthorityPersonSampleAsMap()); } @AfterAll @@ -53,6 +53,6 @@ static void cleanUp() { }) void searchByLinkedDataAuthority_parameterized_singleResult(int index, int size, String query) throws Throwable { doSearchByLinkedDataAuthority(query) - .andExpect(jsonPath("$.totalRecords", is(size))); + .andExpect(jsonPath(toTotalRecords(), is(size))); } } diff --git a/src/test/java/org/folio/search/controller/SearchLinkedDataInstanceIT.java b/src/test/java/org/folio/search/controller/SearchLinkedDataInstanceIT.java new file mode 100644 index 000000000..5c3a0b4af --- /dev/null +++ b/src/test/java/org/folio/search/controller/SearchLinkedDataInstanceIT.java @@ -0,0 +1,273 @@ +package org.folio.search.controller; + +import static org.folio.search.sample.SampleLinkedData.getInstance2SampleAsMap; +import static org.folio.search.sample.SampleLinkedData.getInstanceSampleAsMap; +import static org.folio.search.utils.LinkedDataTestUtils.toClassificationNumber; +import static org.folio.search.utils.LinkedDataTestUtils.toClassificationSource; +import static org.folio.search.utils.LinkedDataTestUtils.toContributorIsCreator; +import static org.folio.search.utils.LinkedDataTestUtils.toContributorName; +import static org.folio.search.utils.LinkedDataTestUtils.toContributorType; +import static org.folio.search.utils.LinkedDataTestUtils.toEditionStatement; +import static org.folio.search.utils.LinkedDataTestUtils.toFormat; +import static org.folio.search.utils.LinkedDataTestUtils.toHubAap; +import static org.folio.search.utils.LinkedDataTestUtils.toId; +import static org.folio.search.utils.LinkedDataTestUtils.toIdType; +import static org.folio.search.utils.LinkedDataTestUtils.toIdValue; +import static org.folio.search.utils.LinkedDataTestUtils.toLanguage; +import static org.folio.search.utils.LinkedDataTestUtils.toNoteType; +import static org.folio.search.utils.LinkedDataTestUtils.toNoteValue; +import static org.folio.search.utils.LinkedDataTestUtils.toParentWork; +import static org.folio.search.utils.LinkedDataTestUtils.toPublicationDate; +import static org.folio.search.utils.LinkedDataTestUtils.toPublicationName; +import static org.folio.search.utils.LinkedDataTestUtils.toRootContent; +import static org.folio.search.utils.LinkedDataTestUtils.toSubject; +import static org.folio.search.utils.LinkedDataTestUtils.toSuppressFromDiscovery; +import static org.folio.search.utils.LinkedDataTestUtils.toSuppressStaff; +import static org.folio.search.utils.LinkedDataTestUtils.toTitleType; +import static org.folio.search.utils.LinkedDataTestUtils.toTitleValue; +import static org.folio.search.utils.LinkedDataTestUtils.toTotalRecords; +import static org.hamcrest.Matchers.is; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; + +import org.folio.search.domain.dto.LinkedDataInstance; +import org.folio.search.support.base.BaseIntegrationTest; +import org.folio.spring.testing.type.IntegrationTest; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +@IntegrationTest +class SearchLinkedDataInstanceIT extends BaseIntegrationTest { + + @BeforeAll + static void prepare() { + setUpTenant(LinkedDataInstance.class, getInstanceSampleAsMap(), getInstance2SampleAsMap()); + } + + @AfterAll + static void cleanUp() { + removeTenant(); + } + + @DisplayName("search by linked data instances (all 2 instances are found)") + @ParameterizedTest(name = "[{0}] {1}") + @CsvSource({ + "1, cql.allRecords = 1", + "2, title all \"titleAbc\"", + "3, title any \"titleAbc\"", + "4, title any \"titleAbc def\"", + "5, title any \"titleAbc XXX\"", + "6, title = \"titleAbc\"", + "7, title <> \"titleXXX\"", + "8, title = \"title*\"", + "9, title = \"*\"", + "10, isbn <> \"1234\"", + "11, lccn <> \"2023\"", + "12, contributor all \"common\"", + "13, contributor any \"common\"", + "14, contributor = \"common\"", + "15, contributor <> \"commonXXX\"", + "16, contributor = \"com*\"", + "17, contributor = \"*\"", + "18, (title all \"titleAbc\") sortBy title", + "19, title all \"titleAbc\" sortBy title", + "20, title all \"titleAbc\" sortBy title/sort.ascending", + "21, title all \"titleAbc\" sortBy title/sort.descending", + }) + void searchByLinkedDataInstance_parameterized_allResults(int index, String query) throws Throwable { + var asc = !query.contains("descending"); + doSearchByLinkedDataInstance(query) + .andExpect(jsonPath(toTotalRecords(), is(2))) + .andExpect(jsonPath(toTitleValue(toRootContent(0), 0), is(asc ? "titleAbc def" : "titleAbc xyz"))) + .andExpect(jsonPath(toTitleValue(toRootContent(1), 0), is(asc ? "titleAbc xyz" : "titleAbc def"))); + } + + @DisplayName("search by linked data instance (single instance is found)") + @ParameterizedTest(name = "[{0}] {1}") + @CsvSource({ + "1, keyword = Instance1_Family", + "2, keyword = titleAbc def", + "3, keyword = 1234567890123", + "4, keyword = hubAAP1", + "5, keyword = first instance note", + "6, title any \"def\"", + "7, title = \"titleAbc def\"", + "8, title == \"titleAbc def\"", + "9, title ==/string \"titleAbc def\"", + "10, isbn = \"*\"", + "11, isbn = \"1234567890123\"", + "12, isbn = \"1234*\"", + "13, isbn == \"1234567890123\"", + "14, isbn ==/string \"1234567890123\"", + "15, isbn any \"1234567890123\"", + "16, isbn any \"1234567890123 XXX\"", + "17, isbn all \"1234567890123\"", + "18, lccn = \"*\"", + "19, lccn = \"2023202345\"", + "20, lccn = \"2023*\"", + "21, lccn == \"2023202345\"", + "22, lccn ==/string \"2023202345\"", + "23, lccn any \"2023202345\"", + "24, lccn any \"2023202345 XXX\"", + "25, lccn all \"2023202345\"", + "26, contributor = Family", + "27, contributor == Meeting", + "28, contributor ==/string Organization", + "29, contributor any Person", + "30, contributor all Family", + "31, hub = *", + "32, hub = hubA*", + "33, hub = hubAAP1", + "34, hub == hubAAP2", + "35, hub ==/string hubAAP1", + "36, hub any \"hubAAP1 hubAAP2 XXX\"", + "37, hub all hubAAP1", + "38, note = *", + "39, note = first*", + "40, note = first instance note", + "41, note == first instance note", + "42, note ==/string first instance note", + "43, note any \"first instance note XXX\"", + "44, note all \"first instance note\"", + "45, lang = *", + "46, lang = ru*", + "47, lang = eng", + "48, lang == rus", + "49, lang ==/string eng", + "50, lang == (\"rus\" or \"eng\" or \"XXX\")", + "51, lang all rus", + "52, format = *", + "53, format = Mono*", + "54, format = monograph", + "55, format == monograph", + "56, format ==/string Monograph", + "57, format any \"Monograph XXX\"", + "58, format all Monograph", + "59, suppressFromDiscovery = false", + "60, suppressFromDiscovery == false", + "61, staffSuppress = true", + "62, staffSuppress == true", + "63, publicationDate = 2023", + "64, publicationDate == 2024", + "65, publicationDate ==/string 2023", + "66, publicationDate == (\"2023\" or \"2024\" or \"2020\")", + "67, publicationDate all 2024" + }) + void searchByLinkedDataInstance_parameterized_singleResult(int index, String query) throws Throwable { + doSearchByLinkedDataInstance(query) + .andExpect(jsonPath(toTotalRecords(), is(1))) + .andExpect(jsonPath(toId(toRootContent()), is("instance1"))) + .andExpect(jsonPath(toClassificationNumber(toParentWork(), 0), is("1234"))) + .andExpect(jsonPath(toClassificationSource(toParentWork(), 0), is("ddc"))) + .andExpect(jsonPath(toClassificationNumber(toParentWork(), 1), is("5678"))) + .andExpect(jsonPath(toClassificationSource(toParentWork(), 1), is("other"))) + .andExpect(jsonPath(toContributorName(toRootContent(), 0), is("Instance1_Family"))) + .andExpect(jsonPath(toContributorType(toRootContent(), 0), is("Family"))) + .andExpect(jsonPath(toContributorIsCreator(toRootContent(), 0), is(true))) + .andExpect(jsonPath(toContributorName(toRootContent(), 1), is("Instance1_Meeting"))) + .andExpect(jsonPath(toContributorType(toRootContent(), 1), is("Meeting"))) + .andExpect(jsonPath(toContributorIsCreator(toRootContent(), 1), is(false))) + .andExpect(jsonPath(toContributorName(toRootContent(), 2), is("Instance1_Organization"))) + .andExpect(jsonPath(toContributorType(toRootContent(), 2), is("Organization"))) + .andExpect(jsonPath(toContributorIsCreator(toRootContent(), 2), is(true))) + .andExpect(jsonPath(toContributorName(toRootContent(), 3), is("Instance1_Person"))) + .andExpect(jsonPath(toContributorType(toRootContent(), 3), is("Person"))) + .andExpect(jsonPath(toContributorIsCreator(toRootContent(), 3), is(false))) + .andExpect(jsonPath(toContributorName(toParentWork(), 4), is("common"))) + .andExpect(jsonPath(toContributorType(toParentWork(), 4), is("Family"))) + .andExpect(jsonPath(toContributorIsCreator(toParentWork(), 4), is(true))) + .andExpect(jsonPath(toContributorName(toParentWork(), 0), is("Family"))) + .andExpect(jsonPath(toContributorType(toParentWork(), 0), is("Family"))) + .andExpect(jsonPath(toContributorIsCreator(toParentWork(), 0), is(true))) + .andExpect(jsonPath(toContributorName(toParentWork(), 1), is("Meeting"))) + .andExpect(jsonPath(toContributorType(toParentWork(), 1), is("Meeting"))) + .andExpect(jsonPath(toContributorIsCreator(toParentWork(), 1), is(false))) + .andExpect(jsonPath(toContributorName(toParentWork(), 2), is("Organization"))) + .andExpect(jsonPath(toContributorType(toParentWork(), 2), is("Organization"))) + .andExpect(jsonPath(toContributorIsCreator(toParentWork(), 2), is(true))) + .andExpect(jsonPath(toContributorName(toParentWork(), 3), is("Person"))) + .andExpect(jsonPath(toContributorType(toParentWork(), 3), is("Person"))) + .andExpect(jsonPath(toContributorIsCreator(toParentWork(), 3), is(false))) + .andExpect(jsonPath(toContributorName(toParentWork(), 4), is("common"))) + .andExpect(jsonPath(toContributorType(toParentWork(), 4), is("Family"))) + .andExpect(jsonPath(toContributorIsCreator(toParentWork(), 4), is(true))) + .andExpect(jsonPath(toEditionStatement(toRootContent(), 0), is("1st edition"))) + .andExpect(jsonPath(toEditionStatement(toRootContent(), 1), is("2nd edition"))) + .andExpect(jsonPath(toFormat(toRootContent()), is("Monograph"))) + .andExpect(jsonPath(toHubAap(toParentWork(), 0), is("hubAAP1"))) + .andExpect(jsonPath(toHubAap(toParentWork(), 1), is("hubAAP2"))) + .andExpect(jsonPath(toIdValue(toRootContent(), 0), is("1234567890123"))) + .andExpect(jsonPath(toIdType(toRootContent(), 0), is("ISBN"))) + .andExpect(jsonPath(toIdValue(toRootContent(), 1), is(" 2023-202345/AC/r932"))) + .andExpect(jsonPath(toIdType(toRootContent(), 1), is("LCCN"))) + .andExpect(jsonPath(toLanguage(toParentWork(), 0), is("eng"))) + .andExpect(jsonPath(toLanguage(toParentWork(), 1), is("rus"))) + .andExpect(jsonPath(toNoteValue(toRootContent(), 0), is("first instance note"))) + .andExpect(jsonPath(toNoteType(toRootContent(), 0), is("firstInstanceNoteType"))) + .andExpect(jsonPath(toNoteValue(toRootContent(), 1), is("second instance note"))) + .andExpect(jsonPath(toNoteType(toRootContent(), 1), is("secondInstanceNoteType"))) + .andExpect(jsonPath(toNoteValue(toParentWork(), 0), is("first work note"))) + .andExpect(jsonPath(toNoteType(toParentWork(), 0), is("firstWorkNoteType"))) + .andExpect(jsonPath(toNoteValue(toParentWork(), 1), is("second work note"))) + .andExpect(jsonPath(toNoteType(toParentWork(), 1), is("secondWorkNoteType"))) + .andExpect(jsonPath(toPublicationName(toRootContent(), 0), is("publisher"))) + .andExpect(jsonPath(toPublicationDate(toRootContent(), 0), is("2023"))) + .andExpect(jsonPath(toPublicationName(toRootContent(), 1), is("publisher2"))) + .andExpect(jsonPath(toPublicationDate(toRootContent(), 1), is("2024"))) + .andExpect(jsonPath(toSubject(toParentWork(), 0), is("Subject 1"))) + .andExpect(jsonPath(toSubject(toParentWork(), 1), is("Subject 2"))) + .andExpect(jsonPath(toSuppressFromDiscovery(toRootContent()), is(false))) + .andExpect(jsonPath(toSuppressStaff(toRootContent()), is(true))) + .andExpect(jsonPath(toTitleValue(toRootContent(), 0), is("titleAbc def"))) + .andExpect(jsonPath(toTitleType(toRootContent(), 0), is("Main"))) + .andExpect(jsonPath(toTitleValue(toRootContent(), 1), is("sub"))) + .andExpect(jsonPath(toTitleType(toRootContent(), 1), is("Sub"))) + .andExpect(jsonPath(toTitleValue(toParentWork(), 0), is("WorkTitle"))) + .andExpect(jsonPath(toTitleType(toParentWork(), 0), is("Main"))) + .andExpect(jsonPath(toTitleValue(toParentWork(), 1), is("sub"))) + .andExpect(jsonPath(toTitleType(toParentWork(), 1), is("Sub"))) + ; + } + + @DisplayName("search by liked data instance (nothing is found)") + @ParameterizedTest(name = "[{0}] {1}") + @CsvSource({ + "1, title ==/string \"titleAbc\"", + "2, title ==/string \"def\"", + "3, title ==/string \"xyz\"", + "4, title == \"def titleAbc\"", + "5, title == \"titleAbcdef def\"", + "6, title all \"titleAbcdef\"", + "7, title any \"titleAbcdef\"", + "8, title = \"titleAbcdef\"", + "9, title <> \"titleAbc\"", + "10, isbn ==/string \"1234\"", + "11, isbn == \"1234\"", + "12, isbn any \"1234\"", + "13, isbn any \"12345678901231\"", + "14, isbn all \"1234\"", + "15, isbn = \"1234\"", + "16, lccn ==/string \"2023\"", + "17, lccn == \"2023\"", + "18, lccn any \"2023\"", + "19, lccn any \"202320231\"", + "20, lccn all \"2023\"", + "21, lccn = \"2023\"", + "22, contributor ==/string \"Famil\"", + "23, contributor ==/string \"Meeting1\"", + "24, contributor ==/string \"rganizatio\"", + "25, contributor == \"Person common\"", + "26, contributor == \"common Person\"", + "27, contributor all \"comm\"", + "28, contributor any \"comm\"", + "29, contributor = \"comm\"", + "30, contributor <> \"common\"", + }) + void searchByLinkedDataInstance_parameterized_zeroResults(int index, String query) throws Throwable { + doSearchByLinkedDataInstance(query) + .andExpect(jsonPath(toTotalRecords(), is(0))); + } + +} diff --git a/src/test/java/org/folio/search/controller/SearchLinkedDataWorkIT.java b/src/test/java/org/folio/search/controller/SearchLinkedDataWorkIT.java index 878176fc2..9f01963df 100644 --- a/src/test/java/org/folio/search/controller/SearchLinkedDataWorkIT.java +++ b/src/test/java/org/folio/search/controller/SearchLinkedDataWorkIT.java @@ -1,9 +1,31 @@ package org.folio.search.controller; -import static java.lang.String.format; -import static java.lang.String.join; import static org.folio.search.sample.SampleLinkedData.getWork2SampleAsMap; import static org.folio.search.sample.SampleLinkedData.getWorkSampleAsMap; +import static org.folio.search.utils.LinkedDataTestUtils.toClassificationNumber; +import static org.folio.search.utils.LinkedDataTestUtils.toClassificationSource; +import static org.folio.search.utils.LinkedDataTestUtils.toContributorIsCreator; +import static org.folio.search.utils.LinkedDataTestUtils.toContributorName; +import static org.folio.search.utils.LinkedDataTestUtils.toContributorType; +import static org.folio.search.utils.LinkedDataTestUtils.toEditionStatement; +import static org.folio.search.utils.LinkedDataTestUtils.toFormat; +import static org.folio.search.utils.LinkedDataTestUtils.toHubAap; +import static org.folio.search.utils.LinkedDataTestUtils.toId; +import static org.folio.search.utils.LinkedDataTestUtils.toIdType; +import static org.folio.search.utils.LinkedDataTestUtils.toIdValue; +import static org.folio.search.utils.LinkedDataTestUtils.toInstance; +import static org.folio.search.utils.LinkedDataTestUtils.toLanguage; +import static org.folio.search.utils.LinkedDataTestUtils.toNoteType; +import static org.folio.search.utils.LinkedDataTestUtils.toNoteValue; +import static org.folio.search.utils.LinkedDataTestUtils.toPublicationDate; +import static org.folio.search.utils.LinkedDataTestUtils.toPublicationName; +import static org.folio.search.utils.LinkedDataTestUtils.toRootContent; +import static org.folio.search.utils.LinkedDataTestUtils.toSubject; +import static org.folio.search.utils.LinkedDataTestUtils.toSuppressFromDiscovery; +import static org.folio.search.utils.LinkedDataTestUtils.toSuppressStaff; +import static org.folio.search.utils.LinkedDataTestUtils.toTitleType; +import static org.folio.search.utils.LinkedDataTestUtils.toTitleValue; +import static org.folio.search.utils.LinkedDataTestUtils.toTotalRecords; import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -21,7 +43,7 @@ class SearchLinkedDataWorkIT extends BaseIntegrationTest { @BeforeAll static void prepare() { - setUpTenant(LinkedDataWork.class, 2, getWorkSampleAsMap(), getWork2SampleAsMap()); + setUpTenant(LinkedDataWork.class, getWorkSampleAsMap(), getWork2SampleAsMap()); } @AfterAll @@ -57,80 +79,120 @@ static void cleanUp() { void searchByLinkedDataWork_parameterized_allResults(int index, String query) throws Throwable { var asc = query.contains("titleAbc def") || query.contains("sortBy") && !query.contains("descending"); doSearchByLinkedDataWork(query) - .andExpect(jsonPath("$.totalRecords", is(2))) - .andExpect(jsonPath("$.content[0].titles[0].value", is(asc ? "titleAbc def" : "titleAbc xyz"))) - .andExpect(jsonPath("$.content[1].titles[0].value", is(asc ? "titleAbc xyz" : "titleAbc def"))); + .andExpect(jsonPath(toTotalRecords(), is(2))) + .andExpect(jsonPath(toTitleValue(toRootContent(), 0), is(asc ? "titleAbc def" : "titleAbc xyz"))) + .andExpect(jsonPath(toTitleValue(toRootContent(1), 0), is(asc ? "titleAbc xyz" : "titleAbc def"))); } @DisplayName("search by linked data work (single work is found)") @ParameterizedTest(name = "[{0}] {1}") @CsvSource({ - "1, title any \"def\"", - "2, title = \"titleAbc def\"", - "3, title == \"titleAbc def\"", - "4, title ==/string \"titleAbc def\"", - "5, isbn = \"*\"", - "6, isbn = \"1234567890123\"", - "7, isbn = \"1234*\"", - "8, isbn == \"1234567890123\"", - "9, isbn ==/string \"1234567890123\"", - "10, isbn any \"1234567890123\"", - "11, isbn any \"1234567890123 XXX\"", - "12, isbn all \"1234567890123\"", - "13, lccn = \"*\"", - "14, lccn = \"2023202345\"", - "15, lccn = \"2023*\"", - "16, lccn == \"2023202345\"", - "17, lccn ==/string \"2023202345\"", - "18, lccn any \"2023202345\"", - "19, lccn any \"2023202345 XXX\"", - "20, lccn all \"2023202345\"", - "21, contributor = Family", - "22, contributor == Meeting", - "23, contributor ==/string Organization", - "24, contributor any Person", - "25, contributor all Family" + "1, keyword = Family", + "2, keyword = titleAbc def", + "3, keyword = 1234567890123", + "4, keyword = hubAAP1", + "5, keyword = first instance note", + "6, title any \"def\"", + "7, title = \"titleAbc def\"", + "8, title == \"titleAbc def\"", + "9, title ==/string \"titleAbc def\"", + "10, isbn = \"*\"", + "11, isbn = \"1234567890123\"", + "12, isbn = \"1234*\"", + "13, isbn == \"1234567890123\"", + "14, isbn ==/string \"1234567890123\"", + "15, isbn any \"1234567890123\"", + "16, isbn any \"1234567890123 XXX\"", + "17, isbn all \"1234567890123\"", + "18, lccn = \"*\"", + "19, lccn = \"2023202345\"", + "20, lccn = \"2023*\"", + "21, lccn == \"2023202345\"", + "22, lccn ==/string \"2023202345\"", + "23, lccn any \"2023202345\"", + "24, lccn any \"2023202345 XXX\"", + "25, lccn all \"2023202345\"", + "26, contributor = Family", + "27, contributor == Meeting", + "28, contributor ==/string Organization", + "29, contributor any Person", + "30, contributor all Family", + "31, hub = *", + "32, hub = hubA*", + "33, hub = hubAAP1", + "34, hub == hubAAP2", + "35, hub ==/string hubAAP1", + "36, hub any \"hubAAP1 hubAAP2 XXX\"", + "37, hub all hubAAP1", + "38, note = *", + "39, note = first*", + "40, note = first instance note", + "41, note == first instance note", + "42, note ==/string first instance note", + "43, note any \"first instance note XXX\"", + "44, note all \"first instance note\"", + "45, lang = *", + "46, lang = ru*", + "47, lang = eng", + "48, lang == rus", + "49, lang ==/string eng", + "50, lang == (\"rus\" or \"eng\" or \"XXX\")", + "51, lang all rus", + "52, format = *", + "53, format = Mono*", + "54, format = monograph", + "55, format == monograph", + "56, format ==/string Monograph", + "57, format any \"Monograph XXX\"", + "58, format all Monograph", + "59, suppressFromDiscovery = false", + "60, suppressFromDiscovery == false", + "61, staffSuppress = true", + "62, staffSuppress == true", + "63, publicationDate = 2023", + "64, publicationDate == 2024", + "65, publicationDate ==/string 2023", + "66, publicationDate == (\"2023\" or \"2024\" or \"2020\")", + "67, publicationDate all 2024" }) void searchByLinkedDataWork_parameterized_singleResult(int index, String query) throws Throwable { doSearchByLinkedDataWork(query) - .andExpect(jsonPath("$.totalRecords", is(1))) - .andExpect(jsonPath(toId(toWork()), is("123456123456"))) - .andExpect(jsonPath(toTitleValue(toWork(), 0), is("titleAbc def"))) - .andExpect(jsonPath(toTitleType(toWork(), 0), is("Main"))) - .andExpect(jsonPath(toTitleValue(toWork(), 1), is("sub"))) - .andExpect(jsonPath(toTitleType(toWork(), 1), is("Sub"))) - .andExpect(jsonPath(toContributorName(toWork(), 0), is("Family"))) - .andExpect(jsonPath(toContributorType(toWork(), 0), is("Family"))) - .andExpect(jsonPath(toContributorIsCreator(toWork(), 0), is(true))) - .andExpect(jsonPath(toContributorName(toWork(), 1), is("Meeting"))) - .andExpect(jsonPath(toContributorType(toWork(), 1), is("Meeting"))) - .andExpect(jsonPath(toContributorIsCreator(toWork(), 1), is(false))) - .andExpect(jsonPath(toContributorName(toWork(), 2), is("Organization"))) - .andExpect(jsonPath(toContributorType(toWork(), 2), is("Organization"))) - .andExpect(jsonPath(toContributorIsCreator(toWork(), 2), is(true))) - .andExpect(jsonPath(toContributorName(toWork(), 3), is("Person"))) - .andExpect(jsonPath(toContributorType(toWork(), 3), is("Person"))) - .andExpect(jsonPath(toContributorIsCreator(toWork(), 3), is(false))) - .andExpect(jsonPath(toContributorName(toWork(), 4), is("common"))) - .andExpect(jsonPath(toContributorType(toWork(), 4), is("Family"))) - .andExpect(jsonPath(toContributorIsCreator(toWork(), 4), is(true))) - .andExpect(jsonPath(toLanguage(0), is("eng"))) - .andExpect(jsonPath(toLanguage(1), is("rus"))) - .andExpect(jsonPath(toClassificationNumber(0), is("1234"))) - .andExpect(jsonPath(toClassificationSource(0), is("ddc"))) - .andExpect(jsonPath(toClassificationNumber(1), is("5678"))) - .andExpect(jsonPath(toClassificationSource(1), is("other"))) - .andExpect(jsonPath(toSubject(0), is("Subject 1"))) - .andExpect(jsonPath(toSubject(1), is("Subject 2"))) + .andExpect(jsonPath(toTotalRecords(), is(1))) + .andExpect(jsonPath(toId(toRootContent()), is("123456123456"))) + .andExpect(jsonPath(toClassificationNumber(toRootContent(), 0), is("1234"))) + .andExpect(jsonPath(toClassificationSource(toRootContent(), 0), is("ddc"))) + .andExpect(jsonPath(toClassificationNumber(toRootContent(), 1), is("5678"))) + .andExpect(jsonPath(toClassificationSource(toRootContent(), 1), is("other"))) + .andExpect(jsonPath(toContributorName(toRootContent(), 0), is("Family"))) + .andExpect(jsonPath(toContributorType(toRootContent(), 0), is("Family"))) + .andExpect(jsonPath(toContributorIsCreator(toRootContent(), 0), is(true))) + .andExpect(jsonPath(toContributorName(toRootContent(), 1), is("Meeting"))) + .andExpect(jsonPath(toContributorType(toRootContent(), 1), is("Meeting"))) + .andExpect(jsonPath(toContributorIsCreator(toRootContent(), 1), is(false))) + .andExpect(jsonPath(toContributorName(toRootContent(), 2), is("Organization"))) + .andExpect(jsonPath(toContributorType(toRootContent(), 2), is("Organization"))) + .andExpect(jsonPath(toContributorIsCreator(toRootContent(), 2), is(true))) + .andExpect(jsonPath(toContributorName(toRootContent(), 3), is("Person"))) + .andExpect(jsonPath(toContributorType(toRootContent(), 3), is("Person"))) + .andExpect(jsonPath(toContributorIsCreator(toRootContent(), 3), is(false))) + .andExpect(jsonPath(toContributorName(toRootContent(), 4), is("common"))) + .andExpect(jsonPath(toContributorType(toRootContent(), 4), is("Family"))) + .andExpect(jsonPath(toContributorIsCreator(toRootContent(), 4), is(true))) + .andExpect(jsonPath(toHubAap(toRootContent(), 0), is("hubAAP1"))) + .andExpect(jsonPath(toHubAap(toRootContent(), 1), is("hubAAP2"))) + .andExpect(jsonPath(toLanguage(toRootContent(), 0), is("eng"))) + .andExpect(jsonPath(toLanguage(toRootContent(), 1), is("rus"))) + .andExpect(jsonPath(toNoteValue(toRootContent(), 0), is("first work note"))) + .andExpect(jsonPath(toNoteType(toRootContent(), 0), is("firstWorkNoteType"))) + .andExpect(jsonPath(toNoteValue(toRootContent(), 1), is("second work note"))) + .andExpect(jsonPath(toNoteType(toRootContent(), 1), is("secondWorkNoteType"))) + .andExpect(jsonPath(toSubject(toRootContent(), 0), is("Subject 1"))) + .andExpect(jsonPath(toSubject(toRootContent(), 1), is("Subject 2"))) + .andExpect(jsonPath(toTitleValue(toRootContent(), 0), is("titleAbc def"))) + .andExpect(jsonPath(toTitleType(toRootContent(), 0), is("Main"))) + .andExpect(jsonPath(toTitleValue(toRootContent(), 1), is("sub"))) + .andExpect(jsonPath(toTitleType(toRootContent(), 1), is("Sub"))) .andExpect(jsonPath(toId(toInstance()), is("instance1"))) - .andExpect(jsonPath(toTitleValue(toInstance(), 0), is("Instance1_Title"))) - .andExpect(jsonPath(toTitleType(toInstance(), 0), is("Main"))) - .andExpect(jsonPath(toTitleValue(toInstance(), 1), is("Instance1_Subtitle"))) - .andExpect(jsonPath(toTitleType(toInstance(), 1), is("Sub"))) - .andExpect(jsonPath(toIdValue(0), is("1234567890123"))) - .andExpect(jsonPath(toIdType(0), is("ISBN"))) - .andExpect(jsonPath(toIdValue(1), is(" 2023-202345/AC/r932"))) - .andExpect(jsonPath(toIdType(1), is("LCCN"))) .andExpect(jsonPath(toContributorName(toInstance(), 0), is("Instance1_Family"))) .andExpect(jsonPath(toContributorType(toInstance(), 0), is("Family"))) .andExpect(jsonPath(toContributorIsCreator(toInstance(), 0), is(true))) @@ -143,13 +205,28 @@ void searchByLinkedDataWork_parameterized_singleResult(int index, String query) .andExpect(jsonPath(toContributorName(toInstance(), 3), is("Instance1_Person"))) .andExpect(jsonPath(toContributorType(toInstance(), 3), is("Person"))) .andExpect(jsonPath(toContributorIsCreator(toInstance(), 3), is(false))) - .andExpect(jsonPath(toPublicationName(0), is("publisher"))) - .andExpect(jsonPath(toPublicationDate(0), is("2023"))) - .andExpect(jsonPath(toPublicationName(1), is("publisher2"))) - .andExpect(jsonPath(toPublicationDate(1), is("2024"))) - .andExpect(jsonPath(toEditionStatement(0), is("1st edition"))) - .andExpect(jsonPath(toEditionStatement(1), is("2nd edition"))) - ; + .andExpect(jsonPath(toEditionStatement(toInstance(), 0), is("1st edition"))) + .andExpect(jsonPath(toEditionStatement(toInstance(), 1), is("2nd edition"))) + .andExpect(jsonPath(toFormat(toInstance()), is("Monograph"))) + .andExpect(jsonPath(toIdValue(toInstance(), 0), is("1234567890123"))) + .andExpect(jsonPath(toIdType(toInstance(), 0), is("ISBN"))) + .andExpect(jsonPath(toIdValue(toInstance(), 1), is(" 2023-202345/AC/r932"))) + .andExpect(jsonPath(toIdType(toInstance(), 1), is("LCCN"))) + .andExpect(jsonPath(toNoteValue(toInstance(), 0), is("first instance note"))) + .andExpect(jsonPath(toNoteType(toInstance(), 0), is("firstInstanceNoteType"))) + .andExpect(jsonPath(toNoteValue(toInstance(), 1), is("second instance note"))) + .andExpect(jsonPath(toNoteType(toInstance(), 1), is("secondInstanceNoteType"))) + .andExpect(jsonPath(toPublicationName(toInstance(), 0), is("publisher"))) + .andExpect(jsonPath(toPublicationDate(toInstance(), 0), is("2023"))) + .andExpect(jsonPath(toPublicationName(toInstance(), 1), is("publisher2"))) + .andExpect(jsonPath(toPublicationDate(toInstance(), 1), is("2024"))) + .andExpect(jsonPath(toSuppressFromDiscovery(toInstance()), is(false))) + .andExpect(jsonPath(toSuppressStaff(toInstance()), is(true))) + .andExpect(jsonPath(toTitleValue(toInstance(), 0), is("Instance1_Title"))) + .andExpect(jsonPath(toTitleType(toInstance(), 0), is("Main"))) + .andExpect(jsonPath(toTitleValue(toInstance(), 1), is("Instance1_Subtitle"))) + .andExpect(jsonPath(toTitleType(toInstance(), 1), is("Sub"))) + ; } @DisplayName("search by liked data work (nothing is found)") @@ -188,99 +265,86 @@ void searchByLinkedDataWork_parameterized_singleResult(int index, String query) }) void searchByLinkedDataWork_parameterized_zeroResults(int index, String query) throws Throwable { doSearchByLinkedDataWork(query) - .andExpect(jsonPath("$.totalRecords", is(0))); - } - - private String path(String path) { - return format("['%s']", path); - } - - private String arrayPath(String path) { - return arrayPath(path, 0); - } - - private String arrayPath(String path, int number) { - return format("['%s'][%s]", path, number); - } - - private String toWork() { - return join(".", "$", arrayPath("content")); - } - - private String toId(String base) { - return join(".", base, path("id")); - } - - private String toTitle(String base, int number) { - return join(".", base, arrayPath("titles", number)); - } - - private String toTitleValue(String base, int number) { - return join(".", toTitle(base, number), path("value")); - } - - private String toTitleType(String base, int number) { - return join(".", toTitle(base, number), path("type")); - } - - private String toContributor(String base, int number) { - return join(".", base, arrayPath("contributors", number)); - } - - private String toContributorName(String base, int number) { - return join(".", toContributor(base, number), path("name")); + .andExpect(jsonPath(toTotalRecords(), is(0))); } - private String toContributorType(String base, int number) { - return join(".", toContributor(base, number), path("type")); - } - - private String toContributorIsCreator(String base, int number) { - return join(".", toContributor(base, number), path("isCreator")); - } - - private String toLanguage(int number) { - return join(".", toWork(), arrayPath("languages", number), path("value")); - } - - private String toClassification(int number) { - return join(".", toWork(), arrayPath("classifications", number)); - } - - private String toClassificationNumber(int number) { - return join(".", toClassification(number), path("number")); - } - - private String toClassificationSource(int number) { - return join(".", toClassification(number), path("source")); - } - - private String toSubject(int number) { - return join(".", toWork(), arrayPath("subjects", number), path("value")); - } - - private String toInstance() { - return join(".", toWork(), arrayPath("instances")); - } - - private String toIdValue(int number) { - return join(".", toInstance(), arrayPath("identifiers", number), path("value")); - } - - private String toIdType(int number) { - return join(".", toInstance(), arrayPath("identifiers", number), path("type")); - } - - private String toPublicationName(int number) { - return join(".", toInstance(), arrayPath("publications", number), path("name")); - } - - private String toPublicationDate(int number) { - return join(".", toInstance(), arrayPath("publications", number), path("date")); - } - - private String toEditionStatement(int number) { - return join(".", toInstance(), arrayPath("editionStatements", number), path("value")); + @DisplayName("search by linked data work without instances (single work is found)") + @ParameterizedTest(name = "[{0}] {1}") + @CsvSource({ + "1, keyword = titleAbc def", + "2, keyword = Family", + "4, keyword = hubAAP1", + "5, keyword = first work note", + "6, workTitle any \"def\"", + "7, workTitle = \"titleAbc def\"", + "8, workTitle == \"titleAbc def\"", + "9, workTitle ==/string \"titleAbc def\"", + "10, workContributor = Family", + "11, workContributor == Meeting", + "12, workContributor ==/string Organization", + "13, workContributor any Person", + "14, workContributor all Family", + "15, hub = *", + "16, hub = hubA*", + "17, hub = hubAAP1", + "18, hub == hubAAP2", + "19, hub ==/string hubAAP1", + "20, hub any \"hubAAP1 hubAAP2 XXX\"", + "21, hub all hubAAP1", + "22, workNote = *", + "23, workNote = first*", + "24, workNote = first work note", + "25, workNote == first work note", + "26, workNote ==/string first work note", + "27, workNote any \"first work note XXX\"", + "28, workNote all \"first work note\"", + "29, lang = *", + "30, lang = ru*", + "31, lang = eng", + "32, lang == rus", + "33, lang ==/string eng", + "34, lang == (\"rus\" or \"eng\" or \"XXX\")", + "35, lang all rus" + }) + void searchByLinkedDataWorkWithNoInstances_parameterized_singleResult(int index, String query) throws Throwable { + doSearchByLinkedDataWorkWithoutInstances(query) + .andExpect(jsonPath(toTotalRecords(), is(1))) + .andExpect(jsonPath(toId(toRootContent()), is("123456123456"))) + .andExpect(jsonPath(toClassificationNumber(toRootContent(), 0), is("1234"))) + .andExpect(jsonPath(toClassificationSource(toRootContent(), 0), is("ddc"))) + .andExpect(jsonPath(toClassificationNumber(toRootContent(), 1), is("5678"))) + .andExpect(jsonPath(toClassificationSource(toRootContent(), 1), is("other"))) + .andExpect(jsonPath(toContributorName(toRootContent(), 0), is("Family"))) + .andExpect(jsonPath(toContributorType(toRootContent(), 0), is("Family"))) + .andExpect(jsonPath(toContributorIsCreator(toRootContent(), 0), is(true))) + .andExpect(jsonPath(toContributorName(toRootContent(), 1), is("Meeting"))) + .andExpect(jsonPath(toContributorType(toRootContent(), 1), is("Meeting"))) + .andExpect(jsonPath(toContributorIsCreator(toRootContent(), 1), is(false))) + .andExpect(jsonPath(toContributorName(toRootContent(), 2), is("Organization"))) + .andExpect(jsonPath(toContributorType(toRootContent(), 2), is("Organization"))) + .andExpect(jsonPath(toContributorIsCreator(toRootContent(), 2), is(true))) + .andExpect(jsonPath(toContributorName(toRootContent(), 3), is("Person"))) + .andExpect(jsonPath(toContributorType(toRootContent(), 3), is("Person"))) + .andExpect(jsonPath(toContributorIsCreator(toRootContent(), 3), is(false))) + .andExpect(jsonPath(toContributorName(toRootContent(), 4), is("common"))) + .andExpect(jsonPath(toContributorType(toRootContent(), 4), is("Family"))) + .andExpect(jsonPath(toContributorIsCreator(toRootContent(), 4), is(true))) + .andExpect(jsonPath(toHubAap(toRootContent(), 0), is("hubAAP1"))) + .andExpect(jsonPath(toHubAap(toRootContent(), 1), is("hubAAP2"))) + .andExpect(jsonPath(toLanguage(toRootContent(), 0), is("eng"))) + .andExpect(jsonPath(toLanguage(toRootContent(), 1), is("rus"))) + .andExpect(jsonPath(toNoteValue(toRootContent(), 0), is("first work note"))) + .andExpect(jsonPath(toNoteType(toRootContent(), 0), is("firstWorkNoteType"))) + .andExpect(jsonPath(toNoteValue(toRootContent(), 1), is("second work note"))) + .andExpect(jsonPath(toNoteType(toRootContent(), 1), is("secondWorkNoteType"))) + .andExpect(jsonPath(toSubject(toRootContent(), 0), is("Subject 1"))) + .andExpect(jsonPath(toSubject(toRootContent(), 1), is("Subject 2"))) + .andExpect(jsonPath(toTitleValue(toRootContent(), 0), is("titleAbc def"))) + .andExpect(jsonPath(toTitleType(toRootContent(), 0), is("Main"))) + .andExpect(jsonPath(toTitleValue(toRootContent(), 1), is("sub"))) + .andExpect(jsonPath(toTitleType(toRootContent(), 1), is("Sub"))) + .andExpect(jsonPath(toInstance()).doesNotExist()) + ; } } diff --git a/src/test/java/org/folio/search/integration/KafkaMessageListenerTest.java b/src/test/java/org/folio/search/integration/KafkaMessageListenerTest.java index 711e59575..ff24b268d 100644 --- a/src/test/java/org/folio/search/integration/KafkaMessageListenerTest.java +++ b/src/test/java/org/folio/search/integration/KafkaMessageListenerTest.java @@ -11,6 +11,7 @@ import static org.folio.search.utils.SearchUtils.CONTRIBUTOR_RESOURCE; import static org.folio.search.utils.SearchUtils.INSTANCE_RESOURCE; import static org.folio.search.utils.SearchUtils.LINKED_DATA_AUTHORITY_RESOURCE; +import static org.folio.search.utils.SearchUtils.LINKED_DATA_INSTANCE_RESOURCE; import static org.folio.search.utils.SearchUtils.LINKED_DATA_WORK_RESOURCE; import static org.folio.search.utils.TestConstants.INVENTORY_INSTANCE_TOPIC; import static org.folio.search.utils.TestConstants.RESOURCE_ID; @@ -24,6 +25,7 @@ import static org.folio.search.utils.TestConstants.inventoryInstanceTopic; import static org.folio.search.utils.TestConstants.inventoryItemTopic; import static org.folio.search.utils.TestConstants.linkedDataAuthorityTopic; +import static org.folio.search.utils.TestConstants.linkedDataInstanceTopic; import static org.folio.search.utils.TestConstants.linkedDataWorkTopic; import static org.folio.search.utils.TestUtils.OBJECT_MAPPER; import static org.folio.search.utils.TestUtils.mapOf; @@ -47,6 +49,7 @@ import org.apache.kafka.clients.consumer.ConsumerRecord; import org.folio.search.domain.dto.Authority; import org.folio.search.domain.dto.LinkedDataAuthority; +import org.folio.search.domain.dto.LinkedDataInstance; import org.folio.search.domain.dto.LinkedDataWork; import org.folio.search.domain.dto.ResourceEvent; import org.folio.search.domain.dto.ResourceEventType; @@ -291,6 +294,38 @@ void handleConsortiumInstanceEvents_negative() { any(), any()); } + @Test + void handleLinkedDataInstanceEvent_positive() { + var payload = toMap(new LinkedDataInstance().id(RESOURCE_ID)); + + var consumerRecord = new ConsumerRecord<>(linkedDataInstanceTopic(TENANT_ID), 0, 0, RESOURCE_ID, + resourceEvent(null, LINKED_DATA_INSTANCE_RESOURCE, CREATE, payload, null)); + messageListener.handleLinkedDataEvents(List.of(consumerRecord)); + + var expectedEvents = singletonList( + resourceEvent(RESOURCE_ID, LINKED_DATA_INSTANCE_RESOURCE, CREATE, payload, null) + ); + verify(resourceService).indexResources(expectedEvents); + verify(batchProcessor).consumeBatchWithFallback(eq(expectedEvents), eq(KAFKA_RETRY_TEMPLATE_NAME), any(), any()); + } + + @Test + void handleLinkedDataInstanceEvent_negative_logFailedEvent() { + var payload = toMap(new LinkedDataInstance().id(RESOURCE_ID)); + var expectedEvents = List.of(resourceEvent(RESOURCE_ID, LINKED_DATA_INSTANCE_RESOURCE, UPDATE, payload, null)); + + doAnswer(inv -> { + inv.>getArgument(3).accept(expectedEvents.get(0), new Exception("error")); + return null; + }).when(batchProcessor).consumeBatchWithFallback(eq(expectedEvents), eq(KAFKA_RETRY_TEMPLATE_NAME), any(), any()); + + var consumerRecord = new ConsumerRecord<>(linkedDataInstanceTopic(TENANT_ID), 0, 0, RESOURCE_ID, + resourceEvent(null, LINKED_DATA_INSTANCE_RESOURCE, UPDATE, payload, null)); + messageListener.handleLinkedDataEvents(List.of(consumerRecord)); + + verify(batchProcessor).consumeBatchWithFallback(eq(expectedEvents), eq(KAFKA_RETRY_TEMPLATE_NAME), any(), any()); + } + @Test void handleLinkedDataWorkEvent_positive() { var payload = toMap(new LinkedDataWork().id(RESOURCE_ID)); diff --git a/src/test/java/org/folio/search/sample/SampleLinkedData.java b/src/test/java/org/folio/search/sample/SampleLinkedData.java index 0d92c1c2b..487c6c037 100644 --- a/src/test/java/org/folio/search/sample/SampleLinkedData.java +++ b/src/test/java/org/folio/search/sample/SampleLinkedData.java @@ -10,31 +10,31 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class SampleLinkedData { - private static final Map WORK_AS_MAP = - readJsonFromFile("/samples/linked-data/work.json", MAP_TYPE_REFERENCE); - - private static final Map WORK_2_AS_MAP = - readJsonFromFile("/samples/linked-data/work2.json", MAP_TYPE_REFERENCE); - - private static final Map AUTHORITY_CONCEPT_AS_MAP = - readJsonFromFile("/samples/linked-data/authority_concept.json", MAP_TYPE_REFERENCE); + public static Map getInstanceSampleAsMap() { + return readJson("/samples/linked-data/instance.json"); + } - private static final Map AUTHORITY_PERSON_AS_MAP = - readJsonFromFile("/samples/linked-data/authority_person.json", MAP_TYPE_REFERENCE); + public static Map getInstance2SampleAsMap() { + return readJson("/samples/linked-data/instance2.json"); + } public static Map getWorkSampleAsMap() { - return WORK_AS_MAP; + return readJson("/samples/linked-data/work.json"); } public static Map getWork2SampleAsMap() { - return WORK_2_AS_MAP; + return readJson("/samples/linked-data/work2.json"); } public static Map getAuthorityConceptSampleAsMap() { - return AUTHORITY_CONCEPT_AS_MAP; + return readJson("/samples/linked-data/authority_concept.json"); } public static Map getAuthorityPersonSampleAsMap() { - return AUTHORITY_PERSON_AS_MAP; + return readJson("/samples/linked-data/authority_person.json"); + } + + private static Map readJson(String path) { + return readJsonFromFile(path, MAP_TYPE_REFERENCE); } } diff --git a/src/test/java/org/folio/search/support/base/ApiEndpoints.java b/src/test/java/org/folio/search/support/base/ApiEndpoints.java index 72eea5f95..4f5e4079b 100644 --- a/src/test/java/org/folio/search/support/base/ApiEndpoints.java +++ b/src/test/java/org/folio/search/support/base/ApiEndpoints.java @@ -101,7 +101,11 @@ public static String instanceClassificationBrowsePath(BrowseOptionType optionTyp return "/browse/classification-numbers/" + optionType.getValue() + "/instances"; } - public static String linkedDataSearchPath() { + public static String linkedDataInstanceSearchPath() { + return "/search/linked-data/instances"; + } + + public static String linkedDataWorkSearchPath() { return "/search/linked-data/works"; } diff --git a/src/test/java/org/folio/search/support/base/BaseConsortiumIntegrationTest.java b/src/test/java/org/folio/search/support/base/BaseConsortiumIntegrationTest.java index b56f35b4b..b8991d316 100644 --- a/src/test/java/org/folio/search/support/base/BaseConsortiumIntegrationTest.java +++ b/src/test/java/org/folio/search/support/base/BaseConsortiumIntegrationTest.java @@ -1,5 +1,6 @@ package org.folio.search.support.base; +import static java.lang.String.valueOf; import static java.util.Arrays.asList; import static org.folio.search.support.base.ApiEndpoints.instanceSearchPath; import static org.folio.search.utils.TestConstants.CENTRAL_TENANT_ID; @@ -13,6 +14,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.util.List; +import java.util.Map; import java.util.function.Consumer; import lombok.SneakyThrows; import org.folio.search.domain.dto.Instance; @@ -124,11 +126,11 @@ public static ResultActions doPost(String uri, String tenantHeader, Object body) @SneakyThrows protected static ResultActions doSearchByInstances(String query) { - return doSearch(instanceSearchPath(), MEMBER_TENANT_ID, query, null, null, null); + return doSearch(instanceSearchPath(), MEMBER_TENANT_ID, Map.of("query", query)); } @SneakyThrows protected static ResultActions doSearchByInstances(String query, boolean expandAll) { - return doSearch(instanceSearchPath(), MEMBER_TENANT_ID, query, null, null, expandAll); + return doSearch(instanceSearchPath(), MEMBER_TENANT_ID, Map.of("query", query, "expandAll", valueOf(expandAll))); } } diff --git a/src/test/java/org/folio/search/support/base/BaseIntegrationTest.java b/src/test/java/org/folio/search/support/base/BaseIntegrationTest.java index 3bc8f0ce6..2526aeabc 100644 --- a/src/test/java/org/folio/search/support/base/BaseIntegrationTest.java +++ b/src/test/java/org/folio/search/support/base/BaseIntegrationTest.java @@ -7,14 +7,18 @@ import static org.folio.search.support.base.ApiEndpoints.authoritySearchPath; import static org.folio.search.support.base.ApiEndpoints.instanceSearchPath; import static org.folio.search.support.base.ApiEndpoints.linkedDataAuthoritySearchPath; -import static org.folio.search.support.base.ApiEndpoints.linkedDataSearchPath; +import static org.folio.search.support.base.ApiEndpoints.linkedDataInstanceSearchPath; +import static org.folio.search.support.base.ApiEndpoints.linkedDataWorkSearchPath; +import static org.folio.search.utils.SearchUtils.LINKED_DATA_AUTHORITY_RESOURCE; +import static org.folio.search.utils.SearchUtils.LINKED_DATA_INSTANCE_RESOURCE; +import static org.folio.search.utils.SearchUtils.LINKED_DATA_WORK_RESOURCE; import static org.folio.search.utils.SearchUtils.getIndexName; import static org.folio.search.utils.TestConstants.TENANT_ID; import static org.folio.search.utils.TestConstants.inventoryAuthorityTopic; import static org.folio.search.utils.TestConstants.linkedDataAuthorityTopic; +import static org.folio.search.utils.TestConstants.linkedDataInstanceTopic; import static org.folio.search.utils.TestConstants.linkedDataWorkTopic; import static org.folio.search.utils.TestUtils.asJsonString; -import static org.folio.search.utils.TestUtils.doIfNotNull; import static org.folio.search.utils.TestUtils.randomId; import static org.folio.search.utils.TestUtils.removeEnvProperty; import static org.folio.search.utils.TestUtils.resourceEvent; @@ -44,6 +48,7 @@ import org.folio.search.domain.dto.FeatureConfig; import org.folio.search.domain.dto.Instance; import org.folio.search.domain.dto.LinkedDataAuthority; +import org.folio.search.domain.dto.LinkedDataInstance; import org.folio.search.domain.dto.LinkedDataWork; import org.folio.search.domain.dto.ResourceEvent; import org.folio.search.domain.dto.TenantConfiguredFeature; @@ -140,53 +145,64 @@ protected static ResultActions doDelete(String uri, Object... args) { @SneakyThrows protected static ResultActions doSearchByInstances(String query) { - return doSearch(instanceSearchPath(), TENANT_ID, query, null, null, null); + return doSearch(instanceSearchPath(), TENANT_ID, Map.of("query", query)); } @SneakyThrows protected static ResultActions doSearchByInstances(String query, boolean expandAll) { - return doSearch(instanceSearchPath(), TENANT_ID, query, null, null, expandAll); + return doSearch(instanceSearchPath(), TENANT_ID, Map.of("query", query, "expandAll", String.valueOf(expandAll))); } @SneakyThrows protected static ResultActions doSearchByInstances(String query, int limit, int offset) { - return doSearch(instanceSearchPath(), TENANT_ID, query, limit, offset, null); + return doSearch(instanceSearchPath(), TENANT_ID, + Map.of("query", query, "limit", String.valueOf(limit), "offset", String.valueOf(offset)) + ); } @SneakyThrows protected static ResultActions attemptSearchByInstances(String query) { - return attemptSearch(instanceSearchPath(), TENANT_ID, query, null, null, null); + return attemptSearch(instanceSearchPath(), TENANT_ID, Map.of("query", query)); } @SneakyThrows protected static ResultActions doSearchByAuthorities(String query) { - return doSearch(authoritySearchPath(), TENANT_ID, query, null, null, null); + return doSearch(authoritySearchPath(), TENANT_ID, Map.of("query", query)); + } + + @SneakyThrows + protected static ResultActions doSearchByLinkedDataInstance(String query) { + return doSearch(linkedDataInstanceSearchPath(), TENANT_ID, Map.of("query", query)); } @SneakyThrows protected static ResultActions doSearchByLinkedDataWork(String query) { - return doSearch(linkedDataSearchPath(), TENANT_ID, query, null, null, null); + return doSearch(linkedDataWorkSearchPath(), TENANT_ID, Map.of("query", query)); + } + + @SneakyThrows + protected static ResultActions doSearchByLinkedDataWorkWithoutInstances(String query) { + return doSearch(linkedDataWorkSearchPath(), TENANT_ID, Map.of("query", query, "omitInstances", "true")); } @SneakyThrows protected static ResultActions doSearchByLinkedDataAuthority(String query) { - return doSearch(linkedDataAuthoritySearchPath(), TENANT_ID, query, null, null, null); + return doSearch(linkedDataAuthoritySearchPath(), TENANT_ID, Map.of("query", query)); } @SneakyThrows protected static ResultActions attemptSearchByAuthorities(String query) { - return attemptSearch(authoritySearchPath(), TENANT_ID, query, null, null, null); + return attemptSearch(authoritySearchPath(), TENANT_ID, Map.of("query", query)); } @SneakyThrows protected static ResultActions doSearch(String path, String query) { - return doSearch(path, TENANT_ID, query, null, null, null); + return doSearch(path, TENANT_ID, Map.of("query", query)); } @SneakyThrows - protected static ResultActions doSearch( - String path, String tenantId, String query, Integer limit, Integer offset, Boolean expandAll) { - return attemptSearch(path, tenantId, query, limit, offset, expandAll).andExpect(status().isOk()); + protected static ResultActions doSearch(String path, String tenantId, Map queryParams) { + return attemptSearch(path, tenantId, queryParams).andExpect(status().isOk()); } protected static long countIndexDocument(String resource, String tenantId) throws IOException { @@ -213,14 +229,10 @@ protected static void cleanUpIndex(String resource, String tenantId) throws IOEx } @SneakyThrows - protected static ResultActions attemptSearch( - String path, String tenantId, String query, Integer limit, Integer offset, Boolean expandAll) { + protected static ResultActions attemptSearch(String path, String tenantId, Map queryParams) { var requestBuilder = get(path); - doIfNotNull(limit, value -> requestBuilder.queryParam("limit", String.valueOf(value))); - doIfNotNull(offset, value -> requestBuilder.queryParam("offset", String.valueOf(value))); - doIfNotNull(expandAll, value -> requestBuilder.queryParam("expandAll", String.valueOf(value))); - - return mockMvc.perform(requestBuilder.queryParam("query", query) + queryParams.forEach(requestBuilder::param); + return mockMvc.perform(requestBuilder .headers(defaultHeaders(tenantId)) .accept("application/json;charset=UTF-8")); } @@ -302,14 +314,25 @@ protected static void setUpTenant(Class type, String tenant, Runnable postIni authority -> kafkaTemplate.send(inventoryAuthorityTopic(tenant), resourceEvent(null, null, authority))); } + if (type.equals(LinkedDataInstance.class)) { + setUpTenant(tenant, linkedDataInstanceSearchPath(), postInitAction, asList(records), expectedCount, + ldInstance -> kafkaTemplate.send(linkedDataInstanceTopic(tenant), + resourceEvent(null, LINKED_DATA_INSTANCE_RESOURCE, ldInstance)) + ); + } + if (type.equals(LinkedDataWork.class)) { - setUpTenant(tenant, linkedDataSearchPath(), postInitAction, asList(records), expectedCount, - ldWork -> kafkaTemplate.send(linkedDataWorkTopic(tenant), resourceEvent(null, null, ldWork))); + setUpTenant(tenant, linkedDataWorkSearchPath(), postInitAction, asList(records), expectedCount, + ldWork -> kafkaTemplate.send(linkedDataWorkTopic(tenant), + resourceEvent(null, LINKED_DATA_WORK_RESOURCE, ldWork)) + ); } if (type.equals(LinkedDataAuthority.class)) { setUpTenant(tenant, linkedDataAuthoritySearchPath(), postInitAction, asList(records), expectedCount, - ldAuthority -> kafkaTemplate.send(linkedDataAuthorityTopic(tenant), resourceEvent(null, null, ldAuthority))); + ldAuthority -> kafkaTemplate.send(linkedDataAuthorityTopic(tenant), + resourceEvent(null, LINKED_DATA_AUTHORITY_RESOURCE, ldAuthority)) + ); } } @@ -324,7 +347,7 @@ protected static void setUpTenant(String tenant, String validationPath, Runn protected static void saveRecords(String tenant, String validationPath, List records, Integer expectedCount, Consumer consumer) { records.forEach(consumer); - if (!records.isEmpty()) { + if (! records.isEmpty()) { checkThatEventsFromKafkaAreIndexed(tenant, validationPath, expectedCount); } } @@ -384,7 +407,8 @@ protected static void removeTenant(String tenantId) { protected static void checkThatEventsFromKafkaAreIndexed(String tenantId, String path, int size) { await().atMost(TWO_MINUTES).pollInterval(TWO_HUNDRED_MILLISECONDS).untilAsserted(() -> - doSearch(path, tenantId, "cql.allRecords=1", 1, null, null).andExpect(jsonPath("$.totalRecords", is(size)))); + doSearch(path, tenantId, Map.of("query", "cql.allRecords=1", "limit", "1")) + .andExpect(jsonPath("$.totalRecords", is(size)))); } protected static String prepareQuery(String queryTemplate, String value) { diff --git a/src/test/java/org/folio/search/utils/LinkedDataTestUtils.java b/src/test/java/org/folio/search/utils/LinkedDataTestUtils.java new file mode 100644 index 000000000..9703d37b2 --- /dev/null +++ b/src/test/java/org/folio/search/utils/LinkedDataTestUtils.java @@ -0,0 +1,143 @@ +package org.folio.search.utils; + +import static java.lang.String.format; +import static java.lang.String.join; + +import lombok.experimental.UtilityClass; + +@UtilityClass +public class LinkedDataTestUtils { + + public static String path(String path) { + return format("['%s']", path); + } + + public static String arrayPath(String path) { + return arrayPath(path, 0); + } + + public static String arrayPath(String path, int number) { + return format("['%s'][%s]", path, number); + } + + public static String toRootContent() { + return join(".", "$", arrayPath("content")); + } + + public static String toRootContent(int number) { + return join(".", "$", arrayPath("content", number)); + } + + public static String toId(String base) { + return join(".", base, path("id")); + } + + public static String toTitle(String base, int number) { + return join(".", base, arrayPath("titles", number)); + } + + public static String toTitleValue(String base, int number) { + return join(".", toTitle(base, number), path("value")); + } + + public static String toTitleType(String base, int number) { + return join(".", toTitle(base, number), path("type")); + } + + public static String toContributor(String base, int number) { + return join(".", base, arrayPath("contributors", number)); + } + + public static String toContributorName(String base, int number) { + return join(".", toContributor(base, number), path("name")); + } + + public static String toContributorType(String base, int number) { + return join(".", toContributor(base, number), path("type")); + } + + public static String toContributorIsCreator(String base, int number) { + return join(".", toContributor(base, number), path("isCreator")); + } + + public static String toHubAap(String base, int number) { + return join(".", base, arrayPath("hubAAPs", number)); + } + + + public static String toLanguage(String base, int number) { + return join(".", base, arrayPath("languages", number)); + } + + public static String toClassification(String base, int number) { + return join(".", base, arrayPath("classifications", number)); + } + + public static String toClassificationNumber(String base, int number) { + return join(".", toClassification(base, number), path("number")); + } + + public static String toClassificationSource(String base, int number) { + return join(".", toClassification(base, number), path("source")); + } + + public static String toSubject(String base, int number) { + return join(".", base, arrayPath("subjects", number)); + } + + public static String toInstance() { + return join(".", toRootContent(), arrayPath("instances")); + } + + public static String toIdValue(String base, int number) { + return join(".", base, arrayPath("identifiers", number), path("value")); + } + + public static String toIdType(String base, int number) { + return join(".", base, arrayPath("identifiers", number), path("type")); + } + + public static String toPublicationName(String base, int number) { + return join(".", base, arrayPath("publications", number), path("name")); + } + + public static String toPublicationDate(String base, int number) { + return join(".", base, arrayPath("publications", number), path("date")); + } + + public static String toEditionStatement(String base, int number) { + return join(".", base, arrayPath("editionStatements", number)); + } + + public static String toNote(String base, int number) { + return join(".", base, arrayPath("notes", number)); + } + + public static String toNoteValue(String base, int number) { + return join(".", toNote(base, number), path("value")); + } + + public static String toNoteType(String base, int number) { + return join(".", toNote(base, number), path("type")); + } + + public static String toFormat(String base) { + return join(".", base, path("format")); + } + + public static String toSuppressFromDiscovery(String base) { + return join(".", base, path("suppress"), path("fromDiscovery")); + } + + public static String toSuppressStaff(String base) { + return join(".", base, path("suppress"), path("staff")); + } + + public static String toTotalRecords() { + return join(".", "$", path("totalRecords")); + } + + public static String toParentWork() { + return join(".", toRootContent(), path("parentWork")); + } +} diff --git a/src/test/java/org/folio/search/utils/TestConstants.java b/src/test/java/org/folio/search/utils/TestConstants.java index 83ca7f3f1..585696e91 100644 --- a/src/test/java/org/folio/search/utils/TestConstants.java +++ b/src/test/java/org/folio/search/utils/TestConstants.java @@ -37,7 +37,8 @@ public class TestConstants { public static final String INVENTORY_BOUND_WITH_TOPIC = "inventory.bound-with"; public static final String INVENTORY_CLASSIFICATION_TYPE_TOPIC = "inventory.classification-type"; public static final String CONSORTIUM_INSTANCE_TOPIC = "search.consortium.instance"; - public static final String LINKED_DATA_TOPIC = "linked-data.work"; + public static final String LINKED_DATA_WORK_INSTANCE = "linked-data.instance"; + public static final String LINKED_DATA_WORK_TOPIC = "linked-data.work"; public static final String LINKED_DATA_AUTHORITY_TOPIC = "linked-data.authority"; public static final String CAMPUS_TOPIC = "inventory.campus"; public static final String INSTITUTION_TOPIC = "inventory.institution"; @@ -124,8 +125,12 @@ public static String inventoryBoundWithTopic(String tenantId) { return getTopicName(tenantId, INVENTORY_BOUND_WITH_TOPIC); } + public static String linkedDataInstanceTopic(String tenantId) { + return getTopicName(tenantId, LINKED_DATA_WORK_INSTANCE); + } + public static String linkedDataWorkTopic(String tenantId) { - return getTopicName(tenantId, LINKED_DATA_TOPIC); + return getTopicName(tenantId, LINKED_DATA_WORK_TOPIC); } public static String linkedDataAuthorityTopic(String tenantId) { diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index c01b97598..f929d47b8 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -111,6 +111,9 @@ folio: - name: inventory.location numPartitions: 1 replicationFactor: 1 + - name: linked-data.instance + numPartitions: 1 + replicationFactor: 1 - name: linked-data.work numPartitions: 1 replicationFactor: 1 @@ -157,7 +160,7 @@ folio: group-id: ${folio.environment}-mod-search-location-type-group linked-data: concurrency: 1 - topic-pattern: (${folio.environment}\.)(.*\.)linked-data\.(work|authority) + topic-pattern: (${folio.environment}\.)(.*\.)linked-data\.(instance|work|authority) group-id: ${folio.environment}-mod-search-linked-data-group okapiUrl: ${okapi.url} logging: diff --git a/src/test/resources/samples/linked-data/instance.json b/src/test/resources/samples/linked-data/instance.json new file mode 100644 index 000000000..2292708ab --- /dev/null +++ b/src/test/resources/samples/linked-data/instance.json @@ -0,0 +1,146 @@ +{ + "id": "instance1", + "contributors": [ + { + "name": "Instance1_Family", + "type": "Family", + "isCreator": true + }, + { + "name": "Instance1_Meeting", + "type": "Meeting", + "isCreator": false + }, + { + "name": "Instance1_Organization", + "type": "Organization", + "isCreator": true + }, + { + "name": "Instance1_Person", + "type": "Person", + "isCreator": false + } + ], + "editionStatements": [ + "1st edition", + "2nd edition" + ], + "format": "Monograph", + "identifiers": [ + { + "value": "1234567890123", + "type": "ISBN" + }, + { + "value": " 2023-202345/AC/r932", + "type": "LCCN" + } + ], + "notes": [ + { + "value": "first instance note", + "type": "firstInstanceNoteType" + }, + { + "value": "second instance note", + "type": "secondInstanceNoteType" + } + ], + "publications": [ + { + "name": "publisher", + "date": "2023" + }, + { + "name": "publisher2", + "date": 2024 + } + ], + "suppress": { + "fromDiscovery": false, + "staff": true + }, + "titles": [ + { + "value": "titleAbc def", + "type": "Main" + }, + { + "value": "sub", + "type": "Sub" + } + ], + "parentWork": { + "id": "123456123456", + "classifications": [ + { + "number": "1234", + "source": "ddc" + }, + { + "number": "5678", + "source": "other" + } + ], + "contributors": [ + { + "name": "Family", + "type": "Family", + "isCreator": true + }, + { + "name": "Meeting", + "type": "Meeting", + "isCreator": false + }, + { + "name": "Organization", + "type": "Organization", + "isCreator": true + }, + { + "name": "Person", + "type": "Person", + "isCreator": false + }, + { + "name": "common", + "type": "Family", + "isCreator": true + } + ], + "hubAAPs": [ + "hubAAP1", + "hubAAP2" + ], + "languages": [ + "eng", + "rus" + ], + "notes": [ + { + "value": "first work note", + "type": "firstWorkNoteType" + }, + { + "value": "second work note", + "type": "secondWorkNoteType" + } + ], + "subjects": [ + "Subject 1", + "Subject 2" + ], + "titles": [ + { + "value": "WorkTitle", + "type": "Main" + }, + { + "value": "sub", + "type": "Sub" + } + ] + } +} diff --git a/src/test/resources/samples/linked-data/instance2.json b/src/test/resources/samples/linked-data/instance2.json new file mode 100644 index 000000000..1c421468f --- /dev/null +++ b/src/test/resources/samples/linked-data/instance2.json @@ -0,0 +1,40 @@ +{ + "id": "999999999999", + "contributors": [ + { + "name": "name one", + "type": "Family", + "isCreator": true + }, + { + "name": "name two", + "type": "Meeting", + "isCreator": false + }, + { + "name": "name three", + "type": "Organization", + "isCreator": true + }, + { + "name": "name four", + "type": "Person", + "isCreator": false + }, + { + "name": "common", + "type": "Family", + "isCreator": true + } + ], + "titles": [ + { + "value": "titleAbc xyz", + "type": "Main" + }, + { + "value": "sub", + "type": "Sub" + } + ] +} diff --git a/src/test/resources/samples/linked-data/work.json b/src/test/resources/samples/linked-data/work.json index 96a1faa67..378cc0d9e 100644 --- a/src/test/resources/samples/linked-data/work.json +++ b/src/test/resources/samples/linked-data/work.json @@ -1,13 +1,13 @@ { "id": "123456123456", - "titles": [ + "classifications": [ { - "value": "titleAbc def", - "type": "Main" + "number": "1234", + "source": "ddc" }, { - "value": "sub", - "type": "Sub" + "number": "5678", + "source": "other" } ], "contributors": [ @@ -37,55 +37,41 @@ "isCreator": true } ], + "hubAAPs": [ + "hubAAP1", + "hubAAP2" + ], "languages": [ - { - "value": "eng" - }, - { - "value": "rus" - } + "eng", + "rus" ], - "classifications": [ + "notes": [ { - "number": "1234", - "source": "ddc" + "value": "first work note", + "type": "firstWorkNoteType" }, { - "number": "5678", - "source": "other" + "value": "second work note", + "type": "secondWorkNoteType" } ], "subjects": [ + "Subject 1", + "Subject 2" + ], + "titles": [ { - "value": "Subject 1" + "value": "titleAbc def", + "type": "Main" }, { - "value": "Subject 2" + "value": "sub", + "type": "Sub" } ], "instances": [ { "id": "instance1", - "titles": [ - { - "value": "Instance1_Title", - "type": "Main" - }, - { - "value": "Instance1_Subtitle", - "type": "Sub" - } - ], - "identifiers": [ - { - "value": "1234567890123", - "type": "ISBN" - }, - { - "value": " 2023-202345/AC/r932", - "type": "LCCN" - } - ], "contributors": [ { "name": "Instance1_Family", @@ -108,22 +94,53 @@ "isCreator": false } ], + "editionStatements": [ + "1st edition", + "2nd edition" + ], + "format": "Monograph", + "identifiers": [ + { + "value": "1234567890123", + "type": "ISBN" + }, + { + "value": " 2023-202345/AC/r932", + "type": "LCCN" + } + ], + "notes": [ + { + "value": "first instance note", + "type": "firstInstanceNoteType" + }, + { + "value": "second instance note", + "type": "secondInstanceNoteType" + } + ], "publications": [ { "name": "publisher", - "date": 2023 + "date": "2023" }, { "name": "publisher2", "date": 2024 } ], - "editionStatements": [ + "suppress": { + "fromDiscovery": false, + "staff": true + }, + "titles": [ { - "value": "1st edition" + "value": "Instance1_Title", + "type": "Main" }, { - "value": "2nd edition" + "value": "Instance1_Subtitle", + "type": "Sub" } ] } diff --git a/src/test/resources/samples/linked-data/work2.json b/src/test/resources/samples/linked-data/work2.json index f1f7a802f..1c421468f 100644 --- a/src/test/resources/samples/linked-data/work2.json +++ b/src/test/resources/samples/linked-data/work2.json @@ -1,15 +1,5 @@ { "id": "999999999999", - "titles": [ - { - "value": "titleAbc xyz", - "type": "Main" - }, - { - "value": "sub", - "type": "Sub" - } - ], "contributors": [ { "name": "name one", @@ -36,5 +26,15 @@ "type": "Family", "isCreator": true } + ], + "titles": [ + { + "value": "titleAbc xyz", + "type": "Main" + }, + { + "value": "sub", + "type": "Sub" + } ] }