Skip to content

Commit

Permalink
MSEARCH-320 Implement browsing by instance contributors (#256)
Browse files Browse the repository at this point in the history
  • Loading branch information
psmagin authored May 20, 2022
1 parent e43e94a commit 01b257f
Show file tree
Hide file tree
Showing 45 changed files with 1,154 additions and 94 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ build/

### VS Code ###
.vscode/

### Development
application-local.yml
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ does not produce any values, so the following search options will return an empt
Supported browsing values

* subject (`${okapi}/browse/subjects/instances`)
* contributor (`${okapi}/browse/contributors/instances`)
* callNumber (`${okapi}/browse/call-numbers/instances`, approach: [call number browsing](doc/browsing.md#call-number-browsing))

**Query parameters**
Expand Down
12 changes: 11 additions & 1 deletion descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
},
{
"id": "browse",
"version": "0.3",
"version": "0.4",
"handlers": [
{
"methods": [ "GET" ],
Expand All @@ -103,6 +103,11 @@
"pathPattern": "/browse/subjects/instances",
"permissionsRequired": [ "browse.subjects.instances.collection.get" ]
},
{
"methods": [ "GET" ],
"pathPattern": "/browse/contributors/instances",
"permissionsRequired": [ "browse.contributors.instances.collection.get" ]
},
{
"methods": [ "GET" ],
"pathPattern": "/browse/authorities",
Expand Down Expand Up @@ -248,6 +253,11 @@
"displayName": "Browse - provides collections of browse items for instance by subjects",
"description": "Browse instances by given query"
},
{
"permissionName": "browse.contributors.instances.collection.get",
"displayName": "Browse - provides collections of browse items for instance by contributors",
"description": "Browse instances by given query"
},
{
"permissionName": "browse.authorities.collection.get",
"displayName": "Browse - provides collections of browse items for authorities",
Expand Down
1 change: 1 addition & 0 deletions docker/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
COMPOSE_PROJECT_NAME=folio-mod-search
23 changes: 18 additions & 5 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ services:
mod-search:
container_name: mod-search
image: folioci/mod-search:latest
networks:
- mod-search-local
ports:
- "8081:8081"
environment:
Expand All @@ -14,19 +16,23 @@ services:
api-mock:
image: wiremock/wiremock:2.32.0
container_name: api-mock
networks:
- mod-search-local
command:
- "--verbose"
ports:
- "9130:8080"
- "9130:8080"
volumes:
- ../src/test/resources/mappings:/home/wiremock/mappings

elasticsearch:
container_name: elasticsearch
image: opendistro-elasticsearch-it:1.13.2
build:
context: elasticsearch
dockerfile: Dockerfile
context: ./elasticsearch
networks:
- mod-search-local
ports:
- "9200:9200"
- "9300:9300"
Expand All @@ -41,6 +47,8 @@ services:
zookeeper:
image: zookeeper:3.6.2
container_name: zookeeper
networks:
- mod-search-local
ports:
- "2181:2181"
- "8080:8080"
Expand All @@ -52,6 +60,8 @@ services:
image: wurstmeister/kafka:2.13-2.6.0
container_name: kafka
hostname: kafka
networks:
- mod-search-local
depends_on:
- zookeeper
ports:
Expand All @@ -66,20 +76,23 @@ services:
KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE
KAFKA_MESSAGE_MAX_BYTES: 1000000
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "false"
MIN_INSYNC_REPLICAS: 2
KAFKA_CREATE_TOPICS: >-
inventory.instance:4:1

db:
container_name: db
image: postgres:13.4-alpine
networks:
- mod-search-local
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: okapi_modules

networks:
mod-search-local:
driver: bridge

volumes:
es-data: { }
db-data: { }
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<version>2.6.7</version>
<relativePath />
</parent>

Expand All @@ -25,7 +25,7 @@

<properties>
<java.version>11</java.version>
<folio-spring-base.version>3.0.1</folio-spring-base.version>
<folio-spring-base.version>3.0.3</folio-spring-base.version>
<openapi-generator.version>5.3.1</openapi-generator.version>
<es-client.version>7.10.2</es-client.version>
<mapstruct.version>1.4.2.Final</mapstruct.version>
Expand All @@ -44,7 +44,7 @@
<lombok.mapstruct-binding.version>0.2.0</lombok.mapstruct-binding.version>
<testcontainer.version>1.16.3</testcontainer.version>
<wiremock.version>2.27.2</wiremock.version>
<log4j2.version>2.17.1</log4j2.version>
<log4j2.version>2.17.2</log4j2.version>
</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,27 @@

import static org.apache.kafka.clients.consumer.ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG;
import static org.apache.kafka.clients.consumer.ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG;
import static org.apache.kafka.clients.producer.ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG;
import static org.apache.kafka.clients.producer.ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG;

import com.fasterxml.jackson.databind.JsonNode;
import java.util.HashMap;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;
import org.folio.search.domain.dto.ResourceEvent;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.core.ProducerFactory;
import org.springframework.kafka.support.serializer.JsonDeserializer;
import org.springframework.kafka.support.serializer.JsonSerializer;

/**
* Responsible for configuration of kafka consumer bean factories and creation of topics at at application startup for
Expand Down Expand Up @@ -46,15 +52,43 @@ public ConcurrentKafkaListenerContainerFactory<String, ResourceEvent> kafkaListe
/**
* Creates and configures {@link ConsumerFactory} as Spring bean.
*
* <p>Key type - {@link String}, value - {@link JsonNode}.</p>
* <p>Key type - {@link String}, value - {@link ResourceEvent}.</p>
*
* @return typed {@link ConsumerFactory} object as Spring bean.
*/
private ConsumerFactory<String, ResourceEvent> jsonNodeConsumerFactory() {
@Bean
public ConsumerFactory<String, ResourceEvent> jsonNodeConsumerFactory() {
var deserializer = new JsonDeserializer<>(ResourceEvent.class);
Map<String, Object> config = new HashMap<>(kafkaProperties.buildConsumerProperties());
config.put(KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
config.put(VALUE_DESERIALIZER_CLASS_CONFIG, deserializer);
return new DefaultKafkaConsumerFactory<>(config, new StringDeserializer(), deserializer);
}

/**
* Creates and configures {@link ProducerFactory} as Spring bean.
*
* <p>Key type - {@link String}, value - {@link Object}.</p>
*
* @return typed {@link ProducerFactory} object as Spring bean.
*/
@Bean
public ProducerFactory<String, Object> producerFactory() {
Map<String, Object> configProps = new HashMap<>(kafkaProperties.buildProducerProperties());
configProps.put(KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configProps.put(VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
return new DefaultKafkaProducerFactory<>(configProps);
}

/**
* Creates and configures {@link KafkaTemplate} as Spring bean.
*
* <p>Key type - {@link String}, value - {@link Object}.</p>
*
* @return typed {@link KafkaTemplate} object as Spring bean.
*/
@Bean
public KafkaTemplate<String, Object> kafkaTemplate(ProducerFactory<String, Object> producerFactory) {
return new KafkaTemplate<>(producerFactory);
}
}
20 changes: 20 additions & 0 deletions src/main/java/org/folio/search/controller/BrowseController.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,24 @@
import static org.folio.search.utils.SearchUtils.AUTHORITY_BROWSING_FIELD;
import static org.folio.search.utils.SearchUtils.AUTHORITY_RESOURCE;
import static org.folio.search.utils.SearchUtils.CALL_NUMBER_BROWSING_FIELD;
import static org.folio.search.utils.SearchUtils.CONTRIBUTOR_BROWSING_FIELD;
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.INSTANCE_SUBJECT_RESOURCE;
import static org.folio.search.utils.SearchUtils.SUBJECT_BROWSING_FIELD;

import lombok.RequiredArgsConstructor;
import org.folio.search.domain.dto.AuthorityBrowseResult;
import org.folio.search.domain.dto.CallNumberBrowseResult;
import org.folio.search.domain.dto.ContributorBrowseResult;
import org.folio.search.domain.dto.SubjectBrowseResult;
import org.folio.search.exception.RequestValidationException;
import org.folio.search.model.service.BrowseRequest;
import org.folio.search.model.service.BrowseRequest.BrowseRequestBuilder;
import org.folio.search.rest.resource.BrowseApi;
import org.folio.search.service.browse.AuthorityBrowseService;
import org.folio.search.service.browse.CallNumberBrowseService;
import org.folio.search.service.browse.ContributorBrowseService;
import org.folio.search.service.browse.SubjectBrowseService;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
Expand All @@ -31,6 +35,22 @@ public class BrowseController implements BrowseApi {
private final SubjectBrowseService subjectBrowseService;
private final AuthorityBrowseService authorityBrowseService;
private final CallNumberBrowseService callNumberBrowseService;
private final ContributorBrowseService contributorBrowseService;

@Override
public ResponseEntity<ContributorBrowseResult> browseInstancesByContributor(String query, String tenant,
Integer limit, Boolean highlightMatch,
Integer precedingRecordsCount) {
var browseRequest = getBrowseRequestBuilder(query, tenant, limit, null, highlightMatch, precedingRecordsCount)
.resource(CONTRIBUTOR_RESOURCE).targetField(CONTRIBUTOR_BROWSING_FIELD).build();

var browseResult = contributorBrowseService.browse(browseRequest);
return ResponseEntity.ok(new ContributorBrowseResult()
.items(browseResult.getRecords())
.totalRecords(browseResult.getTotalRecords())
.prev(browseResult.getPrev())
.next(browseResult.getNext()));
}

@Override
public ResponseEntity<CallNumberBrowseResult> browseInstancesByCallNumber(String query, String tenant,
Expand Down
Loading

0 comments on commit 01b257f

Please sign in to comment.