diff --git a/.gitignore b/.gitignore index 33589c6..fb08ff7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,42 +1,56 @@ # Created by .ignore support plugin (hsz.mobi) -### JetBrains template -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm - -*.DS_Store - -*.iml - -## Directory-based project format: -.idea/ -# if you remove the above rule, at least ignore the following: +.eunit +deps +*.o +*.beam +*.plt +erl_crash.dump +ebin/*.beam +rel/example_project +.concrete/DEV_MODE +.rebar +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff: -# .idea/workspace.xml -# .idea/tasks.xml -# .idea/dictionaries +.idea/ +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml # Sensitive or high-churn files: -# .idea/dataSources.ids -# .idea/dataSources.xml -# .idea/sqlDataSources.xml -# .idea/dynamic.xml -# .idea/uiDesigner.xml +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml # Gradle: -# .idea/gradle.xml -# .idea/libraries +.idea/gradle.xml +.idea/libraries # Mongo Explorer plugin: -# .idea/mongoSettings.xml +.idea/mongoSettings.xml -## File-based project format: -*.ipr *.iws +*.ipr +*.iml -## Plugin-specific files: # IntelliJ -out/ +/out/ # mpeltonen/sbt-idea plugin .idea_modules/ @@ -48,9 +62,17 @@ atlassian-ide-plugin.xml com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties +fabric.properties +*.class -# Target folder -target +# Mobile Tools for Java (J2ME) +.mtj.tmp/ -# Target ant folder -build +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +env.list diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ca5a761 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "build_utils"] + path = build_utils + url = git@github.com:rbkmoney/build_utils.git + branch = master diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..fff612e --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,16 @@ +#!groovy +build('wb-list-manager', 'java-maven') { + checkoutRepo() + loadBuildUtils() + + def javaServicePipeline + runStage('load JavaService pipeline') { + javaServicePipeline = load("build_utils/jenkins_lib/pipeJavaService.groovy") + } + + def serviceName = env.REPO_NAME + def mvnArgs = '-DjvmArgs="-Xmx256m"' + def useJava11 = true + + javaServicePipeline(serviceName, useJava11, mvnArgs) +} \ No newline at end of file diff --git a/build_utils b/build_utils new file mode 160000 index 0000000..269686d --- /dev/null +++ b/build_utils @@ -0,0 +1 @@ +Subproject commit 269686d735abef363f9f40a1bf4e1b7c751f3722 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6d14eb4 --- /dev/null +++ b/pom.xml @@ -0,0 +1,190 @@ + + + 4.0.0 + + + com.rbkmoney + spring-boot-starter-parent + 2.1.1.RELEASE + + + wb-list-manager + 0.0.1-SNAPSHOT + jar + + wb-list-manager + + + UTF-8 + UTF-8 + 11 + 8022 + ${server.port} + 81f57f9ef5e5854c27659c9f56f59cdb2b7c43e5 + 0.3.2 + + + + + com.rbkmoney + shared-resources + ${shared.resources.version} + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-actuator + + + javax.servlet + javax.servlet-api + 4.0.1 + + + com.rbkmoney.woody + woody-thrift + 1.1.15 + + + com.rbkmoney + spring-boot-starter-metrics-statsd + 1.1.0 + + + org.projectlombok + lombok + 1.18.4 + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + org.testcontainers + testcontainers + 1.10.5 + test + + + + com.basho.riak + riak-client + 2.1.1 + + + + + org.apache.kafka + kafka-clients + 2.1.0 + + + org.springframework.kafka + spring-kafka + 2.2.2.RELEASE + + + org.apache.kafka + kafka-clients + + + + + org.testcontainers + kafka + 1.10.2 + test + + + + com.rbkmoney + wb-list-proto + 1.8-36aed97 + + + + + + + ${project.build.directory}/maven-shared-archive-resources + ${project.build.directory} + + Dockerfile + + true + + + ${project.build.directory}/maven-shared-archive-resources + true + + Dockerfile + + + + src/main/resources + true + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.1.1.RELEASE + + + org.apache.maven.plugins + maven-remote-resources-plugin + 1.6.0 + + + org.apache.maven.shared + maven-filtering + 1.3 + + + + + com.rbkmoney:shared-resources:${shared.resources.version} + + false + false + + + + + process + + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.2 + + ${sonar.jacoco.reportPath} + true + + + + agent + + prepare-agent + + + + + + + diff --git a/src/main/java/com/rbkmoney/wb/list/manager/WbListManagerApplication.java b/src/main/java/com/rbkmoney/wb/list/manager/WbListManagerApplication.java new file mode 100644 index 0000000..53e972f --- /dev/null +++ b/src/main/java/com/rbkmoney/wb/list/manager/WbListManagerApplication.java @@ -0,0 +1,15 @@ +package com.rbkmoney.wb.list.manager; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletComponentScan; + +@ServletComponentScan +@SpringBootApplication +public class WbListManagerApplication extends SpringApplication { + + public static void main(String[] args) { + SpringApplication.run(WbListManagerApplication.class); + } + +} diff --git a/src/main/java/com/rbkmoney/wb/list/manager/config/KafkaConfig.java b/src/main/java/com/rbkmoney/wb/list/manager/config/KafkaConfig.java new file mode 100644 index 0000000..97b32f3 --- /dev/null +++ b/src/main/java/com/rbkmoney/wb/list/manager/config/KafkaConfig.java @@ -0,0 +1,48 @@ +package com.rbkmoney.wb.list.manager.config; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.config.KafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; +import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class KafkaConfig { + + private static final String GROUP_ID = "CommandListener"; + private static final String EARLIEST = "earliest"; + + @Value("${kafka.bootstrap.servers}") + private String bootstrapServers; + + @Bean + public Map consumerConfigs() { + Map props = new HashMap<>(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.GROUP_ID_CONFIG, GROUP_ID); + props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, EARLIEST); + return props; + } + + @Bean + public ConsumerFactory consumerFactory() { + return new DefaultKafkaConsumerFactory<>(consumerConfigs()); + } + + @Bean + public KafkaListenerContainerFactory> kafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory()); + return factory; + } +} diff --git a/src/main/java/com/rbkmoney/wb/list/manager/config/ResourceConfig.java b/src/main/java/com/rbkmoney/wb/list/manager/config/ResourceConfig.java new file mode 100644 index 0000000..d256265 --- /dev/null +++ b/src/main/java/com/rbkmoney/wb/list/manager/config/ResourceConfig.java @@ -0,0 +1,17 @@ +package com.rbkmoney.wb.list.manager.config; + +import com.rbkmoney.damsel.wb_list.WbListServiceSrv; +import com.rbkmoney.wb.list.manager.handler.WbListServiceHandler; +import com.rbkmoney.wb.list.manager.repository.ListRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ResourceConfig { + + @Bean + public WbListServiceSrv.Iface fraudInspectorHandler(ListRepository listRepository) { + return new WbListServiceHandler(listRepository); + } + +} diff --git a/src/main/java/com/rbkmoney/wb/list/manager/config/RiakConfig.java b/src/main/java/com/rbkmoney/wb/list/manager/config/RiakConfig.java new file mode 100644 index 0000000..1947f06 --- /dev/null +++ b/src/main/java/com/rbkmoney/wb/list/manager/config/RiakConfig.java @@ -0,0 +1,35 @@ +package com.rbkmoney.wb.list.manager.config; + +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.core.RiakCluster; +import com.basho.riak.client.core.RiakNode; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RiakConfig { + + @Value("${riak.port}") + public int riakPort; + + @Value("${riak.address}") + public String riakAddress; + + @Bean + public RiakCluster riakCluster() { + RiakNode node = new RiakNode.Builder() + .withRemoteAddress(riakAddress) + .withRemotePort(riakPort) + .build(); + RiakCluster cluster = new RiakCluster.Builder(node) + .build(); + cluster.start(); + return cluster; + } + + @Bean + public RiakClient riakClient() { + return new RiakClient(riakCluster()); + } +} diff --git a/src/main/java/com/rbkmoney/wb/list/manager/exception/RiakExecutionException.java b/src/main/java/com/rbkmoney/wb/list/manager/exception/RiakExecutionException.java new file mode 100644 index 0000000..1652cde --- /dev/null +++ b/src/main/java/com/rbkmoney/wb/list/manager/exception/RiakExecutionException.java @@ -0,0 +1,23 @@ +package com.rbkmoney.wb.list.manager.exception; + +public class RiakExecutionException extends RuntimeException { + + public RiakExecutionException() { + } + + public RiakExecutionException(String message) { + super(message); + } + + public RiakExecutionException(String message, Throwable cause) { + super(message, cause); + } + + public RiakExecutionException(Throwable cause) { + super(cause); + } + + public RiakExecutionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/src/main/java/com/rbkmoney/wb/list/manager/handler/WbListServiceHandler.java b/src/main/java/com/rbkmoney/wb/list/manager/handler/WbListServiceHandler.java new file mode 100644 index 0000000..3700eb2 --- /dev/null +++ b/src/main/java/com/rbkmoney/wb/list/manager/handler/WbListServiceHandler.java @@ -0,0 +1,29 @@ +package com.rbkmoney.wb.list.manager.handler; + +import com.rbkmoney.damsel.wb_list.ListNotFound; +import com.rbkmoney.damsel.wb_list.WbListServiceSrv; +import com.rbkmoney.wb.list.manager.exception.RiakExecutionException; +import com.rbkmoney.wb.list.manager.repository.ListRepository; +import com.rbkmoney.wb.list.manager.utils.KeyGenerator; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.thrift.TException; + +@Slf4j +@RequiredArgsConstructor +public class WbListServiceHandler implements WbListServiceSrv.Iface { + + private final ListRepository listRepository; + + @Override + public boolean isExist(String partyId, String shopId, String listName, String value) throws ListNotFound, TException { + String bucket = KeyGenerator.generateBucket(partyId, shopId); + String key = KeyGenerator.generateKey(listName, value); + try { + return listRepository.get(bucket, key).isPresent(); + } catch (RiakExecutionException e) { + throw new TException(e); + } + } + +} diff --git a/src/main/java/com/rbkmoney/wb/list/manager/listener/WbListListener.java b/src/main/java/com/rbkmoney/wb/list/manager/listener/WbListListener.java new file mode 100644 index 0000000..460ed22 --- /dev/null +++ b/src/main/java/com/rbkmoney/wb/list/manager/listener/WbListListener.java @@ -0,0 +1,22 @@ +package com.rbkmoney.wb.list.manager.listener; + +import com.rbkmoney.wb.list.manager.model.Row; +import com.rbkmoney.wb.list.manager.repository.ListRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class WbListListener { + + private final ListRepository listRepository; + + @KafkaListener(topics = "${kafka.wblist.topic}", containerFactory = "kafkaListenerContainerFactory") + public void listen(Row row) { + log.info("TemplateListener ruleTemplate: {}", row); + listRepository.create(row); + } +} diff --git a/src/main/java/com/rbkmoney/wb/list/manager/model/Row.java b/src/main/java/com/rbkmoney/wb/list/manager/model/Row.java new file mode 100644 index 0000000..e195cd9 --- /dev/null +++ b/src/main/java/com/rbkmoney/wb/list/manager/model/Row.java @@ -0,0 +1,17 @@ +package com.rbkmoney.wb.list.manager.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Row { + + private String bucketName; + private String key; + + private String value; + +} diff --git a/src/main/java/com/rbkmoney/wb/list/manager/repository/CrudRepository.java b/src/main/java/com/rbkmoney/wb/list/manager/repository/CrudRepository.java new file mode 100644 index 0000000..73dde12 --- /dev/null +++ b/src/main/java/com/rbkmoney/wb/list/manager/repository/CrudRepository.java @@ -0,0 +1,15 @@ +package com.rbkmoney.wb.list.manager.repository; + +import com.rbkmoney.wb.list.manager.model.Row; + +import java.util.Optional; + +public interface CrudRepository { + + void create(T row); + + void remove(T row); + + Optional get(String bucket, K key); + +} diff --git a/src/main/java/com/rbkmoney/wb/list/manager/repository/ListRepository.java b/src/main/java/com/rbkmoney/wb/list/manager/repository/ListRepository.java new file mode 100644 index 0000000..1a5139f --- /dev/null +++ b/src/main/java/com/rbkmoney/wb/list/manager/repository/ListRepository.java @@ -0,0 +1,91 @@ +package com.rbkmoney.wb.list.manager.repository; + +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.cap.Quorum; +import com.basho.riak.client.api.commands.kv.DeleteValue; +import com.basho.riak.client.api.commands.kv.FetchValue; +import com.basho.riak.client.api.commands.kv.StoreValue; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.basho.riak.client.core.util.BinaryValue; +import com.rbkmoney.wb.list.manager.exception.RiakExecutionException; +import com.rbkmoney.wb.list.manager.model.Row; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Optional; +import java.util.concurrent.ExecutionException; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ListRepository implements CrudRepository { + + private static final String TEXT_PLAIN = "text/plain"; + private final RiakClient client; + + @Override + public void create(Row row) { + try { + RiakObject quoteObject = new RiakObject() + .setContentType(TEXT_PLAIN) + .setValue(BinaryValue.create(row.getValue())); + Location quoteObjectLocation = createLocation(row.getBucketName(), row.getKey()); + StoreValue storeOp = new StoreValue.Builder(quoteObject) + .withOption(StoreValue.Option.W, Quorum.oneQuorum()) + .withLocation(quoteObjectLocation) + .build(); + client.execute(storeOp); + } catch (ExecutionException e) { + log.error("Exception in ListRepository when create e: ", e); + throw new RiakExecutionException(); + } catch (InterruptedException e) { + log.error("InterruptedException in ListRepository when get e: ", e); + Thread.currentThread().interrupt(); + throw new RiakExecutionException(e); + } + } + + @Override + public void remove(Row row) { + try { + Location quoteObjectLocation = createLocation(row.getBucketName(), row.getKey()); + DeleteValue delete = new DeleteValue.Builder(quoteObjectLocation).build(); + client.execute(delete); + } catch (ExecutionException e) { + log.error("Exception in ListRepository when remove e: ", e); + throw new RiakExecutionException(e); + } catch (InterruptedException e) { + log.error("InterruptedException in ListRepository when get e: ", e); + Thread.currentThread().interrupt(); + throw new RiakExecutionException(e); + } + } + + @Override + public Optional get(String bucket, String key) { + try { + Location quoteObjectLocation = createLocation(bucket, key); + FetchValue fetch = new FetchValue.Builder(quoteObjectLocation) + .withOption(FetchValue.Option.R, new Quorum(3)) + .build(); + FetchValue.Response response = client.execute(fetch); + RiakObject obj = response.getValue(RiakObject.class); + return Optional.of(new Row(bucket, key, obj.getValue().toString())); + } catch (ExecutionException e) { + log.error("Exception in ListRepository when get e: ", e); + throw new RiakExecutionException(e); + } catch (InterruptedException e) { + log.error("InterruptedException in ListRepository when get e: ", e); + Thread.currentThread().interrupt(); + throw new RiakExecutionException(e); + } + } + + private Location createLocation(String bucketName, String key) { + Namespace quotesBucket = new Namespace(bucketName); + return new Location(quotesBucket, key); + } +} diff --git a/src/main/java/com/rbkmoney/wb/list/manager/resource/FraudInspectorServlet.java b/src/main/java/com/rbkmoney/wb/list/manager/resource/FraudInspectorServlet.java new file mode 100644 index 0000000..acdfe0e --- /dev/null +++ b/src/main/java/com/rbkmoney/wb/list/manager/resource/FraudInspectorServlet.java @@ -0,0 +1,30 @@ +package com.rbkmoney.wb.list.manager.resource; + +import com.rbkmoney.damsel.wb_list.WbListServiceSrv; +import com.rbkmoney.woody.thrift.impl.http.THServiceBuilder; +import lombok.RequiredArgsConstructor; + +import javax.servlet.*; +import javax.servlet.annotation.WebServlet; +import java.io.IOException; + +@WebServlet("/v1/wb_list") +@RequiredArgsConstructor +public class FraudInspectorServlet extends GenericServlet { + + private Servlet thriftServlet; + + private final WbListServiceSrv.Iface fraudInspectorHandler; + + @Override + public void init(ServletConfig config) throws ServletException { + super.init(config); + thriftServlet = new THServiceBuilder() + .build(WbListServiceSrv.Iface.class, fraudInspectorHandler); + } + + @Override + public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { + thriftServlet.service(req, res); + } +} diff --git a/src/main/java/com/rbkmoney/wb/list/manager/serializer/JsonPOJODeserializer.java b/src/main/java/com/rbkmoney/wb/list/manager/serializer/JsonPOJODeserializer.java new file mode 100644 index 0000000..12ad0bb --- /dev/null +++ b/src/main/java/com/rbkmoney/wb/list/manager/serializer/JsonPOJODeserializer.java @@ -0,0 +1,46 @@ +package com.rbkmoney.wb.list.manager.serializer; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.kafka.common.errors.SerializationException; +import org.apache.kafka.common.serialization.Deserializer; + +import java.util.Map; + +public class JsonPOJODeserializer implements Deserializer { + private ObjectMapper objectMapper = new ObjectMapper(); + + private Class tClass; + + /** + * Default constructor needed by Kafka + */ + public JsonPOJODeserializer() { + } + + @SuppressWarnings("unchecked") + @Override + public void configure(Map props, boolean isKey) { + tClass = (Class) props.get("JsonPOJOClass"); + } + + @Override + public T deserialize(String topic, byte[] bytes) { + if (bytes == null) + return null; + + T data; + try { + data = objectMapper.readValue(bytes, tClass); + } catch (Exception e) { + throw new SerializationException(e); + } + + return data; + } + + @Override + public void close() { + + } +} \ No newline at end of file diff --git a/src/main/java/com/rbkmoney/wb/list/manager/serializer/JsonPOJOSerializer.java b/src/main/java/com/rbkmoney/wb/list/manager/serializer/JsonPOJOSerializer.java new file mode 100644 index 0000000..42b9c08 --- /dev/null +++ b/src/main/java/com/rbkmoney/wb/list/manager/serializer/JsonPOJOSerializer.java @@ -0,0 +1,38 @@ +package com.rbkmoney.wb.list.manager.serializer; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.kafka.common.errors.SerializationException; +import org.apache.kafka.common.serialization.Serializer; + +import java.util.Map; + +public class JsonPOJOSerializer implements Serializer { + private final ObjectMapper objectMapper = new ObjectMapper(); + + /** + * Default constructor needed by Kafka + */ + public JsonPOJOSerializer() { + } + + @Override + public void configure(Map props, boolean isKey) { + } + + @Override + public byte[] serialize(String topic, T data) { + if (data == null) + return null; + + try { + return objectMapper.writeValueAsBytes(data); + } catch (Exception e) { + throw new SerializationException("Error serializing JSON message", e); + } + } + + @Override + public void close() { + } + +} diff --git a/src/main/java/com/rbkmoney/wb/list/manager/utils/KeyGenerator.java b/src/main/java/com/rbkmoney/wb/list/manager/utils/KeyGenerator.java new file mode 100644 index 0000000..a3aba17 --- /dev/null +++ b/src/main/java/com/rbkmoney/wb/list/manager/utils/KeyGenerator.java @@ -0,0 +1,19 @@ +package com.rbkmoney.wb.list.manager.utils; + +public class KeyGenerator { + + public static final String DELIMITER = "_"; + + public static String generateBucket(String partyId, String shopId) { + return generateKeySecondField(partyId, shopId); + } + + public static String generateKey(String type, String key) { + return generateKeySecondField(type, key); + } + + private static String generateKeySecondField(String first, String second) { + return first + DELIMITER + second; + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..de92a89 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,16 @@ +server.port: @server.port@ +management.security.flag: false +spring.application.name: @project.name@ +info.version: @project.version@ +info.stage: dev + +management.metrics.export.statsd: + flavor: etsy + +riak: + address: localhost + port: 8087 + +kafka: + bootstrap.servers: "localhost:9092" + wblist.topic: "wblist" \ No newline at end of file diff --git a/src/test/java/com/rbkmoney/wb/list/manager/KafkaAbstractTest.java b/src/test/java/com/rbkmoney/wb/list/manager/KafkaAbstractTest.java new file mode 100644 index 0000000..5f1ce2b --- /dev/null +++ b/src/test/java/com/rbkmoney/wb/list/manager/KafkaAbstractTest.java @@ -0,0 +1,34 @@ +package com.rbkmoney.wb.list.manager; + +import org.junit.ClassRule; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import org.testcontainers.containers.KafkaContainer; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +@ContextConfiguration(initializers = KafkaAbstractTest.Initializer.class) +public abstract class KafkaAbstractTest { + + @ClassRule + public static KafkaContainer kafka = new KafkaContainer("5.0.1").withEmbeddedZookeeper(); + + public static class Initializer implements ApplicationContextInitializer { + @Override + public void initialize(ConfigurableApplicationContext configurableApplicationContext) { + TestPropertyValues + .of("kafka.bootstrap.servers=" + kafka.getBootstrapServers()) + .applyTo(configurableApplicationContext.getEnvironment()); + } + } + +} diff --git a/src/test/java/com/rbkmoney/wb/list/manager/WbListManagerApplicationTest.java b/src/test/java/com/rbkmoney/wb/list/manager/WbListManagerApplicationTest.java new file mode 100644 index 0000000..50f23dc --- /dev/null +++ b/src/test/java/com/rbkmoney/wb/list/manager/WbListManagerApplicationTest.java @@ -0,0 +1,96 @@ +package com.rbkmoney.wb.list.manager; + +import com.basho.riak.client.api.RiakClient; +import com.basho.riak.client.api.commands.kv.FetchValue; +import com.basho.riak.client.core.query.Location; +import com.basho.riak.client.core.query.Namespace; +import com.basho.riak.client.core.query.RiakObject; +import com.rbkmoney.wb.list.manager.config.KafkaConfig; +import com.rbkmoney.wb.list.manager.model.Row; +import com.rbkmoney.wb.list.manager.repository.ListRepository; +import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.FilterType; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.WaitAllStrategy; + +import java.util.Optional; +import java.util.concurrent.ExecutionException; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@ContextConfiguration(classes = WbListManagerApplication.class, initializers = WbListManagerApplicationTest.Initializer.class) +public class WbListManagerApplicationTest extends KafkaAbstractTest { + + public static final String TYPE = "type"; + public static final String BUCKET_NAME = "bucketName"; + public static final String VALUE = "value"; + public static final String KEY = "key"; + + @Autowired + private ListRepository listRepository; + @Autowired + private RiakClient client; + + @ClassRule + public static GenericContainer riak = new GenericContainer("basho/riak-kv") + .waitingFor(new WaitAllStrategy()); + + public static class Initializer implements ApplicationContextInitializer { + @Override + public void initialize(ConfigurableApplicationContext configurableApplicationContext) { + TestPropertyValues + .of("riak.port=" + riak.getMappedPort(8087)) + .applyTo(configurableApplicationContext.getEnvironment()); + } + } + + @Before + public void init() throws InterruptedException { + riak.start(); + // TODO add cycle for up check + Thread.sleep(8000L); + } + + @Test + public void contextLoads() throws ExecutionException, InterruptedException { + Row row = new Row(); + row.setKey(KEY); + row.setBucketName(BUCKET_NAME); + row.setValue(VALUE); + listRepository.create(row); + + Namespace ns = new Namespace(BUCKET_NAME); + Location location = new Location(ns, KEY); + FetchValue fv = new FetchValue.Builder(location).build(); + FetchValue.Response response = client.execute(fv); + RiakObject obj = response.getValue(RiakObject.class); + + String result = obj.getValue().toString(); + Assert.assertEquals(VALUE, result); + + Optional resultGet = listRepository.get(BUCKET_NAME, KEY); + Assert.assertFalse(resultGet.isEmpty()); + Assert.assertEquals(VALUE, resultGet.get().getValue()); + + listRepository.remove(row); + response = client.execute(fv); + obj = response.getValue(RiakObject.class); + Assert.assertNull(obj); + } +} diff --git a/src/test/java/com/rbkmoney/wb/list/manager/handler/WbListServiceHandlerTest.java b/src/test/java/com/rbkmoney/wb/list/manager/handler/WbListServiceHandlerTest.java new file mode 100644 index 0000000..a17b69a --- /dev/null +++ b/src/test/java/com/rbkmoney/wb/list/manager/handler/WbListServiceHandlerTest.java @@ -0,0 +1,43 @@ +package com.rbkmoney.wb.list.manager.handler; + +import com.rbkmoney.wb.list.manager.model.Row; +import com.rbkmoney.wb.list.manager.repository.ListRepository; +import org.apache.thrift.TException; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.anyString; + +public class WbListServiceHandlerTest { + + public static final String VALUE = "value"; + WbListServiceHandler wbListServiceHandler; + + @Mock + ListRepository listRepository; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + wbListServiceHandler = new WbListServiceHandler(listRepository); + } + + @Test + public void isExist() throws TException { + Row value = new Row(); + value.setValue(VALUE); + Mockito.when(listRepository.get(anyString(), anyString())).thenReturn(Optional.of(value)); + boolean exist = wbListServiceHandler.isExist("partyId", "shopId", "listName", "value"); + Assert.assertTrue(exist); + + Mockito.when(listRepository.get(anyString(), anyString())).thenReturn(Optional.empty()); + exist = wbListServiceHandler.isExist("partyId", "shopId", "listName", "value"); + Assert.assertFalse(exist); + } +} \ No newline at end of file diff --git a/src/test/resources/app/start.sh b/src/test/resources/app/start.sh new file mode 100644 index 0000000..39cb450 --- /dev/null +++ b/src/test/resources/app/start.sh @@ -0,0 +1,2 @@ +riak-admin bucket-type create type +riak-admin bucket-type activate type \ No newline at end of file diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml new file mode 100644 index 0000000..e1f57ed --- /dev/null +++ b/src/test/resources/logback-test.xml @@ -0,0 +1,10 @@ + + + + + + + + + +