From 357c1b0bf124fa6a2ed34277e60d677bab031a41 Mon Sep 17 00:00:00 2001 From: clunven Date: Fri, 16 Aug 2019 14:49:31 +0200 Subject: [PATCH 01/31] commons and comments are OK --- killrvideo-commons/.factorypath | 156 ++----- killrvideo-commons/pom.xml | 143 ++---- .../conf/DseDriverConfiguration.java | 180 +++++++ .../killrvideo/conf/GrpcConfiguration.java | 25 + .../conf/KafkaConfiguration.java | 55 ++- .../conf/KillrVideoConfiguration.java | 55 --- .../MessagingGuavaConfiguration.java} | 8 +- .../discovery/ServiceDiscoveryDao.java | 55 --- .../discovery/ServiceDiscoveryDaoEtcd.java | 230 --------- .../discovery/ServiceDiscoveryDaoStatic.java | 93 ---- .../killrvideo/dse/conf/DseConfiguration.java | 360 -------------- .../killrvideo/dse/dao/AbstractDseDao.java | 51 ++ .../com/killrvideo/dse/dao/DseDaoSupport.java | 79 ---- .../com/killrvideo/dse/dao/DseSchema.java | 88 ++++ .../killrvideo/dse/dto/AbstractEntity.java | 38 -- .../com/killrvideo/dse/dto/AbstractVideo.java | 16 +- .../killrvideo/dse/dto/ResultListPage.java | 32 +- .../java/com/killrvideo/dse/dto/Video.java | 37 +- .../dse/graph/KillrVideoTraversalDsl.java | 1 - .../dse/utils/BlobToStringCodec.java | 46 -- .../com/killrvideo/dse/utils/DseUtils.java | 65 +-- .../dse/utils/EmptyCollectionIfNull.java | 58 --- .../grpc/AbstractSingleServiceGrpcServer.java | 32 +- .../{utils => grpc}/GrpcMappingUtils.java | 2 +- .../messaging/dao/MessagingDao.java | 2 +- .../messaging/dao/MessagingDaoInMemory.java | 3 +- .../messaging/dao/MessagingDaoKafka.java | 3 +- .../com/killrvideo/model/CommonConstants.java | 12 - .../KillrVideoThreadFactory.java | 2 +- .../src/main/resources/logback.xml | 4 + .../test/resources/killrvideo-test.properties | 12 - killrvideo-service-comments/.factorypath | 156 ++----- killrvideo-service-comments/pom.xml | 95 ++-- .../service/comment/dao/CommentDseDao.java | 442 +++--------------- .../comment/dao/CommentDseDaoMapper.java | 26 ++ .../dao/CommentDseDaoQueryProvider.java | 243 ++++++++++ .../comment/{ => dao}/dto/Comment.java | 42 +- .../dto/CommentByUserEntity.java} | 22 +- .../dto/CommentByVideoEntity.java} | 20 +- .../comment/grpc/CommentsServiceGrpc.java | 50 +- .../grpc/CommentsServiceGrpcMapper.java | 18 +- .../{ => grpc}/dto/QueryCommentByUser.java | 2 +- .../{ => grpc}/dto/QueryCommentByVideo.java | 2 +- .../comment/test/CommentDseDaoTest.java | 209 +++++++++ killrvideo-service-ratings/pom.xml | 53 +-- .../rating/grpc/RatingsServiceGrpc.java | 2 +- .../rating/grpc/RatingsServiceGrpcMapper.java | 2 +- killrvideo-service-search/pom.xml | 68 +-- .../search/grpc/SearchServiceGrpcMapper.java | 2 +- killrvideo-service-statistics/pom.xml | 67 +-- .../grpc/StatisticsServiceGrpcMapper.java | 2 +- killrvideo-service-sugestedvideo/pom.xml | 63 +-- .../SuggestedVideosMessagingDaoSupport.java | 2 +- .../grpc/SuggestedVideosServiceGrpc.java | 2 +- .../SuggestedVideosServiceGrpcMapper.java | 4 +- killrvideo-service-users/pom.xml | 83 +--- .../grpc/UserManagementServiceGrpcMapper.java | 2 +- killrvideo-service-videocatalog/pom.xml | 69 +-- .../video/grpc/VideoCatalogServiceGrpc.java | 2 +- .../grpc/VideoCatalogServiceGrpcMapper.java | 4 +- pom.xml | 362 ++++++-------- 61 files changed, 1374 insertions(+), 2685 deletions(-) create mode 100644 killrvideo-commons/src/main/java/com/killrvideo/conf/DseDriverConfiguration.java create mode 100644 killrvideo-commons/src/main/java/com/killrvideo/conf/GrpcConfiguration.java rename killrvideo-commons/src/main/java/com/killrvideo/{messaging => }/conf/KafkaConfiguration.java (76%) delete mode 100644 killrvideo-commons/src/main/java/com/killrvideo/conf/KillrVideoConfiguration.java rename killrvideo-commons/src/main/java/com/killrvideo/{messaging/conf/MessagingConfiguration.java => conf/MessagingGuavaConfiguration.java} (92%) delete mode 100644 killrvideo-commons/src/main/java/com/killrvideo/discovery/ServiceDiscoveryDao.java delete mode 100644 killrvideo-commons/src/main/java/com/killrvideo/discovery/ServiceDiscoveryDaoEtcd.java delete mode 100644 killrvideo-commons/src/main/java/com/killrvideo/discovery/ServiceDiscoveryDaoStatic.java delete mode 100644 killrvideo-commons/src/main/java/com/killrvideo/dse/conf/DseConfiguration.java create mode 100644 killrvideo-commons/src/main/java/com/killrvideo/dse/dao/AbstractDseDao.java delete mode 100644 killrvideo-commons/src/main/java/com/killrvideo/dse/dao/DseDaoSupport.java create mode 100644 killrvideo-commons/src/main/java/com/killrvideo/dse/dao/DseSchema.java delete mode 100644 killrvideo-commons/src/main/java/com/killrvideo/dse/dto/AbstractEntity.java delete mode 100644 killrvideo-commons/src/main/java/com/killrvideo/dse/utils/BlobToStringCodec.java delete mode 100644 killrvideo-commons/src/main/java/com/killrvideo/dse/utils/EmptyCollectionIfNull.java rename killrvideo-commons/src/main/java/com/killrvideo/{utils => grpc}/GrpcMappingUtils.java (97%) delete mode 100644 killrvideo-commons/src/main/java/com/killrvideo/model/CommonConstants.java rename killrvideo-commons/src/main/java/com/killrvideo/{messaging/conf => utils}/KillrVideoThreadFactory.java (97%) delete mode 100644 killrvideo-commons/src/test/resources/killrvideo-test.properties create mode 100644 killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoMapper.java create mode 100644 killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoQueryProvider.java rename killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/{ => dao}/dto/Comment.java (71%) rename killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/{dto/CommentByUser.java => dao/dto/CommentByUserEntity.java} (57%) rename killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/{dto/CommentByVideo.java => dao/dto/CommentByVideoEntity.java} (55%) rename killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/{ => grpc}/dto/QueryCommentByUser.java (97%) rename killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/{ => grpc}/dto/QueryCommentByVideo.java (97%) create mode 100644 killrvideo-service-comments/src/test/java/com/killrvideo/service/comment/test/CommentDseDaoTest.java diff --git a/killrvideo-commons/.factorypath b/killrvideo-commons/.factorypath index 9867b6a5..c579201f 100644 --- a/killrvideo-commons/.factorypath +++ b/killrvideo-commons/.factorypath @@ -1,136 +1,58 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - - - - + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/killrvideo-commons/pom.xml b/killrvideo-commons/pom.xml index f25eeebd..a9159fd7 100644 --- a/killrvideo-commons/pom.xml +++ b/killrvideo-commons/pom.xml @@ -15,60 +15,49 @@ - - - - org.springframework - spring-context - - - - - ch.qos.logback - logback-classic - - - ch.qos.logback - logback-core - - - + com.datastax.dse dse-java-driver-core com.datastax.dse - dse-java-driver-mapping + dse-java-driver-query-builder com.datastax.dse - dse-java-driver-extras + dse-java-driver-mapper-runtime + + - com.datastax.dse - dse-java-driver-graph + org.springframework + spring-context - - + + - com.xqbase - etcd4j + ch.qos.logback + logback-classic - - - com.evanlennick - retry4j + ch.qos.logback + logback-core + + + + commons-codec + commons-codec + org.apache.commons commons-lang3 - javax.annotation - javax.annotation-api - + javax.annotation + javax.annotation-api + javax.validation validation-api @@ -85,17 +74,17 @@ javax.el javax.el-api - + com.google.protobuf protobuf-java - io.grpc - grpc-all - - + io.grpc + grpc-all + + org.apache.kafka @@ -112,84 +101,6 @@ guava - - - org.junit.platform - junit-platform-launcher - - - org.junit.platform - junit-platform-runner - - - org.junit.platform - junit-platform-console-standalone - - - org.junit.jupiter - junit-jupiter-engine - - - org.junit.jupiter - junit-jupiter-params - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - ${build-helper-maven-plugin.version} - - - generate-sources - - add-source - - - - target/generated-sources/protobuf/java - - - - - - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - ${protobuf-maven-plugin.version} - - ${basedir}/src/main/resources/proto - com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} - grpc-java - io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc.version}:exe:${os.detected.classifier} - - - - - compile - compile-custom - - - - - - - - - diff --git a/killrvideo-commons/src/main/java/com/killrvideo/conf/DseDriverConfiguration.java b/killrvideo-commons/src/main/java/com/killrvideo/conf/DseDriverConfiguration.java new file mode 100644 index 00000000..e0d54ff2 --- /dev/null +++ b/killrvideo-commons/src/main/java/com/killrvideo/conf/DseDriverConfiguration.java @@ -0,0 +1,180 @@ +package com.killrvideo.conf; + +import java.net.InetSocketAddress; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.lang.NonNull; + +import com.datastax.dse.driver.api.core.DseSession; +import com.datastax.dse.driver.api.core.DseSessionBuilder; +import com.datastax.dse.driver.api.core.config.DseDriverConfigLoader; +import com.datastax.dse.driver.internal.core.auth.DsePlainTextAuthProvider; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.config.DefaultDriverOption; +import com.datastax.oss.driver.api.core.config.ProgrammaticDriverConfigLoaderBuilder; + +/** + * The DSE (DataStax Enterprise) Driver configuration. + * + *

+ * Driver options should be specified in the usual way, that is, through an + * application.conf file accessible on the application classpath. See the + * driver configuration + * section in the online docs for more information. + * + *

+ * To illustrate how to integrate the driver configuration with Spring, a few driver options should be configured through Spring's + * own configuration mechanism: + * + *

    + *
  1. driver.contactPoints: this property will override the driver's {@code + * datastax-java-driver.basic.contact-points} option; it will default to 127.0.0.1 + * if unspecified; + *
  2. driver.port: this property will be combined with the previous one to create initial contact points; it will + * default to 9042 if unspecified; + *
  3. driver.localdc: this property will override the driver's {@code + * datastax-java-driver.basic.load-balancing-policy.local-datacenter} option; it has no default value and must be specified; + *
  4. driver.keyspace: this property will override the driver's {@code + * datastax-java-driver.basic.session-keyspace} option; it has no default value and must be specified; + *
  5. driver.consistency: this property will override the driver's {@code + * datastax-java-driver.basic.request.consistency}; it will default to LOCAL_QUORUM + * if unspecified; + *
  6. driver.pageSize: this property will override the driver's {@code + * datastax-java-driver.basic.request.page-size}; it will default to 10 if unspecified; + *
  7. driver.username: this property will override the driver's {@code + * datastax-java-driver.advanced.auth-provider.username} option; if unspecified, it will be assumed that no authentication + * is required; + *
  8. driver.password: this property will override the driver's {@code + * datastax-java-driver.advanced.auth-provider.password} option; if unspecified, it will be assumed that no authentication + * is required; + *
+ * + * The above properties should be typically declared in an {@code application.yml} file. + * + * @author DataStax Developer Advocates team. + */ +@Configuration +@Profile("!unit-test & !integration-test") +public class DseDriverConfiguration { + + /** Initialize dedicated connection to ETCD system. */ + private static final Logger LOGGER = LoggerFactory.getLogger(DseDriverConfiguration.class); + + @Value("#{'${killrvideo.dse.contactPoints}'.split(',')}") + private List contactPoints; + + @Value("#{environment.KILLRVIDEO_DSE_CONTACTPOINTS}") + private Optional contactPointsEnvironmentVar; + + @Value("${killrvideo.dse.port: 9042}") + private int port; + + @Value("${killrvideo.dse.keyspace: killrvideo}") + private String keyspaceName; + + @Value("${killrvideo.dse.localdc: dc1}") + private String localDc; + + @Value("${killrvideo.dse.username}") + public Optional dseUsername; + + @Value("${killrvideo.dse.password}") + public Optional dsePassword; + + @Value("${killrvideo.dse.consistency:LOCAL_QUORUM}") + protected String consistency; + + /** + * Returns the keyspace to connect to. The keyspace specified here must exist. + * + * @return The {@linkplain CqlIdentifier keyspace} bean. + */ + @Bean("killrvideo.keyspace") + public CqlIdentifier keyspace() { + return CqlIdentifier.fromCql(keyspaceName); + } + + /** + * Returns a {@link ProgrammaticDriverConfigLoaderBuilder} to load driver options. + * + *

+ * Use this loader if you need to programmatically override default values for any driver setting. In this example, we + * manually set the default consistency level to use, and, if a username and password are present, we define a basic + * authentication scheme using {@link DsePlainTextAuthProvider}. + * + *

+ * Any value explicitly set through this loader will take precedence over values found in the driver's standard + * application.conf file. + * + * @return The {@link ProgrammaticDriverConfigLoaderBuilder} bean. + */ + @Bean + public ProgrammaticDriverConfigLoaderBuilder configLoaderBuilder() { + ProgrammaticDriverConfigLoaderBuilder configLoaderBuilder = DseDriverConfigLoader + .programmaticBuilder() + .withString(DefaultDriverOption.REQUEST_CONSISTENCY, consistency); + if (!dseUsername.isEmpty() && !dsePassword.isEmpty()) { + configLoaderBuilder = configLoaderBuilder + .withString(DefaultDriverOption.AUTH_PROVIDER_CLASS, DsePlainTextAuthProvider.class.getName()) + .withString(DefaultDriverOption.AUTH_PROVIDER_USER_NAME, dseUsername.get()) + .withString(DefaultDriverOption.AUTH_PROVIDER_PASSWORD, dsePassword.get()); + } + return configLoaderBuilder; + } + + /** + * Returns a {@link DseSessionBuilder} that will configure sessions using the provided + * {@link ProgrammaticDriverConfigLoaderBuilder config loader builder}, as well as the contact points and local datacenter + * name found in application.yml, merged with other options found in application.conf. + * + * @param driverConfigLoaderBuilder + * The {@link ProgrammaticDriverConfigLoaderBuilder} bean to use. + * @return The {@link DseSessionBuilder} bean. + */ + @Bean + public DseSessionBuilder sessionBuilder(@NonNull ProgrammaticDriverConfigLoaderBuilder driverConfigLoaderBuilder) { + DseSessionBuilder sessionBuilder = new DseSessionBuilder().withConfigLoader(driverConfigLoaderBuilder.build()); + if (!contactPointsEnvironmentVar.isEmpty() && !contactPointsEnvironmentVar.get().isBlank()) { + contactPoints = Arrays.asList(contactPointsEnvironmentVar.get().split(",")); + LOGGER.info(" + Reading contactPoints from KILLRVIDEO_DSE_CONTACTPOINTS"); + } + for (String contactPoint : contactPoints) { + InetSocketAddress address = InetSocketAddress.createUnresolved(contactPoint, port); + sessionBuilder = sessionBuilder.addContactPoint(address); + } + return sessionBuilder.withLocalDatacenter(localDc); + } + + /** + * Returns the {@link DseSession} to use, configured with the provided {@link DseSessionBuilder session builder}. The returned + * session will be automatically connected to the given keyspace. + * + * @param sessionBuilder + * The {@link DseSessionBuilder} bean to use. + * @param keyspace + * The {@linkplain CqlIdentifier keyspace} bean to use. + * @return The {@link DseSession} bean. + */ + @Bean + public DseSession session(@NonNull DseSessionBuilder dseSessionBuilder) { + return dseSessionBuilder.withKeyspace(getKeyspaceName()).build(); + } + + /** + * Getter accessor for attribute 'keyspaceName'. + * + * @return + * current value of 'keyspaceName' + */ + public String getKeyspaceName() { + return keyspaceName; + } +} diff --git a/killrvideo-commons/src/main/java/com/killrvideo/conf/GrpcConfiguration.java b/killrvideo-commons/src/main/java/com/killrvideo/conf/GrpcConfiguration.java new file mode 100644 index 00000000..b721e28d --- /dev/null +++ b/killrvideo-commons/src/main/java/com/killrvideo/conf/GrpcConfiguration.java @@ -0,0 +1,25 @@ +package com.killrvideo.conf; + +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class GrpcConfiguration { + + @Value("#{environment.KILLRVIDEO_GRPC_PORT}") + private Optional grpcPortEnvironmentVar; + + /** Listening Port for GRPC. */ + @Value("${killrvideo.grpc-server.port: 50101}") + private int grpcPort; + + public int getGrpcPort() { + if (!grpcPortEnvironmentVar.isEmpty()) { + grpcPort = grpcPortEnvironmentVar.get(); + } + return grpcPort; + } + +} diff --git a/killrvideo-commons/src/main/java/com/killrvideo/messaging/conf/KafkaConfiguration.java b/killrvideo-commons/src/main/java/com/killrvideo/conf/KafkaConfiguration.java similarity index 76% rename from killrvideo-commons/src/main/java/com/killrvideo/messaging/conf/KafkaConfiguration.java rename to killrvideo-commons/src/main/java/com/killrvideo/conf/KafkaConfiguration.java index 5fbc6bf1..1fe30ed2 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/messaging/conf/KafkaConfiguration.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/conf/KafkaConfiguration.java @@ -1,4 +1,4 @@ -package com.killrvideo.messaging.conf; +package com.killrvideo.conf; import static org.apache.kafka.clients.consumer.ConsumerConfig.GROUP_ID_CONFIG; import static org.apache.kafka.clients.consumer.ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG; @@ -8,7 +8,10 @@ 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 java.util.Arrays; +import java.util.Optional; import java.util.Properties; +import java.util.stream.Collectors; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.clients.producer.KafkaProducer; @@ -16,41 +19,41 @@ import org.apache.kafka.common.serialization.ByteArraySerializer; import org.apache.kafka.common.serialization.StringDeserializer; import org.apache.kafka.common.serialization.StringSerializer; -import org.springframework.beans.factory.annotation.Autowired; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; -import com.killrvideo.conf.KillrVideoConfiguration; -import com.killrvideo.discovery.ServiceDiscoveryDao; - /** * Use Kafka to exchange messages between services. * - * @author Cedrick LUNVEN (@clunven) * + * @author Cedrick LUNVEN (@clunven) */ @Configuration -@Profile(KillrVideoConfiguration.PROFILE_MESSAGING_KAFKA) +@Profile("messaging_kafka") public class KafkaConfiguration { - /** Name of service in ETCD. */ - public static final String SERVICE_KAFKA = "kafka"; - - /** Default CQL listening port. */ - public static final int DEFAULT_PORT = 8082; + /** Initialize dedicated connection to ETCD system. */ + private static final Logger LOGGER = LoggerFactory.getLogger(KafkaConfiguration.class); + + @Value("${killrvideo.kafka.port: 8082}") + private int kafkaPort; - /** Kafka Server to be used. */ - private String kafkaServer; + @Value("${killrvideo.kafka.brokers:kafka}") + private String kafkaBrokers; - @Value("${kafka.ack: 1 }") - private String producerAck; + @Value("#{environment.KILLRVIDEO_KAFKA_BROKERS}") + private Optional kafkaBrokersEnvironmentVar; - @Value("${kafka.consumerGroup: killrvideo }") + @Value("${killrvideo.kafka.consumerGroup: killrvideo }") private String consumerGroup; - @Autowired - private ServiceDiscoveryDao discoveryDao; + @Value("${killrvideo.kafka.ack: 1 }") + private String producerAck; + + private String connectionURL; /** * Should we init connection with ETCD or direct. @@ -59,10 +62,16 @@ public class KafkaConfiguration { * target kafka adress */ private String getKafkaServerConnectionUrl() { - if (null == kafkaServer) { - kafkaServer = String.join(",", discoveryDao.lookup(SERVICE_KAFKA)); - } - return kafkaServer; + if (null == connectionURL ) { + if (!kafkaBrokersEnvironmentVar.isEmpty() && !kafkaBrokersEnvironmentVar.get().isBlank()) { + kafkaBrokers = kafkaBrokersEnvironmentVar.get(); + LOGGER.info(" + Reading broker from KILLRVIDEO_KAFKA_BROKERS"); + } + connectionURL = String.join(",", Arrays.asList(kafkaBrokers.split(",")) + .stream().map(ip -> ip + ":" + kafkaPort) + .collect(Collectors.toList())); + } + return connectionURL; } @Bean("kafka.producer") diff --git a/killrvideo-commons/src/main/java/com/killrvideo/conf/KillrVideoConfiguration.java b/killrvideo-commons/src/main/java/com/killrvideo/conf/KillrVideoConfiguration.java deleted file mode 100644 index 6e1c5b93..00000000 --- a/killrvideo-commons/src/main/java/com/killrvideo/conf/KillrVideoConfiguration.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.killrvideo.conf; - -import javax.validation.Validation; -import javax.validation.Validator; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * Configuration for KillrVideo application leveraging on DSE, ETCD and any external source. - * - * @author DataStax Developer Advocates team. - */ -@Configuration -public class KillrVideoConfiguration { - - @Value("#{environment.KILLRVIDEO_HOST_IP ?: '10.0.75.1'}") - private String applicationHost; - - @Value("${application.name: KillrVideo}") - private String applicationName; - - /** Use Spring profile to adapt behaviours. */ - public static final String PROFILE_MESSAGING_KAFKA = "messaging_kafka"; - public static final String PROFILE_MESSAGING_MEMORY = "messaging_memory"; - public static final String PROFILE_DISCOVERY_ETCD = "discovery_etcd"; - public static final String PROFILE_DISCOVERY_STATIC = "discovery_static"; - - @Bean - public Validator getBeanValidator() { - return Validation.buildDefaultValidatorFactory().getValidator(); - } - - /** - * Getter for attribute 'applicationName'. - * - * @return - * current value of 'applicationName' - */ - public String getApplicationName() { - return applicationName; - } - - /** - * Getter for attribute 'applicationHost'. - * - * @return - * current value of 'applicationHost' - */ - public String getApplicationHost() { - return applicationHost; - } - -} diff --git a/killrvideo-commons/src/main/java/com/killrvideo/messaging/conf/MessagingConfiguration.java b/killrvideo-commons/src/main/java/com/killrvideo/conf/MessagingGuavaConfiguration.java similarity index 92% rename from killrvideo-commons/src/main/java/com/killrvideo/messaging/conf/MessagingConfiguration.java rename to killrvideo-commons/src/main/java/com/killrvideo/conf/MessagingGuavaConfiguration.java index dae62374..f9cce509 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/messaging/conf/MessagingConfiguration.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/conf/MessagingGuavaConfiguration.java @@ -1,4 +1,4 @@ -package com.killrvideo.messaging.conf; +package com.killrvideo.conf; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; @@ -11,7 +11,7 @@ import org.springframework.context.annotation.Profile; import com.google.common.eventbus.EventBus; -import com.killrvideo.conf.KillrVideoConfiguration; +import com.killrvideo.utils.KillrVideoThreadFactory; /** * Store all configuration related to Messagging. @@ -19,8 +19,8 @@ * @author DataStax Developer Advocates team. */ @Configuration -@Profile(KillrVideoConfiguration.PROFILE_MESSAGING_MEMORY) -public class MessagingConfiguration { +@Profile("messaging_memory") +public class MessagingGuavaConfiguration { /** Event Bus. */ private static final String EVENT_BUS_KILLRVIODEO = "killrvideo_event_bus"; diff --git a/killrvideo-commons/src/main/java/com/killrvideo/discovery/ServiceDiscoveryDao.java b/killrvideo-commons/src/main/java/com/killrvideo/discovery/ServiceDiscoveryDao.java deleted file mode 100644 index 5c4f7428..00000000 --- a/killrvideo-commons/src/main/java/com/killrvideo/discovery/ServiceDiscoveryDao.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.killrvideo.discovery; - -import java.util.List; - -/** - * Work with service registry (ETCD, Consul..) - * - * @author Cedrick LUNVEN (@clunven) - */ -public interface ServiceDiscoveryDao { - - /** - * Register new endpoint for a service. - * @param serviceName - * unique service identifier - * @param hostName - * current hostname - * @param portNumber - * current port number - * @return - * service key (service name + namespace) - */ - String register(String serviceName, String hostName, int portNumber); - - /** - * List endpoints available for a service. - * - * @param serviceName - * service identifier - * @return - * list of endpoints like hostname1:port1, hostname2:port2 - */ - List < String > lookup(String serviceName); - - /** - * Unregister all endpoints for a service. - * - * @param serviceName - * service unique identifier - */ - void unregister(String serviceName); - - /** - * Unregister one endpoint for a service. - * - * @param serviceName - * service unique identifier - * @param hostName - * current hostname - * @param portNumber - * current port number - */ - void unregisterEndpoint(String serviceName, String hostName, int portNumber); - -} diff --git a/killrvideo-commons/src/main/java/com/killrvideo/discovery/ServiceDiscoveryDaoEtcd.java b/killrvideo-commons/src/main/java/com/killrvideo/discovery/ServiceDiscoveryDaoEtcd.java deleted file mode 100644 index 6ec0958d..00000000 --- a/killrvideo-commons/src/main/java/com/killrvideo/discovery/ServiceDiscoveryDaoEtcd.java +++ /dev/null @@ -1,230 +0,0 @@ -package com.killrvideo.discovery; - -import java.net.URI; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.Callable; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -import javax.annotation.PostConstruct; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Component; - -import com.evanlennick.retry4j.CallExecutor; -import com.evanlennick.retry4j.config.RetryConfig; -import com.evanlennick.retry4j.config.RetryConfigBuilder; -import com.killrvideo.conf.KillrVideoConfiguration; -import com.xqbase.etcd4j.EtcdClient; -import com.xqbase.etcd4j.EtcdClientException; -import com.xqbase.etcd4j.EtcdNode; - -/** - * Hanle operation arount ETCD (connection, read, write). - * - * @author DataStax Developer Advocates Team - */ -@Component("killrvideo.discovery.etcd") -@Profile(KillrVideoConfiguration.PROFILE_DISCOVERY_ETCD) -public class ServiceDiscoveryDaoEtcd implements ServiceDiscoveryDao { - - /** Initialize dedicated connection to ETCD system. */ - private static final Logger LOGGER = LoggerFactory.getLogger(ServiceDiscoveryDaoEtcd.class); - - /** Namespace. */ - public static String KILLRVIDEO_SERVICE_NAMESPACE = "/killrvideo/services/"; - - @Value("${killrvideo.discovery.etcd.host: 10.0.75.1}") - private String etcdServerHost; - - @Value("${killrvideo.discovery.etcd.port: 2379}") - private int etcdServerPort; - - @Value("${killrvideo.discovery.etcd.maxNumberOfTries: 10}") - private int maxNumberOfTriesEtcd; - - @Value("${killrvideo.discovery.etcd.delayBetweenTries: 2}") - private int delayBetweenTriesEtcd; - - /** Native client. */ - private EtcdClient etcdClient; - - @PostConstruct - public void connect() { - final String etcdUrl = String.format("http://%s:%d", etcdServerHost, etcdServerPort); - LOGGER.info("Initialize connection to ETCD Server:"); - LOGGER.info(" + Connecting to '{}'", etcdUrl); - etcdClient = new EtcdClient(URI.create(etcdUrl)); - waitForEtcd(); - LOGGER.info(" + [OK] Connection established."); - } - - /** - * Read from ETCD using a retry mecanism. - * - * @param key - * current key to look in ETCD. - * @param required - * key is required if not returning empty list - * @return - */ - private void waitForEtcd() { - final AtomicInteger atomicCount = new AtomicInteger(1); - Callable> getKeyFromEtcd = () -> { - try { - List nodes = etcdClient.listDir("/"); - if ((nodes == null || nodes.isEmpty())) { - throw new IllegalStateException("/ is required in ETCD but not yet present"); - } - return nodes; - } catch (EtcdClientException e) { - throw new IllegalStateException("Cannot Access ETCD Server : " + e.getMessage()); - } - }; - RetryConfig etcdRetryConfig = new RetryConfigBuilder() - .retryOnAnyException() - .withMaxNumberOfTries(maxNumberOfTriesEtcd) - .withDelayBetweenTries(delayBetweenTriesEtcd, ChronoUnit.SECONDS) - .withFixedBackoff() - .build(); - new CallExecutor>(etcdRetryConfig) - .afterFailedTry(s -> { - LOGGER.info("Attempt #{}/{} : ETCD is not ready (retry in {}s)", - atomicCount.getAndIncrement(), maxNumberOfTriesEtcd, delayBetweenTriesEtcd); }) - .onFailure(s -> { - LOGGER.error("ETCD is not ready after {} attempts, exiting", maxNumberOfTriesEtcd); - System.err.println("ETCD is not ready after " + maxNumberOfTriesEtcd + " attempts, exiting now."); - System.exit(500); - }) - .execute(getKeyFromEtcd).getResult() - .stream().map(node -> node.value) - .collect(Collectors.toList()); - } - - /** - * Give a service name like 'CommentServices' look for Directory at namespace killrvideo - * and list value (keys are generated) - * - * @param serviceName - * unique service name - * @return - * list of values - */ - public List < String > lookup(String serviceName) { - List< String > endPointList = new ArrayList<>(); - String serviceDirectoryKey = KILLRVIDEO_SERVICE_NAMESPACE + serviceName + "/"; - LOGGER.info(" List endpoints for key '{}':", serviceDirectoryKey); - try { - List< EtcdNode > existingNodes = etcdClient.listDir(serviceDirectoryKey); - if (existingNodes != null) { - endPointList = existingNodes - .stream() - .map(node -> node.value) - .collect(Collectors.toList()); - } - } catch (EtcdClientException e) {} - LOGGER.info(" + [OK] Endpoints retrieved '{}':", endPointList); - return endPointList; - } - - /** - * Given a servicename and a host give the latest port if exist. This will be used for - * - * @param serviceName - * target service - * @return - */ - public synchronized Optional lookupServicePorts(String serviceName, String hostName) { - int targetPort = -1; - LOGGER.info("Accessing last port for endpoint with same host"); - for (String endpoint : lookup(serviceName)) { - String[] endpointChunks = endpoint.split(":"); - int endPointPort = Integer.valueOf(endpointChunks[1]); - String endPointHost = endpointChunks[0]; - if (hostName.equalsIgnoreCase(endPointHost)) { - if (endPointPort > targetPort) { - targetPort = endPointPort; - LOGGER.info(" + Found {}", targetPort); - } - } - } ; - return (targetPort == -1) ? Optional.empty() : Optional.of(targetPort); - } - - /** {@inheritDoc} */ - public String register(String serviceName, String hostName, int portNumber) { - String serviceDirectoryKey = KILLRVIDEO_SERVICE_NAMESPACE + serviceName.trim() + "/"; - String endPoint = hostName + ":" + portNumber; - try { - try { - LOGGER.info("Register endpoint '{}' for key '{}':", endPoint, serviceDirectoryKey); - etcdClient.createDir(serviceDirectoryKey, null, false); - LOGGER.info(" + Dir '{}' has been created", serviceDirectoryKey); - } catch (EtcdClientException e) { - LOGGER.info(" + Dir '{}' already exist", serviceDirectoryKey); - } - List< EtcdNode > existingNodes = etcdClient.listDir(serviceDirectoryKey); - if (existingNodes != null) { - Optional existingEndpoint = existingNodes - .stream().filter(p -> p.value.equalsIgnoreCase(endPoint)) - .findFirst(); - // Return existing key - if (existingEndpoint.isPresent()) { - LOGGER.info(" + [OK] Endpoint '{}' already exist", endPoint); - return existingEndpoint.get().key; - } - } - // Create new Key - String serviceKey = serviceDirectoryKey + UUID.randomUUID().toString(); - etcdClient.set(serviceKey, endPoint); - LOGGER.info(" + [OK] Endpoint registered with key '{}'", serviceKey); - return serviceKey; - } catch (EtcdClientException e) { - throw new IllegalStateException("Cannot register services into ETCD", e); - } - } - - /** {@inheritDoc} */ - public void unregisterEndpoint(String serviceName, String hostName, int portNumber) { - String serviceDirectoryKey = KILLRVIDEO_SERVICE_NAMESPACE + serviceName + "/"; - String endPoint = hostName + ":" + portNumber; - try { - LOGGER.info("Unregister endpoint '{}' for key '{}':", endPoint, serviceDirectoryKey); - List< EtcdNode > existingNodes = etcdClient.listDir(serviceDirectoryKey); - Optional existingEndpoint = Optional.empty(); - if (existingNodes != null) { - existingEndpoint = existingNodes - .stream().filter(p -> p.value.equalsIgnoreCase(endPoint)) - .findFirst(); - } - if (existingEndpoint.isPresent()) { - etcdClient.delete(existingEndpoint.get().key); - LOGGER.info(" + [OK] Endpoint has been deleted (key={})", existingEndpoint.get().key); - } else { - LOGGER.info(" + [OK] This endpoint does not exist"); - } - } catch (EtcdClientException e) { - throw new IllegalStateException("Cannot register services into ETCD", e); - } - } - - /** {@inheritDoc} */ - public void unregister(String serviceName) { - String serviceDirectoryKey = KILLRVIDEO_SERVICE_NAMESPACE + serviceName + "/"; - try { - LOGGER.info("Delete dir '{}'", serviceDirectoryKey); - etcdClient.deleteDir("/killrvideo/services/" + serviceName, true); - LOGGER.info(" + [OK] Directory has been deleted"); - } catch (EtcdClientException e) { - LOGGER.info(" + [OK] Directory did not exist"); - } - } - -} diff --git a/killrvideo-commons/src/main/java/com/killrvideo/discovery/ServiceDiscoveryDaoStatic.java b/killrvideo-commons/src/main/java/com/killrvideo/discovery/ServiceDiscoveryDaoStatic.java deleted file mode 100644 index dc7e565e..00000000 --- a/killrvideo-commons/src/main/java/com/killrvideo/discovery/ServiceDiscoveryDaoStatic.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.killrvideo.discovery; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Component; - -import com.killrvideo.conf.KillrVideoConfiguration; - -/** - * There is no explicit access to - * - * @author Cedrick LUNVEN (@clunven) - */ -@Component("killrvideo.discovery.network") -@Profile(KillrVideoConfiguration.PROFILE_DISCOVERY_STATIC) -public class ServiceDiscoveryDaoStatic implements ServiceDiscoveryDao { - - /** Initialize dedicated connection to ETCD system. */ - private static final Logger LOGGER = LoggerFactory.getLogger(ServiceDiscoveryDaoStatic.class); - - @Value("${killrvideo.discovery.service.kafka: kafka}") - private String kafkaServiceName; - - @Value("${killrvideo.discovery.static.kafka.port: 8082}") - private int kafkaPort; - - @Value("${killrvideo.discovery.static.kafka.brokers}") - private String kafkaBrokers; - @Value("#{environment.KILLRVIDEO_KAFKA_BROKERS}") - private Optional kafkaBrokersEnvVar; - - @Value("${killrvideo.discovery.service.cassandra: cassandra}") - private String cassandraServiceName; - - @Value("${killrvideo.discovery.static.cassandra.port: 9042}") - private int cassandraPort; - - @Value("${killrvideo.discovery.static.cassandra.contactPoints}") - private String cassandraContactPoints; - @Value("#{environment.KILLRVIDEO_DSE_CONTACTPOINTS}") - private Optional cassandraContactPointsEnvVar; - - /** {@inheritDoc} */ - @Override - public List lookup(String serviceName) { - List< String > endPointList = new ArrayList<>(); - LOGGER.info(" + Lookup for key '{}':", serviceName); - if (kafkaServiceName.equalsIgnoreCase(serviceName)) { - if (!kafkaBrokersEnvVar.isEmpty() && !kafkaBrokersEnvVar.get().isBlank()) { - cassandraContactPoints = kafkaBrokersEnvVar.get(); - LOGGER.info(" + Reading broker from KILLRVIDEO_KAFKA_BROKERS"); - } - Arrays.asList(kafkaBrokers.split(",")).stream() - .forEach(ip -> endPointList.add(ip + ":" + kafkaPort)); - - } else if (cassandraServiceName.equalsIgnoreCase(serviceName)) { - // Explicit overwriting of contact points from env var - // Better than default spring : simpler - if (!cassandraContactPointsEnvVar.isEmpty() && !cassandraContactPointsEnvVar.get().isBlank()) { - cassandraContactPoints = cassandraContactPointsEnvVar.get(); - LOGGER.info(" + Reading contactPoints from KILLRVIDEO_DSE_CONTACTPOINTS"); - } - Arrays.asList(cassandraContactPoints.split(",")) - .stream() - .forEach(ip -> endPointList.add(ip + ":" + cassandraPort)); - } - LOGGER.info(" + Endpoints retrieved '{}':", endPointList); - return endPointList; - } - - /** {@inheritDoc} */ - @Override - public String register(String serviceName, String hostName, int portNumber) { - // Do nothing in k8s service are registered through DNS - return serviceName; - } - - /** {@inheritDoc} */ - @Override - public void unregister(String serviceName) {} - - /** {@inheritDoc} */ - @Override - public void unregisterEndpoint(String serviceName, String hostName, int portNumber) {} - -} diff --git a/killrvideo-commons/src/main/java/com/killrvideo/dse/conf/DseConfiguration.java b/killrvideo-commons/src/main/java/com/killrvideo/dse/conf/DseConfiguration.java deleted file mode 100644 index 02e4f0e1..00000000 --- a/killrvideo-commons/src/main/java/com/killrvideo/dse/conf/DseConfiguration.java +++ /dev/null @@ -1,360 +0,0 @@ -package com.killrvideo.dse.conf; - -import java.io.BufferedInputStream; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.security.KeyStore; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.time.temporal.ChronoUnit; -import java.util.Optional; -import java.util.concurrent.Callable; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.net.ssl.TrustManagerFactory; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import com.datastax.driver.core.AuthProvider; -import com.datastax.driver.core.ConsistencyLevel; -import com.datastax.driver.core.QueryOptions; -import com.datastax.driver.core.RemoteEndpointAwareNettySSLOptions; -import com.datastax.driver.core.policies.AddressTranslator; -import com.datastax.driver.core.policies.ConstantReconnectionPolicy; -import com.datastax.driver.dse.DseCluster.Builder; -import com.datastax.driver.dse.DseSession; -import com.datastax.driver.dse.auth.DsePlainTextAuthProvider; -import com.datastax.driver.dse.graph.GraphOptions; -import com.datastax.driver.dse.graph.GraphProtocol; -import com.datastax.driver.mapping.DefaultPropertyMapper; -import com.datastax.driver.mapping.MappingConfiguration; -import com.datastax.driver.mapping.MappingManager; -import com.datastax.driver.mapping.PropertyMapper; -import com.datastax.driver.mapping.PropertyTransienceStrategy; -import com.datastax.dse.graph.api.DseGraph; -import com.evanlennick.retry4j.CallExecutor; -import com.evanlennick.retry4j.config.RetryConfig; -import com.evanlennick.retry4j.config.RetryConfigBuilder; -import com.killrvideo.discovery.ServiceDiscoveryDao; -import com.killrvideo.dse.graph.KillrVideoTraversalSource; -import com.killrvideo.dse.utils.BlobToStringCodec; -import com.killrvideo.model.CommonConstants; - -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; - -/** - * Connectivity to DSE (cassandra, graph, search, analytics). - * - * @author DataStax Developer Advocates team. - */ -@Configuration -public class DseConfiguration { - - /** Internal logger. */ - private static final Logger LOGGER = LoggerFactory.getLogger(DseConfiguration.class); - - @Value("${killrvideo.discovery.service.cassandra: cassandra}") - private String cassandraServiceName; - - @Value("${killrvideo.cassandra.clustername: 'killrvideo'}") - public String dseClustOerName; - - @Value("${killrvideo.graph.timeout: 30000}") - public Integer graphTimeout; - - @Value("${killrvideo.graph.recommendation.name: 'killrvideo_video_recommendations'}") - public String graphRecommendationName; - - @Value("#{environment.KILLRVIDEO_DSE_USERNAME}") - public Optional < String > dseUsername; - - @Value("#{environment.KILLRVIDEO_DSE_PASSWORD}") - public Optional < String > dsePassword; - - @Value("${killrvideo.cassandra.maxNumberOfTries: 10}") - private int maxNumberOfTries = 10; - - @Value("${killrvideo.cassandra.delayBetweenTries: 2}") - private int delayBetweenTries = 2; - - @Value("${killrvideo.ssl.CACertFileLocation: cassandra.cert}") - private String sslCACertFileLocation; - - @Value("#{environment.KILLRVIDEO_SSL_CERTIFICATE}") - public Optional < String > sslCACertFileLocationEnvVar; - - @Value("${killrvideo.ssl.enable: false}") - public boolean dseEnableSSL = false; - - @Value("#{environment.KILLRVIDEO_ENABLE_SSL}") - public Optional < Boolean > dseEnableSSLEnvVar; - - @Value("${killrvideo.etcd.enabled : true}") - private boolean etcdLookup = false; - - @Autowired - private ServiceDiscoveryDao discoveryDao; - - @Bean - public DseSession initializeDSE() { - long top = System.currentTimeMillis(); - LOGGER.info("Initializing connection to DSE Cluster..."); - Builder clusterConfig = new Builder(); - populateContactPoints(clusterConfig); - populateAuthentication(clusterConfig); - populateGraphOptions(clusterConfig); - populateSSL(clusterConfig); - - // More options available with flags through QueryOptions - QueryOptions options = new QueryOptions(); - options.setConsistencyLevel(ConsistencyLevel.QUORUM); - options.setSerialConsistencyLevel(ConsistencyLevel.LOCAL_SERIAL); - clusterConfig.withQueryOptions(options); - clusterConfig.withReconnectionPolicy(new ConstantReconnectionPolicy(2000)); - - // Required - clusterConfig.withoutJMXReporting(); - clusterConfig.withoutMetrics(); - clusterConfig.getConfiguration().getSocketOptions().setReadTimeoutMillis(1000); - clusterConfig.getConfiguration().getCodecRegistry().register(new BlobToStringCodec()); - - final AtomicInteger atomicCount = new AtomicInteger(1); - Callable connectionToDse = () -> { - return clusterConfig.build().connect(CommonConstants.KILLRVIDEO_KEYSPACE); - }; - - // Connecting to DSE with a retry mechanism : - /* In docker deployments we may have to wait until all components are up and running. */ - RetryConfig config = new RetryConfigBuilder() - .retryOnAnyException() - .withMaxNumberOfTries(maxNumberOfTries) - .withDelayBetweenTries(delayBetweenTries, ChronoUnit.SECONDS) - .withFixedBackoff() - .build(); - - return new CallExecutor(config) - .afterFailedTry(s -> { - LOGGER.info("Attempt #{}/{} failed.. trying in {} seconds, waiting Dse to Start", atomicCount.getAndIncrement(), - maxNumberOfTries, delayBetweenTries); }) - .onFailure(s -> { - LOGGER.error("Cannot connection to DSE after {} attempts, exiting", maxNumberOfTries); - System.err.println("Can not conenction to DSE after " + maxNumberOfTries + " attempts, exiting"); - System.exit(500); - }) - .onSuccess(s -> { - long timeElapsed = System.currentTimeMillis() - top; - LOGGER.info("[OK] Connection etablished to DSE Cluster in {} millis.", timeElapsed);}) - .execute(connectionToDse).getResult(); - } - - /** - * Use to create mapper and perform ORM on top of Cassandra tables. - * - * @param session - * current dse session. - * @return - * mapper - */ - @Bean - public MappingManager initializeMappingManager(DseSession session) { - // Do not map all fields, only the annotated ones with @Column or @Fields - PropertyMapper propertyMapper = new DefaultPropertyMapper() - .setPropertyTransienceStrategy(PropertyTransienceStrategy.OPT_IN); - // Build configuration from mapping - MappingConfiguration configuration = MappingConfiguration.builder() - .withPropertyMapper(propertyMapper) - .build(); - // Sample Manager with advance configuration - return new MappingManager(session, configuration); - } - - /** - * Graph Traversal for suggested videos. - * - * @param session - * current dse session. - * @return - * traversal - */ - @Bean - public KillrVideoTraversalSource initializeGraphTraversalSource(DseSession session) { - return DseGraph.traversal(session, KillrVideoTraversalSource.class); - } - - /** - * Retrieve cluster nodes adresses (eg:node1:9042) from ETCD and initialize the `contact points`, - * endpoints of Cassandra cluster nodes. - * - * @note - * [Initializing Contact Points with Java Driver] - * - * (1) The default port is 9042. If you keep using the default port you - * do not need to use or `withPort()` or `addContactPointsWithPorts()`, only `addContactPoint()`. - * - * (2) Best practice is to use the SAME PORT for each node and to setup the port through `withPort()`. - * - * (3) Never, ever use `addContactPointsWithPorts` in clusters : it will ony set port FOR THE FIRST NODE. - * DON'T USE, EVEN IF ALL NODE USE SAME PORT. It purpose is only for tests and standalone servers. - * - * (4) What if I have a cluster and nodes do not use the same port (eg: node1:9043, node2:9044, node3:9045) ? - * You need to use {@link AddressTranslator} as defined below and reference with `withAddressTranslator(translator);` - * - * - * public class MyClusterAddressTranslator implements AddressTranslator { - * @Override - * public void init(Cluster cluster) {} - * @Override - * public void close() {} - * - * @Override - * public InetSocketAddress translate(InetSocketAddress incoming) { - * // Given the configuration - * Map clusterNodes = new HashMap() { { - * put("node1", 9043);put("node2", 9044);put("node3", 9045); - * }}; - * String targetHostName = incoming.getHostName(); - * if (clusterNodes.containsKey(targetHostName)) { - * return new InetSocketAddress(targetHostName, clusterNodes.get(targetHostName)); - * } - * throw new IllegalArgumentException("Cannot translate URL " + incoming + " hostName not found"); - * } - * } - * - * - * @param clusterConfig - * current configuration - */ - private void populateContactPoints(Builder clusterConfig) { - discoveryDao.lookup(cassandraServiceName).stream() - .map(this::asSocketInetAdress) - .filter(node -> node.isPresent()) - .map(node -> node.get()) - // Use one node port to setup - they are all the same - .peek(node -> clusterConfig.withPort(node.getPort())) - .map(adress -> adress.getHostName()) - .forEach(clusterConfig::addContactPoint); - } - - /** - * Check to see if we have username and password from the environment - * This is here because we have a dual use scenario. One for developers and others - * who download KillrVideo and run within a local Docker container and the other - * who might need (like us for example) to connect KillrVideo up to an external - * cluster that requires authentication. - */ - private void populateAuthentication(Builder clusterConfig) { - if (dseUsername.isPresent() && dsePassword.isPresent() - && dseUsername.get().length() > 0) { - AuthProvider cassandraAuthProvider = new DsePlainTextAuthProvider(dseUsername.get(), dsePassword.get()); - clusterConfig.withAuthProvider(cassandraAuthProvider); - String obfuscatedPassword = new String(new char[dsePassword.get().length()]).replace("\0", "*"); - LOGGER.info(" + Using supplied DSE username: '{}' and password: '{}' from environment variables", - dseUsername.get(), obfuscatedPassword); - } else { - LOGGER.info(" + Connection is not authenticated (no username/password)"); - } - } - - private void populateGraphOptions(Builder clusterConfig) { - GraphOptions go = new GraphOptions(); - go.setGraphName(graphRecommendationName); - go.setReadTimeoutMillis(graphTimeout); - go.setGraphSubProtocol(GraphProtocol.GRAPHSON_2_0); - clusterConfig.withGraphOptions(go); - } - - /** - * Convert information in ETCD as real adress {@link InetSocketAddress} if possible. - * - * @param contactPoint - * network node adress information like hostname:port - * @return - * java formatted inet adress - */ - private Optional asSocketInetAdress(String contactPoint) { - Optional target = Optional.empty(); - try { - if (contactPoint != null && contactPoint.length() > 0) { - String[] chunks = contactPoint.split(":"); - if (chunks.length == 2) { - LOGGER.info(" + Adding node '{}' to the Cassandra cluster definition", contactPoint); - return Optional.of(new InetSocketAddress(InetAddress.getByName(chunks[0]), Integer.parseInt(chunks[1]))); - } - } - } catch (NumberFormatException e) { - LOGGER.warn(" + Cannot read contactPoint - " - + "Invalid Port Numer, entry '" + contactPoint + "' will be ignored", e); - } catch (UnknownHostException e) { - LOGGER.warn(" + Cannot read contactPoint - " - + "Invalid Hostname, entry '" + contactPoint + "' will be ignored", e); - } - return target; - } - - /** - * If SSL is enabled use the supplied CA cert file to create - * an SSL context and use to configure our cluster. - * - * @param clusterConfig - * current configuration - */ - private void populateSSL(Builder clusterConfig) { - - // Reading Environment Variables to eventually override default config - if (!dseEnableSSLEnvVar.isEmpty()) { - dseEnableSSL = dseEnableSSLEnvVar.get(); - if (!sslCACertFileLocationEnvVar.isEmpty()) { - sslCACertFileLocation = sslCACertFileLocationEnvVar.get(); - } - } - - if (dseEnableSSL) { - LOGGER.info(" + SSL is enabled, using supplied SSL certificate: '{}'", sslCACertFileLocation); - try { - // X509 Certificate - FileInputStream fis = new FileInputStream(sslCACertFileLocation); - X509Certificate caCert = (X509Certificate) CertificateFactory.getInstance("X.509") - .generateCertificate(new BufferedInputStream(fis)); - - // KeyStore - KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); - ks.load(null, null); - ks.setCertificateEntry(Integer.toString(1), caCert); - - // TrustStore - TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - tmf.init(ks); - SslContext sslContext = SslContextBuilder.forClient().trustManager(tmf).build(); - clusterConfig.withSSL(new RemoteEndpointAwareNettySSLOptions(sslContext)); - - } catch (FileNotFoundException fne) { - String errMsg = "SSL cert file not found. You must provide a valid certification file when using SSL encryption option."; - LOGGER.error(errMsg, fne); - throw new IllegalArgumentException(errMsg, fne); - - } catch (CertificateException ce) { - String errCert = "Your CA certificate looks invalid. You must provide a valid certification file when using SSL encryption option."; - LOGGER.error(errCert, ce); - - throw new IllegalArgumentException(errCert, ce); - } catch (Exception e) { - String errSsl = "Exception in SSL configuration: "; - LOGGER.error(errSsl, e); - throw new IllegalArgumentException(errSsl, e); - } - } else { - LOGGER.info(" + SSL encryption is not enabled)"); - } - } - -} diff --git a/killrvideo-commons/src/main/java/com/killrvideo/dse/dao/AbstractDseDao.java b/killrvideo-commons/src/main/java/com/killrvideo/dse/dao/AbstractDseDao.java new file mode 100644 index 00000000..af80f1a5 --- /dev/null +++ b/killrvideo-commons/src/main/java/com/killrvideo/dse/dao/AbstractDseDao.java @@ -0,0 +1,51 @@ +package com.killrvideo.dse.dao; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; + +import com.datastax.dse.driver.api.core.DseSession; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.config.DriverConfig; + +/** + * Mutualization of code for DAO. + * + * @author DataStax Developer Advocates team. + */ +public abstract class AbstractDseDao implements DseSchema { + + @Autowired + protected DseSession dseSession; + + @Autowired + @Qualifier("killrvideo.keyspace") + protected CqlIdentifier keyspaceName; + + protected DriverConfig dseDriverConfig; + + /** + * Default constructor. + */ + public AbstractDseDao() {} + + /** + * Allow explicit intialization for test purpose. + */ + public AbstractDseDao(DseSession dseSession) { + this.dseSession = dseSession; + this.dseDriverConfig = dseSession.getContext().getConfig(); + if (!dseSession.getKeyspace().isEmpty()) { + keyspaceName = dseSession.getKeyspace().get(); + } + } + + /** + * Utility for validations. + */ + protected void assertNotNull(String mName, String pName, Object obj) { + if (obj == null) { + throw new IllegalArgumentException("Assertion failed: param " + pName + " is required for method " + mName); + } + } + +} diff --git a/killrvideo-commons/src/main/java/com/killrvideo/dse/dao/DseDaoSupport.java b/killrvideo-commons/src/main/java/com/killrvideo/dse/dao/DseDaoSupport.java deleted file mode 100644 index 01541759..00000000 --- a/killrvideo-commons/src/main/java/com/killrvideo/dse/dao/DseDaoSupport.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.killrvideo.dse.dao; - -import static com.datastax.driver.mapping.Mapper.Option.timestamp; - -import javax.annotation.PostConstruct; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; - -import com.datastax.driver.dse.DseSession; -import com.datastax.driver.mapping.Mapper; -import com.datastax.driver.mapping.MappingManager; -import com.killrvideo.model.CommonConstants; - -/** - * Mutualization of code for DAO. - * - * @author DataStax Developer Advocates team. - */ -public abstract class DseDaoSupport implements CommonConstants { - - /** Loger for that class. */ - private Logger LOGGER = LoggerFactory.getLogger(getClass()); - - /** Code for Solr QUERY. */ - public static final String SOLR_QUERY = "solr_query"; - - /** Hold Connectivity to DSE. */ - @Autowired - protected DseSession dseSession; - - /** Hold Driver Mapper to implement ORM with Cassandra. */ - @Autowired - protected MappingManager mappingManager; - - /** - * Preparation of statements before firing queries allow signifiant performance improvements. - * This can only be done it the statement is 'static', mean the number of parameter - * to bind() is fixed. If not the case you can find sample in method buildStatement*() in this class. - */ - @PostConstruct - protected abstract void initialize (); - - /** - * Default constructor. - */ - public DseDaoSupport() {} - - /** - * Allow explicit intialization for test purpose. - */ - public DseDaoSupport(DseSession dseSession) { - this.dseSession = dseSession; - this.mappingManager = new MappingManager(dseSession); - initialize(); - } - - /** - * Allows to save with custom mapper if relevant. - * - * @param entity - * current entity - * @param overridingMapper - * current mapper - */ - protected void save(T entity, Mapper mapper) { - long start = System.currentTimeMillis(); - mapper.save(entity, timestamp(System.currentTimeMillis())); - LOGGER.debug("Saving entity {} in {} milli(s).", entity, System.currentTimeMillis() - start); - } - - protected void assertNotNull(String mName, String pName, Object obj) { - if (obj == null) { - throw new IllegalArgumentException("Assertion failed: param " + pName + " is required for method " + mName); - } - } - -} diff --git a/killrvideo-commons/src/main/java/com/killrvideo/dse/dao/DseSchema.java b/killrvideo-commons/src/main/java/com/killrvideo/dse/dao/DseSchema.java new file mode 100644 index 00000000..e93a9232 --- /dev/null +++ b/killrvideo-commons/src/main/java/com/killrvideo/dse/dao/DseSchema.java @@ -0,0 +1,88 @@ +package com.killrvideo.dse.dao; + +import java.text.SimpleDateFormat; + +import com.datastax.oss.driver.api.core.CqlIdentifier; + +/** + * Information related to SCHEMA : use to 'decorate' POJO in Mapper, then prepareStatements. + * + * @author DataStax Developer Advocates team. + */ +public interface DseSchema { + + SimpleDateFormat FORMATTER_DAY = new SimpleDateFormat("yyyyMMdd"); + + // user_credentials + String USERCREDENTIAL_COLUMN_USERID ="userid" ; + String USERCREDENTIAL_COLUMN_PASSWORD = "\"password\""; + String USERCREDENTIAL_COLUMN_EMAIL = "email"; + + CqlIdentifier TABLENAME_USER_CREDENTIALS = CqlIdentifier.fromCql("user_credentials"); + CqlIdentifier USERCREDENTIAL_COLUMN_USERID_ = CqlIdentifier.fromCql(USERCREDENTIAL_COLUMN_USERID); + CqlIdentifier USERCREDENTIAL_COLUMN_PASSWORD_ = CqlIdentifier.fromCql(USERCREDENTIAL_COLUMN_PASSWORD); + CqlIdentifier USERCREDENTIAL_COLUMN_EMAIL_ = CqlIdentifier.fromCql(USERCREDENTIAL_COLUMN_EMAIL); + + // users + String USER_COLUMN_USERID = "userid"; + String USER_COLUMN_FIRSTNAME = "firstname"; + String USER_COLUMN_LASTNAME = "lastname"; + String USER_COLUMN_EMAIL = "email"; + String USER_COLUMN_CREATE = "created_date"; + + CqlIdentifier TABLENAME_USERS = CqlIdentifier.fromCql("users"); + CqlIdentifier USER_COLUMN_USERID_ = CqlIdentifier.fromCql(USER_COLUMN_USERID); + CqlIdentifier USER_COLUMN_FIRSTNAME_ = CqlIdentifier.fromCql(USER_COLUMN_FIRSTNAME); + CqlIdentifier USER_COLUMN_LASTNAME_ = CqlIdentifier.fromCql(USER_COLUMN_LASTNAME); + CqlIdentifier USER_COLUMN_EMAIL_ = CqlIdentifier.fromCql(USER_COLUMN_EMAIL); + CqlIdentifier USER_COLUMN_CREATE_ = CqlIdentifier.fromCql(USER_COLUMN_CREATE); + + // videos + CqlIdentifier TABLENAME_VIDEOS = CqlIdentifier.fromCql("videos"); + + String VIDEOS_COLUMN_VIDEOID = "videoid"; + String VIDEOS_COLUMN_USERID = "userid"; + String VIDEOS_COLUMN_NAME = "name"; + String VIDEOS_COLUMN_DESCRIPTION = "description"; + String VIDEOS_COLUMN_LOCATION = "location"; + String VIDEOS_COLUMN_LOCATIONTYPE = "location_type"; + String VIDEOS_COLUMN_PREVIEW = "preview_image_location"; + String VIDEOS_COLUMN_TAGS = "tags"; + String VIDEOS_COLUMN_ADDED_DATE = "added_date"; + + CqlIdentifier VIDEOS_COLUMN_VIDEOID_ = CqlIdentifier.fromCql(VIDEOS_COLUMN_VIDEOID); + CqlIdentifier VIDEOS_COLUMN_USERID_ = CqlIdentifier.fromCql(VIDEOS_COLUMN_USERID); + CqlIdentifier VIDEOS_COLUMN_NAME_ = CqlIdentifier.fromCql(VIDEOS_COLUMN_NAME); + CqlIdentifier VIDEOS_COLUMN_DESCRIPTION_ = CqlIdentifier.fromCql(VIDEOS_COLUMN_DESCRIPTION); + CqlIdentifier VIDEOS_COLUMN_LOCATION_ = CqlIdentifier.fromCql(VIDEOS_COLUMN_LOCATION); + CqlIdentifier VIDEOS_COLUMN_LOCATIONTYPE_ = CqlIdentifier.fromCql(VIDEOS_COLUMN_LOCATIONTYPE); + CqlIdentifier VIDEOS_COLUMN_PREVIEW_ = CqlIdentifier.fromCql(VIDEOS_COLUMN_PREVIEW); + CqlIdentifier VIDEOS_COLUMN_TAGS_ = CqlIdentifier.fromCql(VIDEOS_COLUMN_TAGS); + CqlIdentifier VIDEOS_COLUMN_ADDED_DATE_ = CqlIdentifier.fromCql(VIDEOS_COLUMN_ADDED_DATE); + + // user_videos + CqlIdentifier TABLENAME_USERS_VIDEO = CqlIdentifier.fromCql("user_videos"); + + // latest_videos + CqlIdentifier TABLENAME_LATEST_VIDEO = CqlIdentifier.fromCql("latest_videos"); + + String LATESTVIDEOS_COLUMN_YYYYMMDD = "yyyymmdd"; + + CqlIdentifier LATESTVIDEOS_COLUMN_YYYYMMDD_ = CqlIdentifier.fromCql(LATESTVIDEOS_COLUMN_YYYYMMDD); + + // comments_by_video + comments_by_user + String TABLENAME_COMMENTS_BY_USER = "comments_by_user"; + String TABLENAME_COMMENTS_BY_VIDEO = "comments_by_video"; + String COMMENTS_COLUMN_VIDEOID = "videoid"; + String COMMENTS_COLUMN_USERID = "userid"; + String COMMENTS_COLUMN_COMMENTID = "commentid"; + String COMMENTS_COLUMN_COMMENT = "comment"; + + CqlIdentifier TABLENAME_COMMENTS_BY_USER_ = CqlIdentifier.fromCql(TABLENAME_COMMENTS_BY_USER); + CqlIdentifier TABLENAME_COMMENTS_BY_VIDEO_ = CqlIdentifier.fromCql(TABLENAME_COMMENTS_BY_VIDEO); + CqlIdentifier COMMENTS_COLUMN_VIDEOID_ = CqlIdentifier.fromCql(COMMENTS_COLUMN_VIDEOID); + CqlIdentifier COMMENTS_COLUMN_USERID_ = CqlIdentifier.fromCql(COMMENTS_COLUMN_USERID); + CqlIdentifier COMMENTS_COLUMN_COMMENTID_ = CqlIdentifier.fromCql(COMMENTS_COLUMN_COMMENTID); + CqlIdentifier COMMENTS_COLUMN_COMMENT_ = CqlIdentifier.fromCql(COMMENTS_COLUMN_COMMENT); + +} diff --git a/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/AbstractEntity.java b/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/AbstractEntity.java deleted file mode 100644 index dfd00717..00000000 --- a/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/AbstractEntity.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.killrvideo.dse.dto; - -import java.io.IOException; -import java.io.Serializable; -import java.text.SimpleDateFormat; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.killrvideo.model.CommonConstants; - - - -/** - * Entities to be used in the application. - * - * @author DataStax Developer Advocates team. - */ -public abstract class AbstractEntity implements Serializable, CommonConstants { - - /** Serial. */ - private static final long serialVersionUID = 7239223683486549695L; - - /** Used as converter. */ - public static final SimpleDateFormat FORMATTER_DAY = new SimpleDateFormat("yyyyMMdd"); - - /** Helping Loging. */ - private static ObjectMapper om = new ObjectMapper(); - - /** {@inheritDoc} */ - @Override - public String toString() { - try { - return getClass().getSimpleName() + " : " + om.writeValueAsString(this); - } catch (IOException e) { - return super.toString(); - } - } - -} diff --git a/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/AbstractVideo.java b/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/AbstractVideo.java index 2657cbfc..f6053a0c 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/AbstractVideo.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/AbstractVideo.java @@ -1,29 +1,27 @@ package com.killrvideo.dse.dto; +import java.io.Serializable; + import org.hibernate.validator.constraints.Length; -import com.datastax.driver.mapping.annotations.Column; +import com.datastax.oss.driver.api.mapper.annotations.CqlName; +import com.killrvideo.dse.dao.DseSchema; /** * Bean representing shared attributes in videos. * * @author DataStax Developer Advocates team */ -public abstract class AbstractVideo extends AbstractEntity { +public abstract class AbstractVideo implements Serializable, DseSchema { /** Serial. */ private static final long serialVersionUID = -4366554197274003003L; - /** Column names. */ - public static final String COLUMN_NAME = "name"; - public static final String COLUMN_TAGS = "tags"; - public static final String COLUMN_PREVIEW = "preview_image_location"; - - @Column + @CqlName(VIDEOS_COLUMN_NAME) @Length(min = 1, message = "The video name must not be empty") protected String name; - @Column(name = COLUMN_PREVIEW) + @CqlName(VIDEOS_COLUMN_PREVIEW) protected String previewImageLocation; /** diff --git a/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/ResultListPage.java b/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/ResultListPage.java index b590e1df..a07516af 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/ResultListPage.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/ResultListPage.java @@ -6,10 +6,8 @@ import java.util.Optional; import java.util.stream.IntStream; -import com.datastax.driver.core.PagingState; -import com.datastax.driver.core.ResultSet; -import com.datastax.driver.mapping.Mapper; -import com.datastax.driver.mapping.Result; +import com.datastax.oss.driver.api.core.MappedAsyncPagingIterable; +import com.datastax.oss.driver.api.core.PagingIterable; /** * Ease usage of the paginState. @@ -37,29 +35,25 @@ public ResultListPage() {} * @param mapper * mapper */ - public ResultListPage(Result rs) { + public ResultListPage(PagingIterable rs) { if (null != rs) { Iterator iterResults = rs.iterator(); // rs.getAvailableWithoutFetching() all to parse only current page without fecthing all IntStream.range(0, rs.getAvailableWithoutFetching()) .forEach(item -> listOfResults.add(iterResults.next())); - nextPage = Optional.ofNullable(rs.getExecutionInfo().getPagingState()) - .map(PagingState::toString); + if (null != rs.getExecutionInfo().getPagingState()) { + nextPage = Optional.ofNullable(rs.getExecutionInfo().getPagingState().toString()); + } + } + } + + public ResultListPage(MappedAsyncPagingIterable rs) { + if (null != rs) { + rs.currentPage().forEach(listOfResults::add); + nextPage = Optional.ofNullable(rs.getExecutionInfo().getPagingState().toString()); } } - /** - * Constructor with mapper. - * - * @param rs - * result set - * @param mapper - * mapper - */ - public ResultListPage(ResultSet rs, Mapper mapper) { - this(mapper.map(rs)); - } - /** {@inheritDoc} */ @Override public String toString() { diff --git a/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/Video.java b/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/Video.java index b035f7bd..df46af23 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/Video.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/Video.java @@ -1,6 +1,7 @@ package com.killrvideo.dse.dto; import java.util.Date; +import java.util.HashSet; import java.util.Set; import java.util.UUID; @@ -8,57 +9,45 @@ import org.hibernate.validator.constraints.Length; -import com.datastax.driver.mapping.annotations.Column; -import com.datastax.driver.mapping.annotations.PartitionKey; -import com.datastax.driver.mapping.annotations.Table; -import com.killrvideo.dse.utils.EmptyCollectionIfNull; -import com.killrvideo.model.CommonConstants; +import com.datastax.oss.driver.api.mapper.annotations.CqlName; +import com.datastax.oss.driver.api.mapper.annotations.Entity; +import com.datastax.oss.driver.api.mapper.annotations.PartitionKey; /** * Pojo representing DTO for table 'videos'. * * @author DataStax Developer Advocates team. */ -@Table(keyspace = CommonConstants.KILLRVIDEO_KEYSPACE, name = Video.TABLENAME_VIDEOS) +@Entity public class Video extends AbstractVideo { /** Serial. */ private static final long serialVersionUID = 7035802926837646137L; - public static final String TABLENAME_VIDEOS = "videos"; - - /** Column names in the DB. */ - public static final String COLUMN_USERID = "userid"; - public static final String COLUMN_VIDEOID = "videoid"; - public static final String COLUMN_DESCRIPTION = "description"; - public static final String COLUMN_LOCATION = "location"; - public static final String COLUMN_LOCATIONTYPE = "location_type"; - public static final String COLUMN_ADDED_DATE = "added_date"; - @PartitionKey + @CqlName(VIDEOS_COLUMN_VIDEOID) private UUID videoid; @NotNull - @Column + @CqlName(VIDEOS_COLUMN_USERID) private UUID userid; @Length(min = 1, message = "description must not be empty") - @Column + @CqlName(VIDEOS_COLUMN_DESCRIPTION) private String description; @Length(min = 1, message = "location must not be empty") - @Column + @CqlName(VIDEOS_COLUMN_LOCATION) private String location; - @Column(name = COLUMN_LOCATIONTYPE) + @CqlName(VIDEOS_COLUMN_LOCATIONTYPE) private int locationType; - @Column - @EmptyCollectionIfNull - private Set tags; + @CqlName(VIDEOS_COLUMN_TAGS) + private Set tags = new HashSet<>(); @NotNull - @Column(name = COLUMN_ADDED_DATE) + @CqlName(VIDEOS_COLUMN_ADDED_DATE) private Date addedDate; /** diff --git a/killrvideo-commons/src/main/java/com/killrvideo/dse/graph/KillrVideoTraversalDsl.java b/killrvideo-commons/src/main/java/com/killrvideo/dse/graph/KillrVideoTraversalDsl.java index 22eb0b53..c100fab4 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/dse/graph/KillrVideoTraversalDsl.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/dse/graph/KillrVideoTraversalDsl.java @@ -239,7 +239,6 @@ public default GraphTraversal add(Traversal mutationTraversal) { * engine hackathon team for coming up with the following traversal and passing on some * working code for me to start with. */ - @SuppressWarnings("deprecation") public default GraphTraversal> recommendByUserRating( int recommendations, int minRating, int numRatingsToSample, int localUserRatingsToSample) { Assert.isTrue(recommendations > 0, "recommendations must be greater than zero"); diff --git a/killrvideo-commons/src/main/java/com/killrvideo/dse/utils/BlobToStringCodec.java b/killrvideo-commons/src/main/java/com/killrvideo/dse/utils/BlobToStringCodec.java deleted file mode 100644 index 1212b926..00000000 --- a/killrvideo-commons/src/main/java/com/killrvideo/dse/utils/BlobToStringCodec.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.killrvideo.dse.utils; - -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; - -import com.datastax.driver.core.TypeCodec; -import com.datastax.driver.extras.codecs.MappingCodec; - -/** - * Column expect a blob, attribute is a String, we need a codec here for conversion. - * - * In CQL you would be able to use textAsBlob(). - * - * @author DataStax Developer Advocates team. - */ -public class BlobToStringCodec extends MappingCodec { - - /** Working woth Charset UTF */ - private Charset plarformCharset; - - /** Default charset will be UTF-8. */ - public BlobToStringCodec() { - this(StandardCharsets.UTF_8); - } - - /** - * Default construcot. - */ - public BlobToStringCodec(Charset charset) { - super(TypeCodec.blob(), String.class); - this.plarformCharset = charset; - } - - /** {@inheritDoc} */ - @Override - protected ByteBuffer serialize(String str) { - return ByteBuffer.wrap(str.getBytes(plarformCharset)); - } - - /** {@inheritDoc} */ - @Override - protected String deserialize(ByteBuffer byteBuffer) { - return plarformCharset.decode(byteBuffer).toString(); - } -} \ No newline at end of file diff --git a/killrvideo-commons/src/main/java/com/killrvideo/dse/utils/DseUtils.java b/killrvideo-commons/src/main/java/com/killrvideo/dse/utils/DseUtils.java index 1d464741..c94fba1b 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/dse/utils/DseUtils.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/dse/utils/DseUtils.java @@ -3,19 +3,18 @@ import java.io.FileNotFoundException; import java.io.InputStream; import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; import java.util.Scanner; +import java.util.UUID; import java.util.concurrent.CompletableFuture; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.datastax.driver.core.exceptions.InvalidQueryException; -import com.datastax.driver.core.querybuilder.QueryBuilder; -import com.datastax.driver.core.schemabuilder.SchemaBuilder; -import com.datastax.driver.dse.DseSession; +import com.datastax.dse.driver.api.core.DseSession; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; +import com.datastax.oss.driver.api.querybuilder.QueryBuilder; +import com.datastax.oss.driver.api.querybuilder.SchemaBuilder; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -29,14 +28,13 @@ public class DseUtils { /** Internal logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(DseUtils.class); - - /** Replication Strategies. */ - public static enum ReplicationStrategy { SimpleStrategy, NetworkTopologyStrategy }; - - private static final String KEY_CLASS = "class"; - private static final String KEY_REPLICATIONFACTOR = "replication_factor"; private static final String UTF8_ENCODING = "UTF-8"; private static final String NEW_LINE = System.getProperty("line.separator"); + private static final long INT_SINCE_UUID_EPOCH = 0x01b21dd213814000L; + + public static long getTimeFromUUID(UUID uuid) { + return (uuid.timestamp() - INT_SINCE_UUID_EPOCH) / 10000; + } /** * Helper to create a KeySpace. @@ -45,35 +43,27 @@ public static enum ReplicationStrategy { SimpleStrategy, NetworkTopologyStrategy * target keyspaceName */ public static void createKeySpaceSimpleStrategy(DseSession dseSession, String keyspacename, int replicationFactor) { - final Map replication = new HashMap<>(); - replication.put(KEY_CLASS, ReplicationStrategy.SimpleStrategy.name()); - replication.put(KEY_REPLICATIONFACTOR, replicationFactor); - dseSession.execute(SchemaBuilder.createKeyspace(keyspacename).ifNotExists().with().replication(replication)); + dseSession.execute(SchemaBuilder.createKeyspace(keyspacename) + .ifNotExists() + .withSimpleStrategy(replicationFactor) + .build()); useKeySpace(dseSession, keyspacename); } - /** - * Setup connection to use keyspace. - * - * @param dseSession - * current session - * @param keyspacename - * target keyspace - */ + public static boolean isTableEmpty(DseSession dseSession, CqlIdentifier keyspace, CqlIdentifier tablename) { + return 0 == dseSession.execute(QueryBuilder.selectFrom(keyspace, tablename).all().build()).getAvailableWithoutFetching(); + } + public static void useKeySpace(DseSession dseSession, String keyspacename) { dseSession.execute("USE " + keyspacename); } - /** - * Empty table. - * - * @param dseSession - * current session - * @param tableName - * table name - */ - public static void truncate(DseSession dseSession, String tableName) { - dseSession.execute(QueryBuilder.truncate(tableName)); + public static void dropKeyspace(DseSession dseSession, String keyspacename) { + dseSession.executeAsync(SchemaBuilder.dropKeyspace(keyspacename).ifExists().build()); + } + + public static void truncateTable(DseSession dseSession, CqlIdentifier keyspace, CqlIdentifier tableName) { + dseSession.execute(QueryBuilder.truncate(keyspace, tableName).build()); } /** @@ -156,9 +146,4 @@ public static CompletableFuture buildCompletableFuture(final ListenableFu }); return completable; } - - @SuppressWarnings("rawtypes") - public static String displayGraphTranserval(T graphTraversal) { - return org.apache.tinkerpop.gremlin.groovy.jsr223.GroovyTranslator.of("g").translate(graphTraversal.getBytecode()); - } } diff --git a/killrvideo-commons/src/main/java/com/killrvideo/dse/utils/EmptyCollectionIfNull.java b/killrvideo-commons/src/main/java/com/killrvideo/dse/utils/EmptyCollectionIfNull.java deleted file mode 100644 index 94281ab6..00000000 --- a/killrvideo-commons/src/main/java/com/killrvideo/dse/utils/EmptyCollectionIfNull.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2012-2016 DuyHai DOAN - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.killrvideo.dse.utils; - -import java.lang.annotation.*; - -/** - - * In Cassandra there is no difference between an empty collection/map - * and a null value for collection/map - *
- *
- * In Java we do make the difference. This annotations allows mapping null values from - * Cassandra into empty collection & map. - *
- *
- * Empty list will default to ArrayList.
- * Empty set will default to HashSet.
- * Empty map will default to HashMap.
- - *


- * {@literal @}Column
- * {@literal @}EmptyCollectionIfNull
- * private List<String> friends
- * 
- *
- - * This annotation can be used for nested collections too: - *
- *

- * {@literal @}Column
- * private Tuple2<Integer,{@literal @}EmptyCollectionIfNull List<String>> friends
- * 
- - *

- * - * @see @EmptyCollectionIfNull - */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.TYPE_USE}) -@Documented -public @interface EmptyCollectionIfNull { - -} diff --git a/killrvideo-commons/src/main/java/com/killrvideo/grpc/AbstractSingleServiceGrpcServer.java b/killrvideo-commons/src/main/java/com/killrvideo/grpc/AbstractSingleServiceGrpcServer.java index ce2f25fd..ddf1418f 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/grpc/AbstractSingleServiceGrpcServer.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/grpc/AbstractSingleServiceGrpcServer.java @@ -1,7 +1,5 @@ package com.killrvideo.grpc; -import java.util.Optional; - import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -9,8 +7,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import com.killrvideo.conf.KillrVideoConfiguration; -import com.killrvideo.discovery.ServiceDiscoveryDaoEtcd; +import com.killrvideo.conf.GrpcConfiguration; import io.grpc.Server; import io.grpc.ServerBuilder; @@ -26,20 +23,12 @@ public abstract class AbstractSingleServiceGrpcServer { /** Some logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(AbstractSingleServiceGrpcServer.class); - /** Global Configuration. s*/ - @Autowired - protected KillrVideoConfiguration killrVideoConfig; - - /** Connectivity to ETCD Service discovery. */ @Autowired - protected ServiceDiscoveryDaoEtcd serviceDiscoveryDao; + protected GrpcConfiguration grpcConfig; /** GRPC Server to start. */ protected Server grpcServer; - /** Port to be allocated dynamically based on ETCD. */ - protected static int grpcServerPort = 0; - /** Service Name. */ protected abstract String getServiceName(); @@ -55,13 +44,7 @@ public abstract class AbstractSingleServiceGrpcServer { @PostConstruct public void startGrpcServer() throws Exception { LOGGER.info("Initializing Comment Service"); - grpcServerPort = getDefaultPort(); - Optional maxUsedPort = serviceDiscoveryDao.lookupServicePorts(getServiceName(), - killrVideoConfig.getApplicationHost()); - if (maxUsedPort.isPresent()) { - grpcServerPort = maxUsedPort.get() + 1; - } - grpcServer = ServerBuilder.forPort(grpcServerPort) + grpcServer = ServerBuilder.forPort(grpcConfig.getGrpcPort()) .addService(getService()) .build(); Runtime.getRuntime().addShutdownHook(new Thread() { @@ -70,17 +53,14 @@ public void run() { } }); grpcServer.start(); - LOGGER.info("[OK] Grpc Server started on port: '{}'", grpcServerPort); - serviceDiscoveryDao.register(getServiceName(), - killrVideoConfig.getApplicationHost(), grpcServerPort); + LOGGER.info("[OK] Grpc Server started on port: '{}'", grpcConfig.getGrpcPort()); } @PreDestroy public void stopGrpcServer() { - LOGGER.info("Calling shutdown for GrpcServer"); - serviceDiscoveryDao.register(getServiceName(), - killrVideoConfig.getApplicationHost(), grpcServerPort); + LOGGER.info("Stopping GrpcServer..."); grpcServer.shutdown(); + LOGGER.info("[OK] Grpc Server stopped"); } } diff --git a/killrvideo-commons/src/main/java/com/killrvideo/utils/GrpcMappingUtils.java b/killrvideo-commons/src/main/java/com/killrvideo/grpc/GrpcMappingUtils.java similarity index 97% rename from killrvideo-commons/src/main/java/com/killrvideo/utils/GrpcMappingUtils.java rename to killrvideo-commons/src/main/java/com/killrvideo/grpc/GrpcMappingUtils.java index a626c01d..22086a10 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/utils/GrpcMappingUtils.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/grpc/GrpcMappingUtils.java @@ -1,4 +1,4 @@ -package com.killrvideo.utils; +package com.killrvideo.grpc; import java.time.Instant; import java.util.Date; diff --git a/killrvideo-commons/src/main/java/com/killrvideo/messaging/dao/MessagingDao.java b/killrvideo-commons/src/main/java/com/killrvideo/messaging/dao/MessagingDao.java index 3779882b..bf3af079 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/messaging/dao/MessagingDao.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/messaging/dao/MessagingDao.java @@ -36,7 +36,7 @@ public interface MessagingDao { * @param t */ default CompletableFuture sendErrorEvent(String serviceName, Throwable t) { - return sendEvent(getErrorDestination(), mapError(t)); + return sendEvent(getErrorDestination(), mapError(t)); } /** diff --git a/killrvideo-commons/src/main/java/com/killrvideo/messaging/dao/MessagingDaoInMemory.java b/killrvideo-commons/src/main/java/com/killrvideo/messaging/dao/MessagingDaoInMemory.java index a7fa4137..8fdc0b80 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/messaging/dao/MessagingDaoInMemory.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/messaging/dao/MessagingDaoInMemory.java @@ -9,7 +9,6 @@ import org.springframework.stereotype.Repository; import com.google.common.eventbus.EventBus; -import com.killrvideo.conf.KillrVideoConfiguration; /** * Wrapping any kind of messages. @@ -17,7 +16,7 @@ * @author DataStax Developer Advocates team. */ @Repository("killrvideo.dao.messaging.memory") -@Profile(KillrVideoConfiguration.PROFILE_MESSAGING_MEMORY) +@Profile("messaging_memory") public class MessagingDaoInMemory implements MessagingDao { /** Loger for that class. */ diff --git a/killrvideo-commons/src/main/java/com/killrvideo/messaging/dao/MessagingDaoKafka.java b/killrvideo-commons/src/main/java/com/killrvideo/messaging/dao/MessagingDaoKafka.java index c1ddec91..edbcd750 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/messaging/dao/MessagingDaoKafka.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/messaging/dao/MessagingDaoKafka.java @@ -28,7 +28,6 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.protobuf.AbstractMessageLite; import com.google.protobuf.InvalidProtocolBufferException; -import com.killrvideo.conf.KillrVideoConfiguration; import killrvideo.common.CommonEvents.ErrorEvent; @@ -38,7 +37,7 @@ * @author Cedrick LUNVEN (@clunven) */ @Repository("killrvideo.dao.messaging.kafka") -@Profile(KillrVideoConfiguration.PROFILE_MESSAGING_KAFKA) +@Profile("messaging_kafka") public class MessagingDaoKafka implements MessagingDao { /** Loger for that class. */ diff --git a/killrvideo-commons/src/main/java/com/killrvideo/model/CommonConstants.java b/killrvideo-commons/src/main/java/com/killrvideo/model/CommonConstants.java deleted file mode 100644 index 9f9e4344..00000000 --- a/killrvideo-commons/src/main/java/com/killrvideo/model/CommonConstants.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.killrvideo.model; - -/** - * Information related to SCHEMA : use to 'decorate' POJO in Mapper, then prepareStatements. - * - * @author DataStax Developer Advocates team. - */ -public interface CommonConstants { - - /** Core KeySpace. */ - String KILLRVIDEO_KEYSPACE = "killrvideo"; -} diff --git a/killrvideo-commons/src/main/java/com/killrvideo/messaging/conf/KillrVideoThreadFactory.java b/killrvideo-commons/src/main/java/com/killrvideo/utils/KillrVideoThreadFactory.java similarity index 97% rename from killrvideo-commons/src/main/java/com/killrvideo/messaging/conf/KillrVideoThreadFactory.java rename to killrvideo-commons/src/main/java/com/killrvideo/utils/KillrVideoThreadFactory.java index 0e8ff647..10dafdc6 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/messaging/conf/KillrVideoThreadFactory.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/utils/KillrVideoThreadFactory.java @@ -1,4 +1,4 @@ -package com.killrvideo.messaging.conf; +package com.killrvideo.utils; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; diff --git a/killrvideo-commons/src/main/resources/logback.xml b/killrvideo-commons/src/main/resources/logback.xml index 808ea721..ac6fed00 100644 --- a/killrvideo-commons/src/main/resources/logback.xml +++ b/killrvideo-commons/src/main/resources/logback.xml @@ -9,6 +9,10 @@ + + + + diff --git a/killrvideo-commons/src/test/resources/killrvideo-test.properties b/killrvideo-commons/src/test/resources/killrvideo-test.properties deleted file mode 100644 index b034cae5..00000000 --- a/killrvideo-commons/src/test/resources/killrvideo-test.properties +++ /dev/null @@ -1,12 +0,0 @@ -spring.profiles.active=discovery_etcd,messaging_kafka - -# ETCD Connectivity -killrvideo.etcd.port=2379 -killrvideo.etcd.maxNumberOfTries=10 -killrvideo.etcd.delayBetweenTries=2 - -# this is ETCD host -environment.KILLRVIDEO_DOCKER_IP=10.0.75.1s - - -killrvideo.cassandra.mutation-error-log=./error.log \ No newline at end of file diff --git a/killrvideo-service-comments/.factorypath b/killrvideo-service-comments/.factorypath index 66b136b8..c579201f 100644 --- a/killrvideo-service-comments/.factorypath +++ b/killrvideo-service-comments/.factorypath @@ -1,136 +1,58 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - - - - + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - + + + + + + + + diff --git a/killrvideo-service-comments/pom.xml b/killrvideo-service-comments/pom.xml index 2b31252f..0cb1b42d 100644 --- a/killrvideo-service-comments/pom.xml +++ b/killrvideo-service-comments/pom.xml @@ -1,85 +1,42 @@ - - - - - 4.0.0 - killrvideo-service-comments - + killrvideo-service-comments - Service Comments - - - - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + 4.0.0 + killrvideo-service-comments + + killrvideo-service-comments + Service Comments + com.datastax killrvideo-parent 6.7.0 - - - - + com.datastax killrvideo-commons ${project.version} + + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + + + org.testcontainers + testcontainers + test + - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - ${build-helper-maven-plugin.version} - - - generate-sources - - add-source - - - - target/generated-sources/protobuf/java - target/generated-sources/protobuf/grpc-java - - - - - - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - ${protobuf-maven-plugin.version} - - ${basedir}/src/main/resources/proto - com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} - grpc-java - io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc.version}:exe:${os.detected.classifier} - - - - - compile - compile-custom - - - - - - - - diff --git a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDao.java b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDao.java index 23229d9c..ac2188ab 100644 --- a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDao.java +++ b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDao.java @@ -1,411 +1,93 @@ package com.killrvideo.service.comment.dao; +import java.util.concurrent.CompletionStage; -import static com.killrvideo.service.comment.dto.Comment.COLUMN_COMMENT; -import static com.killrvideo.service.comment.dto.Comment.COLUMN_COMMENTID; -import static com.killrvideo.service.comment.dto.Comment.COLUMN_USERID; -import static com.killrvideo.service.comment.dto.Comment.COLUMN_VIDEOID; - -import java.util.Iterator; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.stream.IntStream; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Repository; -import org.springframework.util.Assert; - -import com.datastax.driver.core.BatchStatement; -import com.datastax.driver.core.BoundStatement; -import com.datastax.driver.core.ConsistencyLevel; -import com.datastax.driver.core.PagingState; -import com.datastax.driver.core.PreparedStatement; -import com.datastax.driver.core.RegularStatement; -import com.datastax.driver.core.ResultSet; -import com.datastax.driver.core.ResultSetFuture; -import com.datastax.driver.core.Row; -import com.datastax.driver.core.Statement; -import com.datastax.driver.core.querybuilder.QueryBuilder; -import com.datastax.driver.dse.DseSession; -import com.datastax.driver.mapping.Mapper; -import com.datastax.driver.mapping.annotations.Accessor; -import com.datastax.driver.mapping.annotations.Param; -import com.datastax.driver.mapping.annotations.Query; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.killrvideo.dse.dao.DseDaoSupport; +import com.datastax.oss.driver.api.mapper.annotations.Dao; +import com.datastax.oss.driver.api.mapper.annotations.QueryProvider; import com.killrvideo.dse.dto.ResultListPage; -import com.killrvideo.service.comment.dto.Comment; -import com.killrvideo.service.comment.dto.CommentByUser; -import com.killrvideo.service.comment.dto.CommentByVideo; -import com.killrvideo.service.comment.dto.QueryCommentByUser; -import com.killrvideo.service.comment.dto.QueryCommentByVideo; -import com.killrvideo.utils.FutureUtils; +import com.killrvideo.service.comment.dao.dto.Comment; +import com.killrvideo.service.comment.dao.dto.CommentByUserEntity; +import com.killrvideo.service.comment.dao.dto.CommentByVideoEntity; +import com.killrvideo.service.comment.grpc.dto.QueryCommentByUser; +import com.killrvideo.service.comment.grpc.dto.QueryCommentByVideo; /** - * Implementation of queries and related to {@link Comment} objects within DataStax Enterprise. - * Comments are store in 2 tables and all queries are performed against Apache Cassandra. - * - * @author DataStax Developer Advocates team. + * Implementation of Services to work with Comments in Killrvideo. We work with + * 2 tables 'comments_by_user' and 'comments_by_video'. */ -@Repository -public class CommentDseDao extends DseDaoSupport { - - /** Logger for that class. */ - private static Logger LOGGER = LoggerFactory.getLogger(CommentDseDao.class); - - /** Data Modelling. */ - public static final String TABLENAME_COMMENTS_BY_VIDEO = "comments_by_video"; - public static final String TABLENAME_COMMENTS_BY_USER = "comments_by_user"; - - /** Mapper to ease queries. */ - protected Mapper < CommentByUser > mapperCommentByUser; - protected Mapper < CommentByVideo > mapperCommentByVideo; - - /** Precompile statements to speed up queries. */ - private PreparedStatement findCommentsByUser; - private PreparedStatement findCommentsByUserPageable; - private PreparedStatement findCommentsByVideo; - private PreparedStatement findCommentsByVideoPageable; - - /** - * Default constructor. - */ - public CommentDseDao() { - super(); - } +@Dao +public interface CommentDseDao { /** - * Allow explicit intialization for test purpose. - */ - public CommentDseDao(DseSession dseSession) { - super(dseSession); - } - - /** - * Cassandra and DSE Session are stateless. For each request a coordinator is chosen - * and execute query against the cluster. The Driver is stateful, it has to maintain some - * network connections pool, here we properly cleanup things. - * - * @throws Exception - * error on cleanup. - */ - @PreDestroy - public void onDestroy() throws Exception { - if (dseSession != null && !dseSession.isClosed()) { - LOGGER.info("Closing DSE Cluster (clean up at shutdown)"); - dseSession.getCluster().close(); - LOGGER.info(" + DSE Cluster is now closed."); - } - } - - /** {@inheritDoc} */ - @PostConstruct - protected void initialize () { - mapperCommentByUser = mappingManager.mapper(CommentByUser.class); - mapperCommentByVideo = mappingManager.mapper(CommentByVideo.class); - - // Using Mapper and annotated bean to get constants value - String keyspaceCommentByUser = mapperCommentByUser.getTableMetadata().getKeyspace().getName(); - String tableNameCommentByUser = mapperCommentByUser.getTableMetadata().getName(); - - // Prepare statements with Query Builder - RegularStatement queryFindComments = QueryBuilder - .select() - .column(COLUMN_USERID).column(COLUMN_COMMENTID) - .column(COLUMN_VIDEOID).column(COLUMN_COMMENT) - .fcall("toTimestamp", QueryBuilder.column(COLUMN_COMMENTID)).as("comment_timestamp") - .from(keyspaceCommentByUser, tableNameCommentByUser) - .where(QueryBuilder.eq(COLUMN_USERID, QueryBuilder.bindMarker())); - findCommentsByUser = dseSession.prepare(queryFindComments); - findCommentsByUser.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM); - - // Prepare statements with Query Builder - RegularStatement queryFindCommentsPage = QueryBuilder - .select() - .column(COLUMN_USERID).column(COLUMN_COMMENTID) - .column(COLUMN_VIDEOID).column(COLUMN_COMMENT) - .fcall("toTimestamp", QueryBuilder.column(COLUMN_COMMENTID)).as("comment_timestamp") - .from(keyspaceCommentByUser, tableNameCommentByUser) - .where(QueryBuilder.eq(COLUMN_USERID, QueryBuilder.bindMarker())) - .and(QueryBuilder.lte(COLUMN_COMMENTID, QueryBuilder.bindMarker())); - findCommentsByUserPageable = dseSession.prepare(queryFindCommentsPage); - findCommentsByUserPageable.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM); - - // Using Mapper and annotated bean to get constants value - String keyspaceCommentByVideo = mapperCommentByVideo.getTableMetadata().getKeyspace().getName(); - String tableNameCommentByVideo = mapperCommentByVideo.getTableMetadata().getName(); - - // Prepare statements with Query Builder - RegularStatement auerySearchAllCommentForvideo = QueryBuilder - .select() - .column(COLUMN_VIDEOID).column(COLUMN_COMMENTID) - .column(COLUMN_USERID).column(COLUMN_COMMENT) - .fcall("toTimestamp", QueryBuilder.column(COLUMN_COMMENTID)).as("comment_timestamp") - .from(keyspaceCommentByVideo, tableNameCommentByVideo) - .where(QueryBuilder.eq(COLUMN_VIDEOID, QueryBuilder.bindMarker())); - findCommentsByVideo = dseSession.prepare(auerySearchAllCommentForvideo); - findCommentsByVideo.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM); - - // Prepare statements with Query Builder - RegularStatement auerySearchCommentForVideo = QueryBuilder - .select() - .column(COLUMN_VIDEOID).column(COLUMN_COMMENTID) - .column(COLUMN_USERID).column(COLUMN_COMMENT) - .fcall("toTimestamp", QueryBuilder.column(COLUMN_COMMENTID)).as("comment_timestamp") - .from(keyspaceCommentByVideo, tableNameCommentByVideo) - .where(QueryBuilder.eq(COLUMN_VIDEOID, QueryBuilder.bindMarker())) - .and(QueryBuilder.lte(COLUMN_COMMENTID, QueryBuilder.bindMarker())); - findCommentsByVideoPageable = dseSession.prepare(auerySearchCommentForVideo); - findCommentsByVideoPageable.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM); - } - - /** - * Insert Comment entity in 2 tables. - */ - public void insertComment(final Comment comment) { - - // Create statement - BatchStatement batchStatement = buildBatchStatementInsertComment(comment); - - // Execute statement (nothing to return, just INSERT here) - dseSession.execute(batchStatement); - } - - /** - * Insert a comment for a video. (in multiple table at once). When executing query aync result will be a completable future. - * Note the 'executeAsync'> No result are expected from insertion and we return CompletableFuture. - * + * Create a comment in both table if not exist, update otherwise. + * * @param comment - * comment to be inserted by signup user. - */ - public CompletableFuture insertCommentAsync(final Comment comment) { - - // Create statement - BatchStatement batchStatement = buildBatchStatementInsertComment(comment); - - // Create a callback for processing result (hey but here it is VOID so no mapping in Success()) - CompletableFuture cfv = new CompletableFuture<>(); - FutureCallback myCallback = new FutureCallback() { - public void onFailure(Throwable ex) { cfv.completeExceptionally(ex); } - public void onSuccess(ResultSet rs) { cfv.complete(null); } - }; - - // Bind execution and callback - Futures.addCallback(dseSession.executeAsync(batchStatement), myCallback); - return cfv; - } - - /** - * Search comment_by_video Asynchronously with Pagination. + * bean wrapping comments information */ - public ResultListPage findCommentsByVideoId(final QueryCommentByVideo query) { - BoundStatement boundStatement = buildStatementVideoComments(query); // Parse input to create statement - ResultSet resultSet = dseSession.execute(boundStatement); // Execute statement to get a resultSet - return mapToCommentList(resultSet); // Iterate on resultSet to build result bean - } - - /** - * Search comment_by_video Asynchronously with Pagination. - */ - public CompletableFuture < ResultListPage > findCommentsByVideosIdAsync(final QueryCommentByVideo query) { - BoundStatement boundStatement = buildStatementVideoComments(query); // Parse input to create statement - ResultSetFuture resultSetFuture = dseSession.executeAsync(boundStatement); // Execute statement to get a FUTURE resultSet (Async) - return FutureUtils.asCompletableFuture(resultSetFuture) - .thenApplyAsync(this::mapToCommentList); // Iterate on resultSet to build result bean - } - - /** - * Execute a query against the 'comment_by_user' table. - */ - public ResultListPage findCommentsByUserId(final QueryCommentByUser query) { - // Like before but inlined as a boss - return mapToCommentList(dseSession.execute(buildStatementUserComments(query))); - } - - /** - * Execute a query against the 'comment_by_user' table (ASYNC). - */ - public CompletableFuture< ResultListPage > findCommentsByUserIdAsync(final QueryCommentByUser query) { - return FutureUtils.asCompletableFuture(dseSession.executeAsync(buildStatementUserComments(query))) - .thenApplyAsync(this::mapToCommentList); - } - + @QueryProvider( + providerClass = CommentDseDaoQueryProvider.class, + entityHelpers = { CommentByUserEntity.class, CommentByVideoEntity.class}) + void upsert(Comment comment); + /** - * Update a commet with the new text. + * Create a comment in both table ASYNCHRONOUSLY if not exist, update otherwise. + * This state as an example to implement async method. * - * @param c - * sample comment + * @param comment + * bean wrapping comments information */ - public void updateComment(final Comment c) { - Assert.notNull(c, "Comment object is required"); - Assert.notNull(c.getUserid(), "userid is required to update a comment"); - Assert.notNull(c.getVideoid(), "videoid is required to update a comment"); - Assert.notNull(c.getCommentid(), "commentid is required to update a comment"); - mappingManager.createAccessor(CommentAccessor.class) - .update(c.getCommentid(), c.getVideoid(), c.getUserid(), c.getComment()); - } + @QueryProvider( + providerClass = CommentDseDaoQueryProvider.class, + entityHelpers = { CommentByUserEntity.class, CommentByVideoEntity.class}) + CompletionStage upsertAsync(Comment comment); /** - * When you need a 'custom' query you may use an {@link Accessor} and provide explicity Query. + * Delete a comment base don * - * @author DataStax Developer Advocates team. - */ - @Accessor - public interface CommentAccessor { - - @Query("BEGIN BATCH\n" + - "UPDATE " + KILLRVIDEO_KEYSPACE + ".comments_by_user SET comment = :comment " + - "WHERE userid = :userid AND commentid= :commentid;\n" + - "UPDATE " + KILLRVIDEO_KEYSPACE + ".comments_by_video SET comment = :comment " + - "WHERE videoid = :videoid AND commentid= :commentid;\n" + - "APPLY BATCH;") - void update(@Param("commentid") UUID commentId, @Param("videoid") UUID videoId, - @Param("userid") UUID userId, @Param("comment") String comment); - } - - /** - * Delete a comment. - * - * @param comment - * entity with identifiers - */ - public void deleteComment(final Comment comment) { - // Check parameterss - Assert.notNull(comment, "Comment object is required"); - Assert.notNull(comment.getUserid(), "userId is required to delete a comment"); - Assert.notNull(comment.getVideoid(), "VideoId is required to delete a comment"); - Assert.notNull(comment.getCommentid(), "CommetId is required to delete a comment"); - - // Creating statements - Statement q1 = mapperCommentByVideo.deleteQuery(new CommentByVideo(comment)); - Statement q2 = mapperCommentByUser.deleteQuery(new CommentByUser(comment)); - LOGGER.debug("Deleting with :" + ((BoundStatement) q1).preparedStatement().getQueryString()); - LOGGER.debug("Deleting with :" + ((BoundStatement) q2).preparedStatement().getQueryString()); - - // Run as LWT Batch - dseSession.execute(new BatchStatement(BatchStatement.Type.LOGGED).add(q1).add(q2)); - } - - /** - * Create batch statement to insert a Comment in 2 tables at the same time. - * - * When inserting an entity in multiple Cassandra tables it is important that the same data is written to - * both tables to keep them in synchronization. The {@link BatchStatement} ensure light transactions and minimum - * consistency in the date. - * - * @see https://docs.datastax.com/en/latest-java-driver-api/com/datastax/driver/core/BatchStatement.html - * * @param comment - * current comment. + * bean wrapping comments information */ - private BatchStatement buildBatchStatementInsertComment(Comment comment) { - BatchStatement batchStatement = new BatchStatement(BatchStatement.Type.UNLOGGED) - .add(mapperCommentByVideo.saveQuery(new CommentByVideo(comment))) // Insert Query generate from annotated bean CommentByVideo - .add(mapperCommentByUser.saveQuery(new CommentByUser(comment))); // Insert Query generate from annotated bean CommentByUser - batchStatement.setDefaultTimestamp(System.currentTimeMillis()); - batchStatement.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM); - return batchStatement; - } + @QueryProvider( + providerClass = CommentDseDaoQueryProvider.class, + entityHelpers = { CommentByUserEntity.class, CommentByVideoEntity.class}) + void delete(Comment res); /** - * Implementation of mapping. + * As this query is dynamic (comment it can be present or not) we delegate the query to query + * builder. The result is mapped to expected {@link Comment} bean. * - * Explicitly set dateOfComment because the @Computed - * annotation set on the dateOfComment field when using QueryBuilder is not executed - * This gives us the "proper" return object expected for the response to the front-end - * UI. It does not function if this value is null or not the correct type. - * This is the reason why we did not (simply) use commentByVideoMapper.map(rs). - * - * @param rs + * @param query + * bean wrapping expecting parameters * @return - * target result + * a page of resultss */ - private ResultListPage mapToCommentList(ResultSet rs) { - ResultListPage result = new ResultListPage<>(); - Iterator iterResults = rs.iterator(); - // FIXME reference to documentation - // https://docs.datastax.com/en/developer/java-driver/3.5/manual/paging/ - - IntStream.range(0, rs.getAvailableWithoutFetching()) - .forEach(idx -> { - Row row = iterResults.next(); - Comment c = new Comment(); - c.setComment(row.getString(COLUMN_COMMENT)); - c.setUserid(row.getUUID(COLUMN_USERID)); - c.setCommentid(row.getUUID(COLUMN_COMMENTID)); - c.setVideoid(row.getUUID(COLUMN_VIDEOID)); - c.setDateOfComment(row.getTimestamp("comment_timestamp")); - result.getResults().add(c); - }); - result.setPagingState( - Optional.ofNullable(rs.getExecutionInfo().getPagingState()) - .map(PagingState::toString)); - return result; - } + @QueryProvider( + providerClass = CommentDseDaoQueryProvider.class, + entityHelpers = { CommentByUserEntity.class, CommentByVideoEntity.class}) + ResultListPage findCommentsByVideoId(final QueryCommentByVideo query); + + @QueryProvider( + providerClass = CommentDseDaoQueryProvider.class, + entityHelpers = { CommentByUserEntity.class, CommentByVideoEntity.class}) + CompletionStage> findCommentsByVideoIdAsync(final QueryCommentByVideo query); /** - * This statement is dynamic this is the reason why it is not implemented as a - * {@link PreparedStatement} but simple {@link BoundStatement}. + * As this query is dynamic (comment it can be present or not) we delegate the query to query + * builder. The result is mapped to expected {@link Comment} bean. * - * @param userId - * user unique identifier (required) - * @param commentId - * comment id as offsert or starting point for the query/page - * @param pageSize - * pageable query, here is the page size - * @param pageState - * provie the PagingState + * @param query + * bean wrapping expecting parameters * @return - * statement to retrieve comments + * a page of resultss */ - private BoundStatement buildStatementUserComments(final QueryCommentByUser query) { - BoundStatement statement = null; - if (query.getCommentId().isPresent()) { - statement = findCommentsByUserPageable.bind() - .setUUID(COLUMN_USERID, query.getUserId()) - .setUUID(COLUMN_COMMENTID, query.getCommentId().get()); - } else { - statement = findCommentsByUser.bind() - .setUUID(COLUMN_USERID, query.getUserId()); - } - if (query.getPageState().isPresent() && query.getPageState().get().length() > 0) { - statement.setPagingState(PagingState.fromString(query.getPageState().get())); - } - statement.setFetchSize(query.getPageSize()); - statement.setConsistencyLevel(ConsistencyLevel.QUORUM); - return statement; - } + @QueryProvider( + providerClass = CommentDseDaoQueryProvider.class, + entityHelpers = { CommentByUserEntity.class, CommentByVideoEntity.class}) + ResultListPage findCommentsByUserId(final QueryCommentByUser query); + + @QueryProvider( + providerClass = CommentDseDaoQueryProvider.class, + entityHelpers = { CommentByUserEntity.class, CommentByVideoEntity.class}) + CompletionStage> findCommentsByUserIdAsync(final QueryCommentByUser query); - /** - * Init statement based on comment tag. - * - * @param request - * current request - * @return - * statement - */ - private BoundStatement buildStatementVideoComments(final QueryCommentByVideo query) { - BoundStatement statement = null; - if (query.getCommentId().isPresent()) { - statement = findCommentsByVideoPageable.bind() - .setUUID(COLUMN_VIDEOID, query.getVideoId()) - .setUUID(COLUMN_COMMENTID, query.getCommentId().get()); - } else { - statement = findCommentsByVideo.bind() - .setUUID(COLUMN_VIDEOID, query.getVideoId()); - } - if (query.getPageState().isPresent() && query.getPageState().get().length() > 0) { - statement.setPagingState(PagingState.fromString(query.getPageState().get())); - } - statement.setFetchSize(query.getPageSize()); - statement.setConsistencyLevel(ConsistencyLevel.QUORUM); - return statement; - } - } diff --git a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoMapper.java b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoMapper.java new file mode 100644 index 00000000..ae4a95e8 --- /dev/null +++ b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoMapper.java @@ -0,0 +1,26 @@ +package com.killrvideo.service.comment.dao; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.mapper.annotations.Dao; +import com.datastax.oss.driver.api.mapper.annotations.DaoFactory; +import com.datastax.oss.driver.api.mapper.annotations.DaoKeyspace; +import com.datastax.oss.driver.api.mapper.annotations.Mapper; + +/** + * Annotated as {@link Mapper} will generate working {@link Dao}. + */ +@Mapper +public interface CommentDseDaoMapper { + + /** + * Initialization of Dao {@link CommentDseDao} + * + * @param keyspace + * working keyspace name + * @return + * instanciation with the mappers + */ + @DaoFactory + CommentDseDao commentDao(@DaoKeyspace CqlIdentifier keyspace); + +} diff --git a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoQueryProvider.java b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoQueryProvider.java new file mode 100644 index 00000000..ec25055c --- /dev/null +++ b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoQueryProvider.java @@ -0,0 +1,243 @@ +package com.killrvideo.service.comment.dao; + +import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.bindMarker; +import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.selectFrom; +import static com.datastax.oss.driver.api.querybuilder.relation.Relation.column; + +import java.nio.ByteBuffer; +import java.util.concurrent.CompletionStage; + +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.cql.BatchStatement; +import com.datastax.oss.driver.api.core.cql.BoundStatement; +import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder; +import com.datastax.oss.driver.api.core.cql.DefaultBatchType; +import com.datastax.oss.driver.api.core.cql.PreparedStatement; +import com.datastax.oss.driver.api.mapper.MapperContext; +import com.datastax.oss.driver.api.mapper.annotations.QueryProvider; +import com.datastax.oss.driver.api.mapper.entity.EntityHelper; +import com.datastax.oss.driver.api.mapper.entity.saving.NullSavingStrategy; +import com.datastax.oss.driver.api.querybuilder.select.Selector; +import com.killrvideo.dse.dao.DseSchema; +import com.killrvideo.dse.dto.ResultListPage; +import com.killrvideo.service.comment.dao.dto.Comment; +import com.killrvideo.service.comment.dao.dto.CommentByUserEntity; +import com.killrvideo.service.comment.dao.dto.CommentByVideoEntity; +import com.killrvideo.service.comment.grpc.dto.QueryCommentByUser; +import com.killrvideo.service.comment.grpc.dto.QueryCommentByVideo; + +public class CommentDseDaoQueryProvider implements DseSchema { + + private final CqlSession dseSession; + + private final EntityHelper entityHelperCommentsByUser; + private final EntityHelper entityHelperCommentsByVideo; + + private PreparedStatement psInsertCommentUser; + private PreparedStatement psDeleteCommentUser; + private PreparedStatement psInsertCommentVideo; + private PreparedStatement psDeleteCommentVideo; + + /** Precompile statements to speed up queries. */ + private PreparedStatement psFindCommentsByUser; + private PreparedStatement psFindCommentsByUserPageable; + private PreparedStatement psFindCommentsByVideo; + private PreparedStatement psFindCommentsByVideoPageable; + + /** + * Constructor invoked by the DataStax driver based on Annotation {@link QueryProvider} + * set on class {@link CommentDseDao}. + * + * @param context + * context to extrat dse session + * @param helperUser + * entity helper to interact with bean {@link CommentByUserEntity} + * @param helperVideo + * entity helper to interact with bean {@link CommentByVideoEntity} + */ + public CommentDseDaoQueryProvider(MapperContext context, + EntityHelper helperUser, + EntityHelper helperVideo) { + + this.dseSession = context.getSession(); + this.entityHelperCommentsByUser = helperUser; + this.entityHelperCommentsByVideo = helperVideo; + + this.psInsertCommentUser = dseSession.prepare(entityHelperCommentsByUser.insert().asCql()); + this.psDeleteCommentUser = dseSession.prepare(entityHelperCommentsByUser.deleteByPrimaryKey().asCql()); + this.psInsertCommentVideo = dseSession.prepare(entityHelperCommentsByVideo.insert().asCql()); + this.psDeleteCommentVideo = dseSession.prepare(entityHelperCommentsByVideo.deleteByPrimaryKey().asCql()); + + psFindCommentsByUser = dseSession.prepare( + selectFrom(TABLENAME_COMMENTS_BY_USER_) + .column(COMMENTS_COLUMN_USERID).column(COMMENTS_COLUMN_COMMENTID) + .column(COMMENTS_COLUMN_VIDEOID).column(COMMENTS_COLUMN_COMMENT) + .function("toTimestamp", Selector.column(COMMENTS_COLUMN_COMMENTID)).as("comment_timestamp") + .where(column(COMMENTS_COLUMN_USERID).isEqualTo(bindMarker(COMMENTS_COLUMN_USERID))) + .build()); + psFindCommentsByUserPageable = dseSession.prepare( + selectFrom(TABLENAME_COMMENTS_BY_USER) + .column(COMMENTS_COLUMN_USERID).column(COMMENTS_COLUMN_COMMENTID) + .column(COMMENTS_COLUMN_VIDEOID).column(COMMENTS_COLUMN_COMMENT) + .function("toTimestamp", Selector.column(COMMENTS_COLUMN_COMMENTID)).as("comment_timestamp") + .where(column(COMMENTS_COLUMN_USERID).isEqualTo(bindMarker(COMMENTS_COLUMN_USERID)), + column(COMMENTS_COLUMN_COMMENTID).isLessThanOrEqualTo(bindMarker(COMMENTS_COLUMN_COMMENTID)) + ).build()); + + psFindCommentsByVideo = dseSession.prepare( + selectFrom(TABLENAME_COMMENTS_BY_VIDEO) + .column(COMMENTS_COLUMN_USERID).column(COMMENTS_COLUMN_USERID) + .column(COMMENTS_COLUMN_VIDEOID).column(COMMENTS_COLUMN_COMMENT) + .column(COMMENTS_COLUMN_COMMENTID) + .function("toTimestamp", Selector.column(COMMENTS_COLUMN_COMMENTID)).as("comment_timestamp") + .where(column(COMMENTS_COLUMN_VIDEOID).isEqualTo(bindMarker(COMMENTS_COLUMN_VIDEOID))) + .build()); + psFindCommentsByVideoPageable = dseSession.prepare( + selectFrom(TABLENAME_COMMENTS_BY_VIDEO) + .column(COMMENTS_COLUMN_USERID).column(COMMENTS_COLUMN_USERID) + .column(COMMENTS_COLUMN_VIDEOID).column(COMMENTS_COLUMN_COMMENT) + .column(COMMENTS_COLUMN_COMMENTID) + .function("toTimestamp", Selector.column(COMMENTS_COLUMN_COMMENTID)).as("comment_timestamp") + .where(column(COMMENTS_COLUMN_VIDEOID).isEqualTo(bindMarker(COMMENTS_COLUMN_VIDEOID)), + column(COMMENTS_COLUMN_COMMENTID).isLessThanOrEqualTo(bindMarker(COMMENTS_COLUMN_COMMENTID))) + .build()); + } + + /** Javadoc in {@link CommentDseDao} */ + public void upsert(Comment comment) { + dseSession.execute(_buildStatementInsertComment(comment)); + } + + /** Javadoc in {@link CommentDseDao} */ + public CompletionStage upsertAsync(Comment comment) { + return dseSession.executeAsync(_buildStatementInsertComment(comment)) + .thenApply(rs -> null); + } + + /** Javadoc in {@link CommentDseDao} */ + void delete(Comment comment) { + CommentByUserEntity c1 = new CommentByUserEntity(); + c1.setUserid(comment.getUserid()); + c1.setCommentid(comment.getCommentid()); + CommentByVideoEntity c2 = new CommentByVideoEntity(); + c2.setVideoid(comment.getVideoid()); + c2.setCommentid(comment.getCommentid()); + dseSession.execute( + BatchStatement.builder(DefaultBatchType.LOGGED) + .addStatement(bind(psDeleteCommentUser, c1, entityHelperCommentsByUser)) + .addStatement(bind(psDeleteCommentVideo, c2, entityHelperCommentsByVideo)) + .build()); + } + + /** Javadoc in {@link CommentDseDao} */ + public ResultListPage findCommentsByVideoId(final QueryCommentByVideo query) { + return new ResultListPage<>(dseSession.execute(buildStatementVideoComments(query)) + .map(entityHelperCommentsByVideo::get)); + } + + /** Javadoc in {@link CommentDseDao} */ + public CompletionStage> findCommentsByVideoIdAsync(final QueryCommentByVideo query) { + return dseSession.executeAsync(buildStatementVideoComments(query)) + .thenApply(ars -> ars.map(entityHelperCommentsByVideo::get)) + .thenApply(ResultListPage::new); + } + + /** Javadoc in {@link CommentDseDao} */ + public ResultListPage findCommentsByUserId(final QueryCommentByUser query) { + return new ResultListPage<>(dseSession.execute(buildStatementUserComments(query)) + .map(entityHelperCommentsByUser::get)); + } + + /** Javadoc in {@link CommentDseDao} */ + public CompletionStage> findCommentsByUserIdAsync(final QueryCommentByUser query) { + return dseSession.executeAsync(buildStatementUserComments(query)) + .thenApply(ars -> ars.map(entityHelperCommentsByUser::get)) + .thenApply(ResultListPage::new); + } + + /** + * Init statement based on comment tag. + * + * @param request + * current request + * @return + * statement + */ + private BoundStatement buildStatementVideoComments(final QueryCommentByVideo query) { + BoundStatement statement = null; + if (query.getCommentId().isPresent()) { + statement = psFindCommentsByVideoPageable.bind() + .setUuid(COMMENTS_COLUMN_VIDEOID, query.getVideoId()) + .setUuid(COMMENTS_COLUMN_COMMENTID, query.getCommentId().get()); + } else { + statement = psFindCommentsByVideo.bind() + .setUuid(COMMENTS_COLUMN_VIDEOID, query.getVideoId()); + } + if (query.getPageState().isPresent() && query.getPageState().get().length() > 0) { + statement.setPagingState(ByteBuffer.wrap(query.getPageState().get().getBytes())); + } + statement.setPageSize(query.getPageSize()); + return statement; + } + + /** + * This statement is dynamic this is the reason why it is not implemented as a + * {@link PreparedStatement} but simple {@link BoundStatement}. + * + * @param userId + * user unique identifier (required) + * @param commentId + * comment id as offsert or starting point for the query/page + * @param pageSize + * pageable query, here is the page size + * @param pageState + * provie the PagingState + * @return + * statement to retrieve comments + */ + private BoundStatement buildStatementUserComments(final QueryCommentByUser query) { + BoundStatement statement = null; + if (query.getCommentId().isPresent()) { + statement = psFindCommentsByUserPageable.bind() + .setUuid(COMMENTS_COLUMN_USERID, query.getUserId()) + .setUuid(COMMENTS_COLUMN_COMMENTID, query.getCommentId().get()); + } else { + statement = psFindCommentsByUser.bind() + .setUuid(COMMENTS_COLUMN_USERID, query.getUserId()); + } + if (query.getPageState().isPresent() && query.getPageState().get().length() > 0) { + statement.setPagingState(ByteBuffer.wrap(query.getPageState().get().getBytes())); + } + statement.setPageSize(query.getPageSize()); + return statement; + } + + /** + * Cassandra and DSE Session are stateless. For each request a coordinator is chosen + * and execute query against the cluster. The Driver is stateful, it has to maintain some + * network connections pool, here we properly cleanup things. + * + * @throws Exception + * error on cleanup. + */ + @Override + protected void finalize() throws Throwable { + if (dseSession != null && !dseSession.isClosed()) { + dseSession.close(); + } + } + + private BatchStatement _buildStatementInsertComment(Comment comment) { + return BatchStatement.builder(DefaultBatchType.LOGGED) + .addStatement(bind(psInsertCommentUser, new CommentByUserEntity(comment), entityHelperCommentsByUser)) + .addStatement(bind(psInsertCommentVideo, new CommentByVideoEntity(comment), entityHelperCommentsByVideo)) + .build(); + } + + protected static BoundStatement bind(PreparedStatement preparedStatement, T entity, EntityHelper entityHelper) { + BoundStatementBuilder boundStatement = preparedStatement.boundStatementBuilder(); + entityHelper.set(entity, boundStatement, NullSavingStrategy.DO_NOT_SET); + return boundStatement.build(); + } + +} diff --git a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dto/Comment.java b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/dto/Comment.java similarity index 71% rename from killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dto/Comment.java rename to killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/dto/Comment.java index 1880ee2e..b8fff7e5 100644 --- a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dto/Comment.java +++ b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/dto/Comment.java @@ -1,5 +1,6 @@ -package com.killrvideo.service.comment.dto; +package com.killrvideo.service.comment.dao.dto; +import java.io.Serializable; import java.util.Date; import java.util.UUID; @@ -7,45 +8,40 @@ import org.hibernate.validator.constraints.Length; -import com.datastax.driver.mapping.annotations.ClusteringColumn; -import com.datastax.driver.mapping.annotations.Column; -import com.datastax.driver.mapping.annotations.Computed; -import com.killrvideo.dse.dto.AbstractEntity; +import com.datastax.oss.driver.api.mapper.annotations.ClusteringColumn; +import com.datastax.oss.driver.api.mapper.annotations.CqlName; +import com.killrvideo.dse.dao.DseSchema; +import com.killrvideo.dse.utils.DseUtils; /** * Bean standing for comment on video. * * @author DataStax Developer Advocates team. */ -public class Comment extends AbstractEntity { +public class Comment implements Serializable, DseSchema { /** Serial. */ private static final long serialVersionUID = 7675521710612951368L; - /** Column names in the DB. */ - public static final String COLUMN_USERID = "userid"; - public static final String COLUMN_VIDEOID = "videoid"; - public static final String COLUMN_COMMENTID = "commentid"; - public static final String COLUMN_COMMENT = "comment"; - - @Column @NotNull + @NotNull + @CqlName(COMMENTS_COLUMN_USERID) protected UUID userid; @NotNull - @Column + @CqlName(COMMENTS_COLUMN_VIDEOID) protected UUID videoid; @NotNull @ClusteringColumn + @CqlName(COMMENTS_COLUMN_COMMENTID) protected UUID commentid; @Length(min = 1, message = "The comment must not be empty") - @Column + @CqlName(COMMENTS_COLUMN_COMMENT) protected String comment; - @NotNull - @Computed("toTimestamp(commentid)") - private Date dateOfComment; + //@Computed("toTimestamp("+ COMMENTS_COLUMN_COMMENTID+ ")") + //private Date dateOfComment; /** * Default constructor. @@ -123,17 +119,9 @@ public void setComment(String comment) { * current value of 'dateOfComment' */ public Date getDateOfComment() { - return dateOfComment; + return new Date(DseUtils.getTimeFromUUID(commentid)); } - /** - * Setter for attribute 'dateOfComment'. - * @param dateOfComment - * new value for 'dateOfComment ' - */ - public void setDateOfComment(Date dateOfComment) { - this.dateOfComment = dateOfComment; - } /** * Getter for attribute 'userid'. diff --git a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dto/CommentByUser.java b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/dto/CommentByUserEntity.java similarity index 57% rename from killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dto/CommentByUser.java rename to killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/dto/CommentByUserEntity.java index fac76b35..6bb7effa 100644 --- a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dto/CommentByUser.java +++ b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/dto/CommentByUserEntity.java @@ -1,22 +1,20 @@ -package com.killrvideo.service.comment.dto; +package com.killrvideo.service.comment.dao.dto; import java.util.UUID; -import com.datastax.driver.mapping.annotations.PartitionKey; -import com.datastax.driver.mapping.annotations.Table; -import com.killrvideo.model.CommonConstants; -import com.killrvideo.service.comment.dao.CommentDseDao; +import com.datastax.oss.driver.api.mapper.annotations.CqlName; +import com.datastax.oss.driver.api.mapper.annotations.Entity; +import com.datastax.oss.driver.api.mapper.annotations.PartitionKey; +import com.killrvideo.dse.dao.DseSchema; /** * Specialization for USER. * * @author DataStax Developer Advocates team. */ -@Table(name= - CommentDseDao.TABLENAME_COMMENTS_BY_USER, - keyspace= - CommonConstants.KILLRVIDEO_KEYSPACE) -public class CommentByUser extends Comment { +@Entity +@CqlName(DseSchema.TABLENAME_COMMENTS_BY_USER) +public class CommentByUserEntity extends Comment { /** Serial. */ private static final long serialVersionUID = 1453554109222565840L; @@ -24,14 +22,14 @@ public class CommentByUser extends Comment { /** * Default constructor. */ - public CommentByUser() {} + public CommentByUserEntity() {} /** * Copy constructor. * * @param c */ - public CommentByUser(Comment c) { + public CommentByUserEntity(Comment c) { this.commentid = c.getCommentid(); this.userid = c.getUserid(); this.videoid = c.getVideoid(); diff --git a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dto/CommentByVideo.java b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/dto/CommentByVideoEntity.java similarity index 55% rename from killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dto/CommentByVideo.java rename to killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/dto/CommentByVideoEntity.java index c360a026..1886a758 100644 --- a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dto/CommentByVideo.java +++ b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/dto/CommentByVideoEntity.java @@ -1,28 +1,28 @@ -package com.killrvideo.service.comment.dto; +package com.killrvideo.service.comment.dao.dto; import java.util.UUID; -import com.datastax.driver.mapping.annotations.PartitionKey; -import com.datastax.driver.mapping.annotations.Table; -import com.killrvideo.model.CommonConstants; -import com.killrvideo.service.comment.dao.CommentDseDao; +import com.datastax.oss.driver.api.mapper.annotations.CqlName; +import com.datastax.oss.driver.api.mapper.annotations.Entity; +import com.datastax.oss.driver.api.mapper.annotations.PartitionKey; +import com.killrvideo.dse.dao.DseSchema; /** * Specialization for VIDEO. * * @author DataStax Developer Advocates team. */ -@Table(name=CommentDseDao.TABLENAME_COMMENTS_BY_VIDEO, - keyspace=CommonConstants.KILLRVIDEO_KEYSPACE) -public class CommentByVideo extends Comment { +@Entity +@CqlName(DseSchema.TABLENAME_COMMENTS_BY_VIDEO) +public class CommentByVideoEntity extends Comment { /** Serial. */ private static final long serialVersionUID = -6738790629520080307L; - public CommentByVideo() { + public CommentByVideoEntity() { } - public CommentByVideo(Comment c) { + public CommentByVideoEntity(Comment c) { this.commentid = c.getCommentid(); this.userid = c.getUserid(); this.videoid = c.getVideoid(); diff --git a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/grpc/CommentsServiceGrpc.java b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/grpc/CommentsServiceGrpc.java index a48493ba..1e0483b6 100644 --- a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/grpc/CommentsServiceGrpc.java +++ b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/grpc/CommentsServiceGrpc.java @@ -11,7 +11,6 @@ import java.time.Duration; import java.time.Instant; -import java.util.concurrent.CompletableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,12 +18,12 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import com.killrvideo.grpc.GrpcMappingUtils; import com.killrvideo.messaging.dao.MessagingDao; import com.killrvideo.service.comment.dao.CommentDseDao; -import com.killrvideo.service.comment.dto.Comment; -import com.killrvideo.service.comment.dto.QueryCommentByUser; -import com.killrvideo.service.comment.dto.QueryCommentByVideo; -import com.killrvideo.utils.GrpcMappingUtils; +import com.killrvideo.service.comment.dao.dto.Comment; +import com.killrvideo.service.comment.grpc.dto.QueryCommentByUser; +import com.killrvideo.service.comment.grpc.dto.QueryCommentByVideo; import io.grpc.Status; import io.grpc.stub.StreamObserver; @@ -54,9 +53,6 @@ public class CommentsServiceGrpc extends CommentsServiceImplBase { @Autowired private MessagingDao messagingDao; - - @Value("${killrvideo.discovery.services.comment : CommentsService}") - private String serviceKey; @Value("${killrvideo.messaging.destinations.commentCreated : topic-kv-commentCreation}") private String messageDestination; @@ -81,19 +77,15 @@ public void commentOnVideo(final CommentOnVideoRequest grpcReq, StreamObserver futureDse = dseCommentDao.insertCommentAsync(q); - - // If OK, then send Message to Kafka - CompletableFuture futureDseThensKafka = futureDse.thenCompose(rs -> { - return messagingDao.sendEvent(messageDestination, UserCommentedOnVideo.newBuilder() - .setCommentId(grpcReq.getCommentId()) - .setVideoId(grpcReq.getVideoId()) - .setUserId(grpcReq.getUserId()) - .setCommentTimestamp(GrpcMappingUtils.instantToTimeStamp(Instant.now())) - .build()); - }); - - futureDseThensKafka.whenComplete((result, error) -> { + dseCommentDao.upsertAsync(q) + .thenApply(rs -> { + return messagingDao.sendEvent(messageDestination, UserCommentedOnVideo.newBuilder() + .setCommentId(grpcReq.getCommentId()) + .setVideoId(grpcReq.getVideoId()) + .setUserId(grpcReq.getUserId()) + .setCommentTimestamp(GrpcMappingUtils.instantToTimeStamp(Instant.now())) + .build()); + }).whenComplete((result, error) -> { if (error != null ) { traceError("commentOnVideo", starts, error); grpcResObserver.onError(Status.INTERNAL.withCause(error).asRuntimeException()); @@ -119,14 +111,14 @@ public void getVideoComments(final GetVideoCommentsRequest grpcReq, StreamObserv QueryCommentByVideo query = mapFromGrpcVideoCommentToDseQuery(grpcReq); // ASYNCHRONOUS works with ComputableFuture - dseCommentDao.findCommentsByVideosIdAsync(query).whenComplete((result, error) -> { + dseCommentDao.findCommentsByVideoIdAsync(query).whenComplete((result, error) -> { if (result != null) { traceSuccess( "getVideoComments", starts); responseObserver.onNext(mapFromDseVideoCommentToGrpcResponse(result)); responseObserver.onCompleted(); } else if (error != null){ traceError("getVideoComments", starts, error); - messagingDao.sendErrorEvent(getServiceKey(), error); + messagingDao.sendErrorEvent("CommentService", error); responseObserver.onError(error); } }); @@ -156,7 +148,7 @@ public void getUserComments(final GetUserCommentsRequest grpcReq, StreamObserver responseObserver.onCompleted(); } else if (error != null){ traceError("getUserComments", starts, error); - messagingDao.sendErrorEvent(getServiceKey(), error); + messagingDao.sendErrorEvent("CommentService", error); responseObserver.onError(error); } }); @@ -188,14 +180,4 @@ private void traceError(String method, Instant starts, Throwable t) { LOGGER.error("An error occured in {} after {}", method, Duration.between(starts, Instant.now()), t); } - /** - * Getter accessor for attribute 'serviceKey'. - * - * @return - * current value of 'serviceKey' - */ - public String getServiceKey() { - return serviceKey; - } - } diff --git a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/grpc/CommentsServiceGrpcMapper.java b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/grpc/CommentsServiceGrpcMapper.java index fb6eafb1..3716bfff 100644 --- a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/grpc/CommentsServiceGrpcMapper.java +++ b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/grpc/CommentsServiceGrpcMapper.java @@ -1,12 +1,12 @@ package com.killrvideo.service.comment.grpc; +import static com.killrvideo.grpc.GrpcMappingUtils.dateToTimestamp; +import static com.killrvideo.grpc.GrpcMappingUtils.uuidToTimeUuid; +import static com.killrvideo.grpc.GrpcMappingUtils.uuidToUuid; import static com.killrvideo.service.comment.grpc.CommentsServiceGrpcValidator.initErrorString; import static com.killrvideo.service.comment.grpc.CommentsServiceGrpcValidator.notEmpty; import static com.killrvideo.service.comment.grpc.CommentsServiceGrpcValidator.positive; import static com.killrvideo.service.comment.grpc.CommentsServiceGrpcValidator.validate; -import static com.killrvideo.utils.GrpcMappingUtils.dateToTimestamp; -import static com.killrvideo.utils.GrpcMappingUtils.uuidToTimeUuid; -import static com.killrvideo.utils.GrpcMappingUtils.uuidToUuid; import static org.apache.commons.lang3.StringUtils.isBlank; import java.util.Optional; @@ -16,9 +16,11 @@ import org.springframework.util.Assert; import com.killrvideo.dse.dto.ResultListPage; -import com.killrvideo.service.comment.dto.Comment; -import com.killrvideo.service.comment.dto.QueryCommentByUser; -import com.killrvideo.service.comment.dto.QueryCommentByVideo; +import com.killrvideo.service.comment.dao.dto.Comment; +import com.killrvideo.service.comment.dao.dto.CommentByUserEntity; +import com.killrvideo.service.comment.dao.dto.CommentByVideoEntity; +import com.killrvideo.service.comment.grpc.dto.QueryCommentByUser; +import com.killrvideo.service.comment.grpc.dto.QueryCommentByVideo; import io.grpc.stub.StreamObserver; import killrvideo.comments.CommentsServiceOuterClass; @@ -129,7 +131,7 @@ public static QueryCommentByUser mapFromGrpcUserCommentToDseQuery(GetUserComment } // Map from CommentDseDao response bean to expected GRPC object. - public static GetVideoCommentsResponse mapFromDseVideoCommentToGrpcResponse(ResultListPage dseRes) { + public static GetVideoCommentsResponse mapFromDseVideoCommentToGrpcResponse(ResultListPage dseRes) { final GetVideoCommentsResponse.Builder builder = GetVideoCommentsResponse.newBuilder(); for (Comment c : dseRes.getResults()) { builder.setVideoId(uuidToUuid(c.getVideoid())); @@ -145,7 +147,7 @@ public static GetVideoCommentsResponse mapFromDseVideoCommentToGrpcResponse(Resu } // Map from CommentDseDao response bean to expected GRPC object. - public static GetUserCommentsResponse mapFromDseUserCommentToGrpcResponse(ResultListPage dseRes) { + public static GetUserCommentsResponse mapFromDseUserCommentToGrpcResponse(ResultListPage dseRes) { final GetUserCommentsResponse.Builder builder = GetUserCommentsResponse.newBuilder(); for (Comment c : dseRes.getResults()) { builder.setUserId(uuidToUuid(c.getUserid())); diff --git a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dto/QueryCommentByUser.java b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/grpc/dto/QueryCommentByUser.java similarity index 97% rename from killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dto/QueryCommentByUser.java rename to killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/grpc/dto/QueryCommentByUser.java index 9c2d7c3a..fcf882b6 100644 --- a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dto/QueryCommentByUser.java +++ b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/grpc/dto/QueryCommentByUser.java @@ -1,4 +1,4 @@ -package com.killrvideo.service.comment.dto; +package com.killrvideo.service.comment.grpc.dto; import java.util.Optional; import java.util.UUID; diff --git a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dto/QueryCommentByVideo.java b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/grpc/dto/QueryCommentByVideo.java similarity index 97% rename from killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dto/QueryCommentByVideo.java rename to killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/grpc/dto/QueryCommentByVideo.java index 601c4125..7dd72d91 100644 --- a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dto/QueryCommentByVideo.java +++ b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/grpc/dto/QueryCommentByVideo.java @@ -1,4 +1,4 @@ -package com.killrvideo.service.comment.dto; +package com.killrvideo.service.comment.grpc.dto; import java.util.Optional; import java.util.UUID; diff --git a/killrvideo-service-comments/src/test/java/com/killrvideo/service/comment/test/CommentDseDaoTest.java b/killrvideo-service-comments/src/test/java/com/killrvideo/service/comment/test/CommentDseDaoTest.java new file mode 100644 index 00000000..1acb256a --- /dev/null +++ b/killrvideo-service-comments/src/test/java/com/killrvideo/service/comment/test/CommentDseDaoTest.java @@ -0,0 +1,209 @@ +package com.killrvideo.service.comment.test; + +import static com.datastax.oss.driver.api.querybuilder.SchemaBuilder.createTable; +import static com.killrvideo.dse.utils.DseUtils.createKeySpaceSimpleStrategy; +import static com.killrvideo.dse.utils.DseUtils.isTableEmpty; +import static com.killrvideo.dse.utils.DseUtils.truncateTable; + +import java.net.InetSocketAddress; +import java.util.UUID; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.GenericContainer; + +import com.datastax.dse.driver.api.core.DseSession; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder; +import com.datastax.oss.driver.api.core.type.DataTypes; +import com.datastax.oss.driver.internal.core.metadata.DefaultEndPoint; +import com.killrvideo.dse.dao.DseSchema; +import com.killrvideo.dse.dto.ResultListPage; +import com.killrvideo.service.comment.dao.CommentDseDao; +import com.killrvideo.service.comment.dao.CommentDseDaoMapperBuilder; +import com.killrvideo.service.comment.dao.dto.Comment; +import com.killrvideo.service.comment.dao.dto.CommentByUserEntity; +import com.killrvideo.service.comment.dao.dto.CommentByVideoEntity; +import com.killrvideo.service.comment.grpc.dto.QueryCommentByUser; +import com.killrvideo.service.comment.grpc.dto.QueryCommentByVideo; + +/** + * Integration Test for Comment Services. + */ +public class CommentDseDaoTest implements DseSchema { + + protected static CqlIdentifier dseKeyspace = CqlIdentifier.fromCql("killrvideo_test_comments"); + protected static int dseKeyspaceRF = 1; + protected static InetSocketAddress dseContactPoint = new InetSocketAddress("localhost", 9042);; + protected static String dseLocalDc = "dc1"; + protected static DseSession dseSession = null; + protected static GenericContainer dseContainer = null; + + protected static CommentDseDao commentDseDao = null; + + @SuppressWarnings("resource") + protected static void startDseContainer() { + dseContainer = new GenericContainer<>("datastax/dse-server:6.7.2") + .withExposedPorts(9042) + .withCommand("-s -g") + .withEnv("DS_LICENSE", "accept") + .withEnv("DC", "dc1"); + dseContainer.start(); + + dseContactPoint = new InetSocketAddress( + dseContainer.getContainerIpAddress(), + dseContainer.getMappedPort(9042)); + } + + protected static void stopDseContainer() { + if (dseContainer != null) { + dseContainer.stop(); + } + } + + @BeforeAll + public static void setupDse() { + // startDseContainer(); + dseSession = DseSession.builder() + .addContactEndPoint(new DefaultEndPoint(dseContactPoint)) + .withLocalDatacenter(dseLocalDc).build(); + createKeySpaceSimpleStrategy(dseSession, dseKeyspace.asInternal(), dseKeyspaceRF); + createTableComments(dseSession); + commentDseDao = new CommentDseDaoMapperBuilder(dseSession).build().commentDao(dseKeyspace); + } + + @BeforeEach + public void truncate() { + truncateTable(dseSession, dseKeyspace, TABLENAME_COMMENTS_BY_USER_); + truncateTable(dseSession, dseKeyspace, TABLENAME_COMMENTS_BY_VIDEO_); + } + + @AfterAll + public static void cleanUp() { + //dropKeyspace(dseSession, dseKeyspace.asInternal()); + dseSession.close(); + stopDseContainer(); + } + + @Test + @DisplayName("Upsert a new comment") + public void shoud_create_record_when_table_empty() { + + // Given + Assertions.assertTrue(isTableEmpty(dseSession, dseKeyspace, TABLENAME_COMMENTS_BY_USER_)); + Assertions.assertTrue(isTableEmpty(dseSession, dseKeyspace, TABLENAME_COMMENTS_BY_VIDEO_)); + + // When + Comment c1= new Comment(); + c1.setComment("Killrvideo Rocks!"); + c1.setUserid(UUID.fromString("2d32af9c-7889-4256-aedb-b458976f781c")); + c1.setVideoid(UUID.fromString("6fd4df0a-74ca-4891-ae3b-0c16e37880ed")); + c1.setCommentid(UUID.fromString("79ff95e0-befc-11e9-9051-714df5616ec8")); + commentDseDao.upsert(c1); + + // Then + Assertions.assertFalse(isTableEmpty(dseSession, dseKeyspace, TABLENAME_COMMENTS_BY_USER_)); + Assertions.assertFalse(isTableEmpty(dseSession, dseKeyspace, TABLENAME_COMMENTS_BY_VIDEO_)); + } + + @Test + @DisplayName("Upsert an existing comment") + public void shoud_update_record_when_record_exist() { + // Given + Comment c1= new Comment(); + c1.setComment("Killrvideo Rocks!"); + c1.setUserid(UUID.fromString("2d32af9c-7889-4256-aedb-b458976f781c")); + c1.setVideoid(UUID.fromString("6fd4df0a-74ca-4891-ae3b-0c16e37880ed")); + c1.setCommentid(UUID.fromString("79ff95e0-befc-11e9-9051-714df5616ec8")); + commentDseDao.upsert(c1); + c1.setComment("updated Comment"); + commentDseDao.upsert(c1); + + // When + QueryCommentByUser queryUser = new QueryCommentByUser(UUID.fromString("2d32af9c-7889-4256-aedb-b458976f781c")); + ResultListPage res = commentDseDao.findCommentsByUserId(queryUser); + + // Then + Assertions.assertEquals(1, res.getResults().size()); + Assertions.assertEquals("updated Comment", res.getResults().get(0).getComment()); + } + + @Test + @DisplayName("Delete an existing comment") + public void shoud_delete_record_when_record_exist() { + // Given + Comment c1= new Comment(); + c1.setComment("Killrvideo Rocks!"); + c1.setUserid(UUID.fromString("2d32af9c-7889-4256-aedb-b458976f781c")); + c1.setVideoid(UUID.fromString("6fd4df0a-74ca-4891-ae3b-0c16e37880ed")); + c1.setCommentid(UUID.fromString("79ff95e0-befc-11e9-9051-714df5616ec8")); + commentDseDao.upsert(c1); + + // When + commentDseDao.delete(c1); + + // Then + Assertions.assertTrue(isTableEmpty(dseSession, dseKeyspace, TABLENAME_COMMENTS_BY_USER_)); + Assertions.assertTrue(isTableEmpty(dseSession, dseKeyspace, TABLENAME_COMMENTS_BY_VIDEO_)); + } + + @Test + @DisplayName("Search by user id") + public void should_find_record_when_searchByUser() { + // Given + Comment c1= new Comment(); + c1.setComment("Killrvideo Rocks!"); + c1.setUserid(UUID.fromString("2d32af9c-7889-4256-aedb-b458976f781c")); + c1.setVideoid(UUID.fromString("6fd4df0a-74ca-4891-ae3b-0c16e37880ed")); + c1.setCommentid(UUID.fromString("79ff95e0-befc-11e9-9051-714df5616ec8")); + commentDseDao.upsert(c1); + // When + QueryCommentByUser queryUser = new QueryCommentByUser(UUID.fromString("2d32af9c-7889-4256-aedb-b458976f781c")); + ResultListPage res = commentDseDao.findCommentsByUserId(queryUser); + // Then + Assertions.assertEquals(1, res.getResults().size()); + Assertions.assertEquals(UUID.fromString("2d32af9c-7889-4256-aedb-b458976f781c"), res.getResults().get(0).getUserid()); + } + + @Test + @DisplayName("Search by video id") + public void should_find_record_when_searchByVideo() { + // Given + Comment c1= new Comment(); + c1.setComment("Killrvideo Rocks!"); + c1.setUserid(UUID.fromString("2d32af9c-7889-4256-aedb-b458976f781c")); + c1.setVideoid(UUID.fromString("6fd4df0a-74ca-4891-ae3b-0c16e37880ed")); + c1.setCommentid(UUID.fromString("79ff95e0-befc-11e9-9051-714df5616ec8")); + commentDseDao.upsert(c1); + // When + QueryCommentByVideo queryVideo = new QueryCommentByVideo(UUID.fromString("6fd4df0a-74ca-4891-ae3b-0c16e37880ed")); + ResultListPage res = commentDseDao.findCommentsByVideoId(queryVideo); + // Then + Assertions.assertEquals(1, res.getResults().size()); + Assertions.assertEquals(UUID.fromString("6fd4df0a-74ca-4891-ae3b-0c16e37880ed"), res.getResults().get(0).getVideoid()); + } + + /** Javadoc in {@link CommentDseDao} */ + private static void createTableComments(DseSession dseSession) { + dseSession.execute(createTable(dseKeyspace, TABLENAME_COMMENTS_BY_USER_).ifNotExists() + .withPartitionKey(COMMENTS_COLUMN_USERID, DataTypes.UUID) + .withClusteringColumn(COMMENTS_COLUMN_COMMENTID, DataTypes.TIMEUUID) + .withColumn(COMMENTS_COLUMN_COMMENT, DataTypes.TEXT) + .withColumn(COMMENTS_COLUMN_VIDEOID, DataTypes.UUID) + .withClusteringOrder(COMMENTS_COLUMN_COMMENTID, ClusteringOrder.DESC) + .withComment("List comments on user page") + .build()); + dseSession.execute(createTable(dseKeyspace, TABLENAME_COMMENTS_BY_VIDEO_).ifNotExists() + .withPartitionKey(COMMENTS_COLUMN_VIDEOID, DataTypes.UUID) + .withClusteringColumn(COMMENTS_COLUMN_COMMENTID, DataTypes.TIMEUUID) + .withColumn(COMMENTS_COLUMN_COMMENT, DataTypes.TEXT) + .withColumn(COMMENTS_COLUMN_USERID, DataTypes.UUID) + .withClusteringOrder(COMMENTS_COLUMN_COMMENTID, ClusteringOrder.DESC) + .withComment("List comments on user page") + .build()); + } +} diff --git a/killrvideo-service-ratings/pom.xml b/killrvideo-service-ratings/pom.xml index d2d34ac7..200f6fa0 100644 --- a/killrvideo-service-ratings/pom.xml +++ b/killrvideo-service-ratings/pom.xml @@ -46,56 +46,5 @@ spring-context - - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - ${build-helper-maven-plugin.version} - - - generate-sources - - add-source - - - - target/generated-sources/protobuf/java - target/generated-sources/protobuf/grpc-java - - - - - - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - ${protobuf-maven-plugin.version} - - ${basedir}/src/main/resources/proto - com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} - grpc-java - io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc.version}:exe:${os.detected.classifier} - - - - - compile - compile-custom - - - - - - - - + diff --git a/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/grpc/RatingsServiceGrpc.java b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/grpc/RatingsServiceGrpc.java index 70874df0..4c1f3ce0 100644 --- a/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/grpc/RatingsServiceGrpc.java +++ b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/grpc/RatingsServiceGrpc.java @@ -16,9 +16,9 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import com.killrvideo.grpc.GrpcMappingUtils; import com.killrvideo.messaging.dao.MessagingDao; import com.killrvideo.service.rating.dao.RatingDseDao; -import com.killrvideo.utils.GrpcMappingUtils; import io.grpc.Status; import io.grpc.stub.StreamObserver; diff --git a/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/grpc/RatingsServiceGrpcMapper.java b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/grpc/RatingsServiceGrpcMapper.java index be36b6c4..cb0be4fa 100644 --- a/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/grpc/RatingsServiceGrpcMapper.java +++ b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/grpc/RatingsServiceGrpcMapper.java @@ -1,6 +1,6 @@ package com.killrvideo.service.rating.grpc; -import static com.killrvideo.utils.GrpcMappingUtils.uuidToUuid; +import static com.killrvideo.grpc.GrpcMappingUtils.uuidToUuid; import java.util.Optional; diff --git a/killrvideo-service-search/pom.xml b/killrvideo-service-search/pom.xml index a59c675a..d801ff1a 100644 --- a/killrvideo-service-search/pom.xml +++ b/killrvideo-service-search/pom.xml @@ -2,85 +2,23 @@ - - - - 4.0.0 killrvideo-service-search + killrvideo-service-search Service Search - - - - + com.datastax killrvideo-parent 6.7.0 - - - - - + + com.datastax killrvideo-commons ${project.version} - - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - ${build-helper-maven-plugin.version} - - - generate-sources - - add-source - - - - target/generated-sources/protobuf/java - target/generated-sources/protobuf/grpc-java - - - - - - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - ${protobuf-maven-plugin.version} - - ${basedir}/src/main/resources/proto - com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} - grpc-java - io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc.version}:exe:${os.detected.classifier} - - - - - compile - compile-custom - - - - - - - - diff --git a/killrvideo-service-search/src/main/java/com/killrvideo/service/search/grpc/SearchServiceGrpcMapper.java b/killrvideo-service-search/src/main/java/com/killrvideo/service/search/grpc/SearchServiceGrpcMapper.java index 183b814d..086bf51f 100644 --- a/killrvideo-service-search/src/main/java/com/killrvideo/service/search/grpc/SearchServiceGrpcMapper.java +++ b/killrvideo-service-search/src/main/java/com/killrvideo/service/search/grpc/SearchServiceGrpcMapper.java @@ -1,7 +1,7 @@ package com.killrvideo.service.search.grpc; import com.killrvideo.dse.dto.Video; -import com.killrvideo.utils.GrpcMappingUtils; +import com.killrvideo.grpc.GrpcMappingUtils; import killrvideo.search.SearchServiceOuterClass.SearchResultsVideoPreview; import killrvideo.search.SearchServiceOuterClass.SearchResultsVideoPreview.Builder; diff --git a/killrvideo-service-statistics/pom.xml b/killrvideo-service-statistics/pom.xml index 3bc6028f..9ea86388 100644 --- a/killrvideo-service-statistics/pom.xml +++ b/killrvideo-service-statistics/pom.xml @@ -2,85 +2,24 @@ - - - - 4.0.0 killrvideo-service-statistics + killrvideo-service-statistics Service Statistics - - - - + com.datastax killrvideo-parent 6.7.0 - - - - + com.datastax killrvideo-commons ${project.version} - - - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - ${build-helper-maven-plugin.version} - - - generate-sources - - add-source - - - - target/generated-sources/protobuf/java - target/generated-sources/protobuf/grpc-java - - - - - - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - ${protobuf-maven-plugin.version} - - ${basedir}/src/main/resources/proto - com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} - grpc-java - io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc.version}:exe:${os.detected.classifier} - - - - - compile - compile-custom - - - - - - - + diff --git a/killrvideo-service-statistics/src/main/java/com/killrvideo/service/statistic/grpc/StatisticsServiceGrpcMapper.java b/killrvideo-service-statistics/src/main/java/com/killrvideo/service/statistic/grpc/StatisticsServiceGrpcMapper.java index d73bb56f..f1b6bd80 100644 --- a/killrvideo-service-statistics/src/main/java/com/killrvideo/service/statistic/grpc/StatisticsServiceGrpcMapper.java +++ b/killrvideo-service-statistics/src/main/java/com/killrvideo/service/statistic/grpc/StatisticsServiceGrpcMapper.java @@ -7,8 +7,8 @@ import org.springframework.stereotype.Component; +import com.killrvideo.grpc.GrpcMappingUtils; import com.killrvideo.service.statistic.dto.VideoPlaybackStats; -import com.killrvideo.utils.GrpcMappingUtils; import killrvideo.common.CommonTypes.Uuid; import killrvideo.statistics.StatisticsServiceOuterClass.GetNumberOfPlaysRequest; diff --git a/killrvideo-service-sugestedvideo/pom.xml b/killrvideo-service-sugestedvideo/pom.xml index bfa6b956..a87aea3b 100644 --- a/killrvideo-service-sugestedvideo/pom.xml +++ b/killrvideo-service-sugestedvideo/pom.xml @@ -2,85 +2,24 @@ - - - - 4.0.0 killrvideo-service-sugestedvideo + killrvideo-service-sugestedvideo Service Sugested Videos - - - com.datastax killrvideo-parent 6.7.0 - - - com.datastax killrvideo-commons ${project.version} - - - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - ${build-helper-maven-plugin.version} - - - generate-sources - - add-source - - - - target/generated-sources/protobuf/java - target/generated-sources/protobuf/grpc-java - - - - - - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - ${protobuf-maven-plugin.version} - - ${basedir}/src/main/resources/proto - com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} - grpc-java - io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc.version}:exe:${os.detected.classifier} - - - - - compile - compile-custom - - - - - - - + diff --git a/killrvideo-service-sugestedvideo/src/main/java/com/killrvideo/service/sugestedvideo/dao/SuggestedVideosMessagingDaoSupport.java b/killrvideo-service-sugestedvideo/src/main/java/com/killrvideo/service/sugestedvideo/dao/SuggestedVideosMessagingDaoSupport.java index 61a0f9a0..25e1a131 100644 --- a/killrvideo-service-sugestedvideo/src/main/java/com/killrvideo/service/sugestedvideo/dao/SuggestedVideosMessagingDaoSupport.java +++ b/killrvideo-service-sugestedvideo/src/main/java/com/killrvideo/service/sugestedvideo/dao/SuggestedVideosMessagingDaoSupport.java @@ -9,7 +9,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import com.killrvideo.utils.GrpcMappingUtils; +import com.killrvideo.grpc.GrpcMappingUtils; import killrvideo.ratings.events.RatingsEvents.UserRatedVideo; import killrvideo.user_management.events.UserManagementEvents.UserCreated; diff --git a/killrvideo-service-sugestedvideo/src/main/java/com/killrvideo/service/sugestedvideo/grpc/SuggestedVideosServiceGrpc.java b/killrvideo-service-sugestedvideo/src/main/java/com/killrvideo/service/sugestedvideo/grpc/SuggestedVideosServiceGrpc.java index 42326bb6..0f8d93ea 100644 --- a/killrvideo-service-sugestedvideo/src/main/java/com/killrvideo/service/sugestedvideo/grpc/SuggestedVideosServiceGrpc.java +++ b/killrvideo-service-sugestedvideo/src/main/java/com/killrvideo/service/sugestedvideo/grpc/SuggestedVideosServiceGrpc.java @@ -1,8 +1,8 @@ package com.killrvideo.service.sugestedvideo.grpc; +import static com.killrvideo.grpc.GrpcMappingUtils.uuidToUuid; import static com.killrvideo.service.sugestedvideo.grpc.SuggestedVideosServiceGrpcMapper.validateGrpcRequest_getRelatedVideo; import static com.killrvideo.service.sugestedvideo.grpc.SuggestedVideosServiceGrpcMapper.validateGrpcRequest_getUserSuggestedVideo; -import static com.killrvideo.utils.GrpcMappingUtils.uuidToUuid; import java.time.Duration; import java.time.Instant; diff --git a/killrvideo-service-sugestedvideo/src/main/java/com/killrvideo/service/sugestedvideo/grpc/SuggestedVideosServiceGrpcMapper.java b/killrvideo-service-sugestedvideo/src/main/java/com/killrvideo/service/sugestedvideo/grpc/SuggestedVideosServiceGrpcMapper.java index 6be093f8..af68f1dd 100644 --- a/killrvideo-service-sugestedvideo/src/main/java/com/killrvideo/service/sugestedvideo/grpc/SuggestedVideosServiceGrpcMapper.java +++ b/killrvideo-service-sugestedvideo/src/main/java/com/killrvideo/service/sugestedvideo/grpc/SuggestedVideosServiceGrpcMapper.java @@ -1,6 +1,6 @@ package com.killrvideo.service.sugestedvideo.grpc; -import static com.killrvideo.utils.GrpcMappingUtils.uuidToUuid; +import static com.killrvideo.grpc.GrpcMappingUtils.uuidToUuid; import static com.killrvideo.utils.ValidationUtils.initErrorString; import static com.killrvideo.utils.ValidationUtils.validate; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -13,7 +13,7 @@ import org.springframework.util.Assert; import com.killrvideo.dse.dto.Video; -import com.killrvideo.utils.GrpcMappingUtils; +import com.killrvideo.grpc.GrpcMappingUtils; import io.grpc.stub.StreamObserver; import killrvideo.suggested_videos.SuggestedVideosService.GetRelatedVideosRequest; diff --git a/killrvideo-service-users/pom.xml b/killrvideo-service-users/pom.xml index abaf79c1..b6d0cc32 100644 --- a/killrvideo-service-users/pom.xml +++ b/killrvideo-service-users/pom.xml @@ -1,28 +1,18 @@ - - - - - 4.0.0 - killrvideo-service-users - + killrvideo-service-users - Service User Management - - - - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + killrvideo-service-users + + killrvideo-service-users + Service User Management + com.datastax killrvideo-parent 6.7.0 - - - - + com.datastax @@ -30,61 +20,10 @@ ${project.version} - org.apache.commons - commons-collections4 - + org.apache.commons + commons-collections4 + - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - ${build-helper-maven-plugin.version} - - - generate-sources - - add-source - - - - target/generated-sources/protobuf/java - target/generated-sources/protobuf/grpc-java - - - - - - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - ${protobuf-maven-plugin.version} - - ${basedir}/src/main/resources/proto - com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} - grpc-java - io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc.version}:exe:${os.detected.classifier} - - - - - compile - compile-custom - - - - - - - - diff --git a/killrvideo-service-users/src/main/java/com/killrvideo/service/user/grpc/UserManagementServiceGrpcMapper.java b/killrvideo-service-users/src/main/java/com/killrvideo/service/user/grpc/UserManagementServiceGrpcMapper.java index bbccc768..2621bb48 100644 --- a/killrvideo-service-users/src/main/java/com/killrvideo/service/user/grpc/UserManagementServiceGrpcMapper.java +++ b/killrvideo-service-users/src/main/java/com/killrvideo/service/user/grpc/UserManagementServiceGrpcMapper.java @@ -3,8 +3,8 @@ import java.util.Date; import java.util.UUID; +import com.killrvideo.grpc.GrpcMappingUtils; import com.killrvideo.service.user.dto.User; -import com.killrvideo.utils.GrpcMappingUtils; import killrvideo.user_management.UserManagementServiceOuterClass.CreateUserRequest; import killrvideo.user_management.UserManagementServiceOuterClass.UserProfile; diff --git a/killrvideo-service-videocatalog/pom.xml b/killrvideo-service-videocatalog/pom.xml index 3e4d0afa..5a2ae584 100644 --- a/killrvideo-service-videocatalog/pom.xml +++ b/killrvideo-service-videocatalog/pom.xml @@ -2,28 +2,18 @@ - - - - 4.0.0 killrvideo-service-videocatalog + killrvideo-service-videocatalog Service Videos Catalog - - - - - + + com.datastax killrvideo-parent 6.7.0 - - - - + com.datastax killrvideo-commons @@ -34,57 +24,6 @@ commons-collections4 - - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - ${build-helper-maven-plugin.version} - - - generate-sources - - add-source - - - - target/generated-sources/protobuf/java - target/generated-sources/protobuf/grpc-java - - - - - - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - ${protobuf-maven-plugin.version} - - ${basedir}/src/main/resources/proto - com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} - grpc-java - io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc.version}:exe:${os.detected.classifier} - - - - - compile - compile-custom - - - - - - - - + diff --git a/killrvideo-service-videocatalog/src/main/java/com/killrvideo/service/video/grpc/VideoCatalogServiceGrpc.java b/killrvideo-service-videocatalog/src/main/java/com/killrvideo/service/video/grpc/VideoCatalogServiceGrpc.java index ea81af2d..7547c5a7 100644 --- a/killrvideo-service-videocatalog/src/main/java/com/killrvideo/service/video/grpc/VideoCatalogServiceGrpc.java +++ b/killrvideo-service-videocatalog/src/main/java/com/killrvideo/service/video/grpc/VideoCatalogServiceGrpc.java @@ -30,10 +30,10 @@ import com.google.protobuf.Timestamp; import com.killrvideo.dse.dto.CustomPagingState; import com.killrvideo.dse.dto.Video; +import com.killrvideo.grpc.GrpcMappingUtils; import com.killrvideo.messaging.dao.MessagingDao; import com.killrvideo.service.video.dao.VideoCatalogDseDao; import com.killrvideo.service.video.dto.LatestVideosPage; -import com.killrvideo.utils.GrpcMappingUtils; import io.grpc.Status; import io.grpc.stub.StreamObserver; diff --git a/killrvideo-service-videocatalog/src/main/java/com/killrvideo/service/video/grpc/VideoCatalogServiceGrpcMapper.java b/killrvideo-service-videocatalog/src/main/java/com/killrvideo/service/video/grpc/VideoCatalogServiceGrpcMapper.java index a393cf01..51b629d6 100644 --- a/killrvideo-service-videocatalog/src/main/java/com/killrvideo/service/video/grpc/VideoCatalogServiceGrpcMapper.java +++ b/killrvideo-service-videocatalog/src/main/java/com/killrvideo/service/video/grpc/VideoCatalogServiceGrpcMapper.java @@ -1,7 +1,7 @@ package com.killrvideo.service.video.grpc; -import static com.killrvideo.utils.GrpcMappingUtils.dateToTimestamp; -import static com.killrvideo.utils.GrpcMappingUtils.uuidToUuid; +import static com.killrvideo.grpc.GrpcMappingUtils.dateToTimestamp; +import static com.killrvideo.grpc.GrpcMappingUtils.uuidToUuid; import java.util.Optional; import java.util.UUID; diff --git a/pom.xml b/pom.xml index 8f804318..0c96b2fc 100644 --- a/pom.xml +++ b/pom.xml @@ -41,65 +41,56 @@ - + + 11 + UTF-8 + - 1.8.1 - + 2.1.1 + - 5.1.4.RELEASE - 2.1.1.RELEASE + 5.1.9.RELEASE + 2.1.7.RELEASE - - 3.0.0 - 6.0.11.Final - 2.0.1.Final - - - 1.10.0 - 3.5.1-1 - 3.5.1 - 1.10.0 - 2.1.5 + + 1.22.1 + 3.9.1 + 1.22.1 - - 2.1.0 - - 3.3.0.2 - 3.11.2 - 4.12 - 1.2.0 - 5.2.0 + 5.5.1 1.2.5 + 3.13.2 + 1.12.0 + + + 3.0.0 + 6.0.11.Final + 2.0.1.Final + + + 2.1.0 + + + 1.2.3 1.9.5 - 1.7.0 - - - 3.7 + + 3.7 1.11 4.1 - 1.2.3 - 1.2 - 0.14.0 + 19.0 - 2.21.0 - 1.7.1 4.1.23.Final - - - 11 - - killrvideo - 1.4.10 - 3.0.0 - 3.5 - 3.7.0 - 2.22.0 - 1.5.0.Final - 0.5.0 - + + 1.6.2 + 3.8.0 + 0.6.1 + 3.0.0 + 2.22.2 + 2.22.2 + 1.4.10 @@ -108,54 +99,49 @@ - + com.datastax.dse dse-java-driver-core - ${dse.version} + ${dse-java-driver.version} com.datastax.dse - dse-java-driver-mapping - ${dse.version} + dse-java-driver-query-builder + ${dse-java-driver.version} com.datastax.dse - dse-java-driver-extras - ${dse.version} + dse-java-driver-mapper-runtime + ${dse-java-driver.version} + + - com.datastax.dse - dse-java-driver-graph - ${dse.version} - - - - - javax.validation - validation-api - ${validation.api.version} - - - org.hibernate - hibernate-validator - ${validator.version} - - - org.hibernate.validator - hibernate-validator - ${validator.version} - - - org.glassfish - javax.el - ${javax.el.api.version} - - - javax.el - javax.el-api - ${javax.el.api.version} - + javax.validation + validation-api + ${validation.api.version} + + + org.hibernate + hibernate-validator + ${validator.version} + + + org.hibernate.validator + hibernate-validator + ${validator.version} + + + org.glassfish + javax.el + ${javax.el.api.version} + + + javax.el + javax.el-api + ${javax.el.api.version} + @@ -166,8 +152,9 @@ com.google.protobuf protobuf-java - ${protobuf.java.version} + ${protobuf.version} + org.apache.commons commons-lang3 @@ -183,7 +170,7 @@ commons-collections4 ${commons-collections.version} - + org.apache.kafka @@ -257,82 +244,38 @@ ${logback.version} - - junit - junit - ${junit4.version} + org.junit.jupiter + junit-jupiter + ${junit-jupiter.version} - org.springframework - spring-test - ${spring.version} - - - - - org.cassandraunit - cassandra-unit - ${cassandra-unit.version} + org.testcontainers + testcontainers + ${testcontainers.version} + + + org.assertj + assertj-core + ${assertj.version} - org.apache.cassandra - cassandra-all - ${cassandra-all.version} + info.cukes + cucumber-java + ${cucumber.version} - org.cassandraunit - cassandra-unit-spring - ${cassandra-unit.version} + info.cukes + cucumber-spring + ${cucumber.version} + test - - org.junit.platform - junit-platform-launcher - ${junit-platform.version} + info.cukes + cucumber-junit + ${cucumber.version} - - org.junit.platform - junit-platform-runner - ${junit-platform.version} - - - org.junit.platform - junit-platform-console-standalone - ${junit-platform.version} - - - org.junit.jupiter - junit-jupiter-engine - ${junit-jupiter.version} - - - org.junit.jupiter - junit-jupiter-params - ${junit-jupiter.version} - - - - info.cukes - cucumber-java - ${cucumber.version} - - - info.cukes - cucumber-spring - ${cucumber.version} - test - - - info.cukes - cucumber-junit - ${cucumber.version} - - - org.assertj - assertj-core - ${assertj-core.version} - + @@ -346,19 +289,9 @@ ${guava.version} - org.ff4j - ff4j-core - ${ff4j.version} - - - org.ff4j - ff4j-web - ${ff4j.version} - - - io.netty - netty-all - ${netty.version} + io.netty + netty-all + ${netty.version} @@ -383,62 +316,71 @@ maven-compiler-plugin ${maven-compiler-plugin.version} - true - ${java.version} + ${java.version} ${java.version} ${java.version} + + + com.datastax.dse + dse-java-driver-mapper-processor + ${dse-java-driver.version} + + - - - org.ow2.asm - asm - 6.2 - - - - org.codehaus.mojo - cassandra-maven-plugin - ${cassandra-maven-plugin.version} - - - - start - flush - cleanup - - compile - - + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - - - org.junit.platform - junit-platform-surefire-provider - ${junit-platform.version} - - - org.junit.jupiter - junit-jupiter-engine - ${junit-jupiter.version} - - - org.ow2.asm - asm - 6.2 - - - - + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + + + org.codehaus.mojo + build-helper-maven-plugin + ${maven-build-helper-plugin.version} + + + generate-sources + + add-source + + + + target/generated-sources/protobuf/java + + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + ${maven-protobuf-plugin.version} + + ${basedir}/src/main/resources/proto + com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc.version}:exe:${os.detected.classifier} + + + + + compile + compile-custom + + + + + From eaa0051d312037d9ba157ec1c333a3daa4ad3233 Mon Sep 17 00:00:00 2001 From: clunven Date: Wed, 21 Aug 2019 17:23:52 +0200 Subject: [PATCH 02/31] Made progress service by service --- .../com/killrvideo/dse/dao/DseSchema.java | 62 ++-- .../killrvideo/dse/dto/ResultListPage.java | 8 +- .../com/killrvideo/dse/utils/DseUtils.java | 11 + .../java/com/killrvideo/utils/IOUtils.java | 26 ++ killrvideo-service-comments/pom.xml | 5 - .../dao/CommentDseDaoQueryProvider.java | 115 +++----- .../comment/dao/CommentDseDaoUtils.java | 98 +++++++ .../comment/test/CommentDseDaoTest.java | 34 +-- killrvideo-service-ratings/.factorypath | 157 +++-------- killrvideo-service-ratings/pom.xml | 29 +- .../service/rating/dao/RatingDseDao.java | 136 ++------- .../rating/dao/RatingDseDaoMapper.java | 26 ++ .../rating/dao/RatingDseDaoQueryProvider.java | 77 +++++ .../{VideoRating.java => VideoRatings.java} | 28 +- ...ingByUser.java => VideoRatingsByUser.java} | 26 +- .../rating/grpc/RatingsServiceGrpcMapper.java | 8 +- .../service/rating/test/RatingDseDaoTest.java | 128 +++++++++ killrvideo-service-search/.factorypath | 156 +++-------- killrvideo-service-search/pom.xml | 14 +- .../service/search/dao/SearchDseDao.java | 264 ++---------------- .../search/dao/SearchDseDaoMapper.java | 28 ++ .../search/dao/SearchDseDaoQueryProvider.java | 255 +++++++++++++++++ .../search/grpc/SearchServiceGrpc.java | 11 +- killrvideo-service-statistics/.factorypath | 156 +++-------- .../statistic/dao/StatisticsDseDao.java | 107 +------ .../statistic/dao/StatisticsDseDaoMapper.java | 26 ++ .../statistic/dto/VideoPlaybackStats.java | 24 +- .../statistic/grpc/StatisticsServiceGrpc.java | 31 +- .../service/user/dao/UserDseDao.java | 169 ++--------- .../service/user/dao/UserDseDaoMapper.java | 26 ++ .../service/user/dao/UserDseDaoOld.java | 173 ++++++++++++ .../user/dao/UserDseDaoQueryProvider.java | 136 +++++++++ .../com/killrvideo/service/user/dto/User.java | 36 ++- .../service/user/dto/UserCredentials.java | 26 +- pom.xml | 2 +- 35 files changed, 1393 insertions(+), 1221 deletions(-) create mode 100644 killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoUtils.java create mode 100644 killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dao/RatingDseDaoMapper.java create mode 100644 killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dao/RatingDseDaoQueryProvider.java rename killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dto/{VideoRating.java => VideoRatings.java} (66%) rename killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dto/{VideoRatingByUser.java => VideoRatingsByUser.java} (73%) create mode 100644 killrvideo-service-ratings/src/test/java/com/killrvideo/service/rating/test/RatingDseDaoTest.java create mode 100644 killrvideo-service-search/src/main/java/com/killrvideo/service/search/dao/SearchDseDaoMapper.java create mode 100644 killrvideo-service-search/src/main/java/com/killrvideo/service/search/dao/SearchDseDaoQueryProvider.java create mode 100644 killrvideo-service-statistics/src/main/java/com/killrvideo/service/statistic/dao/StatisticsDseDaoMapper.java create mode 100644 killrvideo-service-users/src/main/java/com/killrvideo/service/user/dao/UserDseDaoMapper.java create mode 100644 killrvideo-service-users/src/main/java/com/killrvideo/service/user/dao/UserDseDaoOld.java create mode 100644 killrvideo-service-users/src/main/java/com/killrvideo/service/user/dao/UserDseDaoQueryProvider.java diff --git a/killrvideo-commons/src/main/java/com/killrvideo/dse/dao/DseSchema.java b/killrvideo-commons/src/main/java/com/killrvideo/dse/dao/DseSchema.java index e93a9232..a404ef2d 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/dse/dao/DseSchema.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/dse/dao/DseSchema.java @@ -14,23 +14,25 @@ public interface DseSchema { SimpleDateFormat FORMATTER_DAY = new SimpleDateFormat("yyyyMMdd"); // user_credentials - String USERCREDENTIAL_COLUMN_USERID ="userid" ; + String TABLENAME_USER_CREDENTIALS = "user_credentials"; + String USERCREDENTIAL_COLUMN_USERID = "userid" ; String USERCREDENTIAL_COLUMN_PASSWORD = "\"password\""; String USERCREDENTIAL_COLUMN_EMAIL = "email"; - CqlIdentifier TABLENAME_USER_CREDENTIALS = CqlIdentifier.fromCql("user_credentials"); + CqlIdentifier TABLENAME_USER_CREDENTIALS_ = CqlIdentifier.fromCql(TABLENAME_USER_CREDENTIALS); CqlIdentifier USERCREDENTIAL_COLUMN_USERID_ = CqlIdentifier.fromCql(USERCREDENTIAL_COLUMN_USERID); CqlIdentifier USERCREDENTIAL_COLUMN_PASSWORD_ = CqlIdentifier.fromCql(USERCREDENTIAL_COLUMN_PASSWORD); CqlIdentifier USERCREDENTIAL_COLUMN_EMAIL_ = CqlIdentifier.fromCql(USERCREDENTIAL_COLUMN_EMAIL); // users + String TABLENAME_USERS = "users"; String USER_COLUMN_USERID = "userid"; String USER_COLUMN_FIRSTNAME = "firstname"; String USER_COLUMN_LASTNAME = "lastname"; String USER_COLUMN_EMAIL = "email"; String USER_COLUMN_CREATE = "created_date"; - CqlIdentifier TABLENAME_USERS = CqlIdentifier.fromCql("users"); + CqlIdentifier TABLENAME_USERS_ = CqlIdentifier.fromCql(TABLENAME_USERS); CqlIdentifier USER_COLUMN_USERID_ = CqlIdentifier.fromCql(USER_COLUMN_USERID); CqlIdentifier USER_COLUMN_FIRSTNAME_ = CqlIdentifier.fromCql(USER_COLUMN_FIRSTNAME); CqlIdentifier USER_COLUMN_LASTNAME_ = CqlIdentifier.fromCql(USER_COLUMN_LASTNAME); @@ -71,18 +73,46 @@ public interface DseSchema { CqlIdentifier LATESTVIDEOS_COLUMN_YYYYMMDD_ = CqlIdentifier.fromCql(LATESTVIDEOS_COLUMN_YYYYMMDD); // comments_by_video + comments_by_user - String TABLENAME_COMMENTS_BY_USER = "comments_by_user"; - String TABLENAME_COMMENTS_BY_VIDEO = "comments_by_video"; - String COMMENTS_COLUMN_VIDEOID = "videoid"; - String COMMENTS_COLUMN_USERID = "userid"; - String COMMENTS_COLUMN_COMMENTID = "commentid"; - String COMMENTS_COLUMN_COMMENT = "comment"; - - CqlIdentifier TABLENAME_COMMENTS_BY_USER_ = CqlIdentifier.fromCql(TABLENAME_COMMENTS_BY_USER); - CqlIdentifier TABLENAME_COMMENTS_BY_VIDEO_ = CqlIdentifier.fromCql(TABLENAME_COMMENTS_BY_VIDEO); - CqlIdentifier COMMENTS_COLUMN_VIDEOID_ = CqlIdentifier.fromCql(COMMENTS_COLUMN_VIDEOID); - CqlIdentifier COMMENTS_COLUMN_USERID_ = CqlIdentifier.fromCql(COMMENTS_COLUMN_USERID); - CqlIdentifier COMMENTS_COLUMN_COMMENTID_ = CqlIdentifier.fromCql(COMMENTS_COLUMN_COMMENTID); - CqlIdentifier COMMENTS_COLUMN_COMMENT_ = CqlIdentifier.fromCql(COMMENTS_COLUMN_COMMENT); + String TABLENAME_COMMENTS_BY_USER = "comments_by_user"; + String TABLENAME_COMMENTS_BY_VIDEO = "comments_by_video"; + String COMMENTS_COLUMN_VIDEOID = "videoid"; + String COMMENTS_COLUMN_USERID = "userid"; + String COMMENTS_COLUMN_COMMENTID = "commentid"; + String COMMENTS_COLUMN_COMMENT = "comment"; + + CqlIdentifier TABLENAME_COMMENTS_BY_USER_ = CqlIdentifier.fromCql(TABLENAME_COMMENTS_BY_USER); + CqlIdentifier TABLENAME_COMMENTS_BY_VIDEO_ = CqlIdentifier.fromCql(TABLENAME_COMMENTS_BY_VIDEO); + CqlIdentifier COMMENTS_COLUMN_VIDEOID_ = CqlIdentifier.fromCql(COMMENTS_COLUMN_VIDEOID); + CqlIdentifier COMMENTS_COLUMN_USERID_ = CqlIdentifier.fromCql(COMMENTS_COLUMN_USERID); + CqlIdentifier COMMENTS_COLUMN_COMMENTID_ = CqlIdentifier.fromCql(COMMENTS_COLUMN_COMMENTID); + CqlIdentifier COMMENTS_COLUMN_COMMENT_ = CqlIdentifier.fromCql(COMMENTS_COLUMN_COMMENT); + + // video_ratings + video_ratings_by_user + String TABLENAME_VIDEO_RATINGS = "video_ratings"; + String TABLENAME_VIDEO_RATINGS_BYUSER = "video_ratings_by_user"; + String RATING_COLUMN_RATING = "rating"; + String RATING_COLUMN_RATING_COUNTER = "rating_counter"; + String RATING_COLUMN_RATING_TOTAL = "rating_total"; + String RATING_COLUMN_VIDEOID = "videoid"; + String RATING_COLUMN_USERID = "userid"; + + CqlIdentifier TABLENAME_VIDEO_RATINGS_ = CqlIdentifier.fromCql(TABLENAME_VIDEO_RATINGS); + CqlIdentifier TABLENAME_VIDEO_RATINGS_BYUSER_ = CqlIdentifier.fromCql(TABLENAME_VIDEO_RATINGS_BYUSER); + CqlIdentifier RATING_COLUMN_RATING_COUNTER_ = CqlIdentifier.fromCql(RATING_COLUMN_RATING_COUNTER); + CqlIdentifier RATING_COLUMN_RATING_TOTAL_ = CqlIdentifier.fromCql(RATING_COLUMN_RATING_TOTAL); + CqlIdentifier RATING_COLUMN_VIDEOID_ = CqlIdentifier.fromCql(RATING_COLUMN_VIDEOID); + CqlIdentifier RATING_COLUMN_USERID_ = CqlIdentifier.fromCql(RATING_COLUMN_USERID); + CqlIdentifier RATING_COLUMN_RATING_ = CqlIdentifier.fromCql(RATING_COLUMN_RATING); + + // video_playback_stats + String TABLENAME_PLAYBACK_STATS = "video_playback_stats"; + String COLUMN_PLAYBACK_VIDEOID = "videoid"; + String COLUMN_PLAYBACK_VIEWS = "views"; + + CqlIdentifier TABLENAME_PLAYBACK_STATS_ = CqlIdentifier.fromCql(TABLENAME_PLAYBACK_STATS); + CqlIdentifier COLUMN_PLAYBACK_VIDEOID_ = CqlIdentifier.fromCql(COLUMN_PLAYBACK_VIDEOID); + CqlIdentifier COLUMN_PLAYBACK_VIEWS_ = CqlIdentifier.fromCql(COLUMN_PLAYBACK_VIEWS); + + CqlIdentifier SOLR_QUERY = CqlIdentifier.fromCql("solr_query"); } diff --git a/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/ResultListPage.java b/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/ResultListPage.java index a07516af..f1947bd4 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/ResultListPage.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/dse/dto/ResultListPage.java @@ -1,5 +1,6 @@ package com.killrvideo.dse.dto; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -8,6 +9,7 @@ import com.datastax.oss.driver.api.core.MappedAsyncPagingIterable; import com.datastax.oss.driver.api.core.PagingIterable; +import com.killrvideo.utils.IOUtils; /** * Ease usage of the paginState. @@ -42,7 +44,8 @@ public ResultListPage(PagingIterable rs) { IntStream.range(0, rs.getAvailableWithoutFetching()) .forEach(item -> listOfResults.add(iterResults.next())); if (null != rs.getExecutionInfo().getPagingState()) { - nextPage = Optional.ofNullable(rs.getExecutionInfo().getPagingState().toString()); + ByteBuffer pagingState = rs.getExecutionInfo().getPagingState(); + nextPage = Optional.ofNullable(IOUtils.fromByteBuffer2String(pagingState)); } } } @@ -50,7 +53,8 @@ public ResultListPage(PagingIterable rs) { public ResultListPage(MappedAsyncPagingIterable rs) { if (null != rs) { rs.currentPage().forEach(listOfResults::add); - nextPage = Optional.ofNullable(rs.getExecutionInfo().getPagingState().toString()); + ByteBuffer pagingState = rs.getExecutionInfo().getPagingState(); + nextPage = Optional.ofNullable(IOUtils.fromByteBuffer2String(pagingState)); } } diff --git a/killrvideo-commons/src/main/java/com/killrvideo/dse/utils/DseUtils.java b/killrvideo-commons/src/main/java/com/killrvideo/dse/utils/DseUtils.java index c94fba1b..e8bbbda4 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/dse/utils/DseUtils.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/dse/utils/DseUtils.java @@ -12,7 +12,12 @@ import com.datastax.dse.driver.api.core.DseSession; import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.cql.BoundStatement; +import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder; +import com.datastax.oss.driver.api.core.cql.PreparedStatement; import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; +import com.datastax.oss.driver.api.mapper.entity.EntityHelper; +import com.datastax.oss.driver.api.mapper.entity.saving.NullSavingStrategy; import com.datastax.oss.driver.api.querybuilder.QueryBuilder; import com.datastax.oss.driver.api.querybuilder.SchemaBuilder; import com.google.common.util.concurrent.FutureCallback; @@ -146,4 +151,10 @@ public static CompletableFuture buildCompletableFuture(final ListenableFu }); return completable; } + + public static BoundStatement bind(PreparedStatement preparedStatement, T entity, EntityHelper entityHelper) { + BoundStatementBuilder boundStatement = preparedStatement.boundStatementBuilder(); + entityHelper.set(entity, boundStatement, NullSavingStrategy.DO_NOT_SET); + return boundStatement.build(); + } } diff --git a/killrvideo-commons/src/main/java/com/killrvideo/utils/IOUtils.java b/killrvideo-commons/src/main/java/com/killrvideo/utils/IOUtils.java index e0cc6c48..1afc6237 100644 --- a/killrvideo-commons/src/main/java/com/killrvideo/utils/IOUtils.java +++ b/killrvideo-commons/src/main/java/com/killrvideo/utils/IOUtils.java @@ -2,6 +2,9 @@ import java.io.IOException; import java.net.Socket; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,5 +54,28 @@ public static boolean isServiceReachableAndListening(String service, String addr } } } + + public static ByteBuffer fromString2ByteBuffer(String str) { + return fromString2ByteBuffer(str, StandardCharsets.UTF_8); + } + + public static String fromByteBuffer2String(ByteBuffer bb) { + return fromByteBuffer2String(bb, StandardCharsets.UTF_8); + } + + public static ByteBuffer fromString2ByteBuffer(String str, Charset charset) { + return ByteBuffer.wrap(str.getBytes(charset)); + } + + public static String fromByteBuffer2String(ByteBuffer bb, Charset charset) { + byte[] bytes; + if (bb.hasArray()) { + bytes = bb.array(); + } else { + bytes = new byte[bb.remaining()]; + bb.get(bytes); + } + return new String(bytes, charset); + } } diff --git a/killrvideo-service-comments/pom.xml b/killrvideo-service-comments/pom.xml index 0cb1b42d..49fa1234 100644 --- a/killrvideo-service-comments/pom.xml +++ b/killrvideo-service-comments/pom.xml @@ -28,11 +28,6 @@ test - org.assertj - assertj-core - test - - org.testcontainers testcontainers test diff --git a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoQueryProvider.java b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoQueryProvider.java index ec25055c..01c60fef 100644 --- a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoQueryProvider.java +++ b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoQueryProvider.java @@ -1,8 +1,10 @@ package com.killrvideo.service.comment.dao; -import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.bindMarker; -import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.selectFrom; -import static com.datastax.oss.driver.api.querybuilder.relation.Relation.column; +import static com.killrvideo.service.comment.dao.CommentDseDaoUtils.bind; +import static com.killrvideo.service.comment.dao.CommentDseDaoUtils.stmtFindCommentsForUser; +import static com.killrvideo.service.comment.dao.CommentDseDaoUtils.stmtFindCommentsForUserWithCommentId; +import static com.killrvideo.service.comment.dao.CommentDseDaoUtils.stmtFindCommentsForVideo; +import static com.killrvideo.service.comment.dao.CommentDseDaoUtils.stmtFindCommentsForVideoWithCommentId; import java.nio.ByteBuffer; import java.util.concurrent.CompletionStage; @@ -10,14 +12,11 @@ import com.datastax.oss.driver.api.core.CqlSession; import com.datastax.oss.driver.api.core.cql.BatchStatement; import com.datastax.oss.driver.api.core.cql.BoundStatement; -import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder; import com.datastax.oss.driver.api.core.cql.DefaultBatchType; import com.datastax.oss.driver.api.core.cql.PreparedStatement; import com.datastax.oss.driver.api.mapper.MapperContext; import com.datastax.oss.driver.api.mapper.annotations.QueryProvider; import com.datastax.oss.driver.api.mapper.entity.EntityHelper; -import com.datastax.oss.driver.api.mapper.entity.saving.NullSavingStrategy; -import com.datastax.oss.driver.api.querybuilder.select.Selector; import com.killrvideo.dse.dao.DseSchema; import com.killrvideo.dse.dto.ResultListPage; import com.killrvideo.service.comment.dao.dto.Comment; @@ -26,7 +25,7 @@ import com.killrvideo.service.comment.grpc.dto.QueryCommentByUser; import com.killrvideo.service.comment.grpc.dto.QueryCommentByVideo; -public class CommentDseDaoQueryProvider implements DseSchema { +public class CommentDseDaoQueryProvider implements CommentDseDao, DseSchema { private final CqlSession dseSession; @@ -59,48 +58,18 @@ public CommentDseDaoQueryProvider(MapperContext context, EntityHelper helperUser, EntityHelper helperVideo) { - this.dseSession = context.getSession(); - this.entityHelperCommentsByUser = helperUser; - this.entityHelperCommentsByVideo = helperVideo; + this.dseSession = context.getSession(); + this.entityHelperCommentsByUser = helperUser; + this.entityHelperCommentsByVideo = helperVideo; - this.psInsertCommentUser = dseSession.prepare(entityHelperCommentsByUser.insert().asCql()); - this.psDeleteCommentUser = dseSession.prepare(entityHelperCommentsByUser.deleteByPrimaryKey().asCql()); - this.psInsertCommentVideo = dseSession.prepare(entityHelperCommentsByVideo.insert().asCql()); - this.psDeleteCommentVideo = dseSession.prepare(entityHelperCommentsByVideo.deleteByPrimaryKey().asCql()); - - psFindCommentsByUser = dseSession.prepare( - selectFrom(TABLENAME_COMMENTS_BY_USER_) - .column(COMMENTS_COLUMN_USERID).column(COMMENTS_COLUMN_COMMENTID) - .column(COMMENTS_COLUMN_VIDEOID).column(COMMENTS_COLUMN_COMMENT) - .function("toTimestamp", Selector.column(COMMENTS_COLUMN_COMMENTID)).as("comment_timestamp") - .where(column(COMMENTS_COLUMN_USERID).isEqualTo(bindMarker(COMMENTS_COLUMN_USERID))) - .build()); - psFindCommentsByUserPageable = dseSession.prepare( - selectFrom(TABLENAME_COMMENTS_BY_USER) - .column(COMMENTS_COLUMN_USERID).column(COMMENTS_COLUMN_COMMENTID) - .column(COMMENTS_COLUMN_VIDEOID).column(COMMENTS_COLUMN_COMMENT) - .function("toTimestamp", Selector.column(COMMENTS_COLUMN_COMMENTID)).as("comment_timestamp") - .where(column(COMMENTS_COLUMN_USERID).isEqualTo(bindMarker(COMMENTS_COLUMN_USERID)), - column(COMMENTS_COLUMN_COMMENTID).isLessThanOrEqualTo(bindMarker(COMMENTS_COLUMN_COMMENTID)) - ).build()); - - psFindCommentsByVideo = dseSession.prepare( - selectFrom(TABLENAME_COMMENTS_BY_VIDEO) - .column(COMMENTS_COLUMN_USERID).column(COMMENTS_COLUMN_USERID) - .column(COMMENTS_COLUMN_VIDEOID).column(COMMENTS_COLUMN_COMMENT) - .column(COMMENTS_COLUMN_COMMENTID) - .function("toTimestamp", Selector.column(COMMENTS_COLUMN_COMMENTID)).as("comment_timestamp") - .where(column(COMMENTS_COLUMN_VIDEOID).isEqualTo(bindMarker(COMMENTS_COLUMN_VIDEOID))) - .build()); - psFindCommentsByVideoPageable = dseSession.prepare( - selectFrom(TABLENAME_COMMENTS_BY_VIDEO) - .column(COMMENTS_COLUMN_USERID).column(COMMENTS_COLUMN_USERID) - .column(COMMENTS_COLUMN_VIDEOID).column(COMMENTS_COLUMN_COMMENT) - .column(COMMENTS_COLUMN_COMMENTID) - .function("toTimestamp", Selector.column(COMMENTS_COLUMN_COMMENTID)).as("comment_timestamp") - .where(column(COMMENTS_COLUMN_VIDEOID).isEqualTo(bindMarker(COMMENTS_COLUMN_VIDEOID)), - column(COMMENTS_COLUMN_COMMENTID).isLessThanOrEqualTo(bindMarker(COMMENTS_COLUMN_COMMENTID))) - .build()); + this.psFindCommentsByUser = dseSession.prepare(stmtFindCommentsForUser()); + this.psFindCommentsByUserPageable = dseSession.prepare(stmtFindCommentsForUserWithCommentId()); + this.psFindCommentsByVideo = dseSession.prepare(stmtFindCommentsForVideo()); + this.psFindCommentsByVideoPageable = dseSession.prepare(stmtFindCommentsForVideoWithCommentId()); + this.psInsertCommentUser = dseSession.prepare(helperUser.insert().asCql()); + this.psDeleteCommentUser = dseSession.prepare(helperUser.deleteByPrimaryKey().asCql()); + this.psInsertCommentVideo = dseSession.prepare(helperVideo.insert().asCql()); + this.psDeleteCommentVideo = dseSession.prepare(helperVideo.deleteByPrimaryKey().asCql()); } /** Javadoc in {@link CommentDseDao} */ @@ -115,7 +84,7 @@ public CompletionStage upsertAsync(Comment comment) { } /** Javadoc in {@link CommentDseDao} */ - void delete(Comment comment) { + public void delete(Comment comment) { CommentByUserEntity c1 = new CommentByUserEntity(); c1.setUserid(comment.getUserid()); c1.setCommentid(comment.getCommentid()); @@ -131,29 +100,44 @@ void delete(Comment comment) { /** Javadoc in {@link CommentDseDao} */ public ResultListPage findCommentsByVideoId(final QueryCommentByVideo query) { - return new ResultListPage<>(dseSession.execute(buildStatementVideoComments(query)) + return new ResultListPage<>(dseSession.execute(_buildStatementVideoComments(query)) .map(entityHelperCommentsByVideo::get)); } /** Javadoc in {@link CommentDseDao} */ public CompletionStage> findCommentsByVideoIdAsync(final QueryCommentByVideo query) { - return dseSession.executeAsync(buildStatementVideoComments(query)) + return dseSession.executeAsync(_buildStatementVideoComments(query)) .thenApply(ars -> ars.map(entityHelperCommentsByVideo::get)) .thenApply(ResultListPage::new); } /** Javadoc in {@link CommentDseDao} */ public ResultListPage findCommentsByUserId(final QueryCommentByUser query) { - return new ResultListPage<>(dseSession.execute(buildStatementUserComments(query)) + return new ResultListPage<>(dseSession.execute(_buildStatementUserComments(query)) .map(entityHelperCommentsByUser::get)); } /** Javadoc in {@link CommentDseDao} */ public CompletionStage> findCommentsByUserIdAsync(final QueryCommentByUser query) { - return dseSession.executeAsync(buildStatementUserComments(query)) + return dseSession.executeAsync(_buildStatementUserComments(query)) .thenApply(ars -> ars.map(entityHelperCommentsByUser::get)) .thenApply(ResultListPage::new); } + + /** + * Cassandra and DSE Session are stateless. For each request a coordinator is chosen + * and execute query against the cluster. The Driver is stateful, it has to maintain some + * network connections pool, here we properly cleanup things. + * + * @throws Exception + * error on cleanup. + */ + @Override + protected void finalize() throws Throwable { + if (dseSession != null && !dseSession.isClosed()) { + dseSession.close(); + } + } /** * Init statement based on comment tag. @@ -163,7 +147,7 @@ public CompletionStage> findCommentsByUserId * @return * statement */ - private BoundStatement buildStatementVideoComments(final QueryCommentByVideo query) { + private BoundStatement _buildStatementVideoComments(final QueryCommentByVideo query) { BoundStatement statement = null; if (query.getCommentId().isPresent()) { statement = psFindCommentsByVideoPageable.bind() @@ -195,7 +179,7 @@ private BoundStatement buildStatementVideoComments(final QueryCommentByVideo que * @return * statement to retrieve comments */ - private BoundStatement buildStatementUserComments(final QueryCommentByUser query) { + private BoundStatement _buildStatementUserComments(final QueryCommentByUser query) { BoundStatement statement = null; if (query.getCommentId().isPresent()) { statement = psFindCommentsByUserPageable.bind() @@ -211,21 +195,6 @@ private BoundStatement buildStatementUserComments(final QueryCommentByUser query statement.setPageSize(query.getPageSize()); return statement; } - - /** - * Cassandra and DSE Session are stateless. For each request a coordinator is chosen - * and execute query against the cluster. The Driver is stateful, it has to maintain some - * network connections pool, here we properly cleanup things. - * - * @throws Exception - * error on cleanup. - */ - @Override - protected void finalize() throws Throwable { - if (dseSession != null && !dseSession.isClosed()) { - dseSession.close(); - } - } private BatchStatement _buildStatementInsertComment(Comment comment) { return BatchStatement.builder(DefaultBatchType.LOGGED) @@ -234,10 +203,4 @@ private BatchStatement _buildStatementInsertComment(Comment comment) { .build(); } - protected static BoundStatement bind(PreparedStatement preparedStatement, T entity, EntityHelper entityHelper) { - BoundStatementBuilder boundStatement = preparedStatement.boundStatementBuilder(); - entityHelper.set(entity, boundStatement, NullSavingStrategy.DO_NOT_SET); - return boundStatement.build(); - } - } diff --git a/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoUtils.java b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoUtils.java new file mode 100644 index 00000000..eacc516f --- /dev/null +++ b/killrvideo-service-comments/src/main/java/com/killrvideo/service/comment/dao/CommentDseDaoUtils.java @@ -0,0 +1,98 @@ +package com.killrvideo.service.comment.dao; + +import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.bindMarker; +import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.selectFrom; +import static com.datastax.oss.driver.api.querybuilder.SchemaBuilder.createTable; +import static com.datastax.oss.driver.api.querybuilder.relation.Relation.column; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.cql.BoundStatement; +import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder; +import com.datastax.oss.driver.api.core.cql.PreparedStatement; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; +import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder; +import com.datastax.oss.driver.api.core.type.DataTypes; +import com.datastax.oss.driver.api.mapper.entity.EntityHelper; +import com.datastax.oss.driver.api.mapper.entity.saving.NullSavingStrategy; +import com.datastax.oss.driver.api.querybuilder.select.Selector; +import com.killrvideo.dse.dao.DseSchema; + +/** + * Wrap request building in this utility class + * + * @author Cedrick LUNVEN (@clunven) + */ +public class CommentDseDaoUtils implements DseSchema { + + /** Hide default constructor. */ + private CommentDseDaoUtils() {} + + public static SimpleStatement stmtFindCommentsForUser() { + return selectFrom(TABLENAME_COMMENTS_BY_USER_) + .column(COMMENTS_COLUMN_USERID).column(COMMENTS_COLUMN_COMMENTID) + .column(COMMENTS_COLUMN_VIDEOID).column(COMMENTS_COLUMN_COMMENT) + .function("toTimestamp", Selector.column(COMMENTS_COLUMN_COMMENTID)).as("comment_timestamp") + .where(column(COMMENTS_COLUMN_USERID).isEqualTo(bindMarker(COMMENTS_COLUMN_USERID))) + .build(); + } + + public static SimpleStatement stmtFindCommentsForUserWithCommentId() { + return selectFrom(TABLENAME_COMMENTS_BY_VIDEO) + .column(COMMENTS_COLUMN_USERID).column(COMMENTS_COLUMN_USERID) + .column(COMMENTS_COLUMN_VIDEOID).column(COMMENTS_COLUMN_COMMENT) + .column(COMMENTS_COLUMN_COMMENTID) + .function("toTimestamp", Selector.column(COMMENTS_COLUMN_COMMENTID)).as("comment_timestamp") + .where(column(COMMENTS_COLUMN_VIDEOID).isEqualTo(bindMarker(COMMENTS_COLUMN_VIDEOID))) + .build(); + } + + public static SimpleStatement stmtFindCommentsForVideo() { + return selectFrom(TABLENAME_COMMENTS_BY_VIDEO) + .column(COMMENTS_COLUMN_USERID).column(COMMENTS_COLUMN_USERID) + .column(COMMENTS_COLUMN_VIDEOID).column(COMMENTS_COLUMN_COMMENT) + .column(COMMENTS_COLUMN_COMMENTID) + .function("toTimestamp", Selector.column(COMMENTS_COLUMN_COMMENTID)).as("comment_timestamp") + .where(column(COMMENTS_COLUMN_VIDEOID).isEqualTo(bindMarker(COMMENTS_COLUMN_VIDEOID))) + .build(); + } + + public static SimpleStatement stmtFindCommentsForVideoWithCommentId() { + return selectFrom(TABLENAME_COMMENTS_BY_VIDEO) + .column(COMMENTS_COLUMN_USERID).column(COMMENTS_COLUMN_USERID) + .column(COMMENTS_COLUMN_VIDEOID).column(COMMENTS_COLUMN_COMMENT) + .column(COMMENTS_COLUMN_COMMENTID) + .function("toTimestamp", Selector.column(COMMENTS_COLUMN_COMMENTID)).as("comment_timestamp") + .where(column(COMMENTS_COLUMN_VIDEOID).isEqualTo(bindMarker(COMMENTS_COLUMN_VIDEOID)), + column(COMMENTS_COLUMN_COMMENTID).isLessThanOrEqualTo(bindMarker(COMMENTS_COLUMN_COMMENTID))) + .build(); + } + + public static SimpleStatement stmtCreateTableCommentByUser(CqlIdentifier kspace) { + return createTable(kspace, TABLENAME_COMMENTS_BY_USER_).ifNotExists() + .withPartitionKey(COMMENTS_COLUMN_USERID, DataTypes.UUID) + .withClusteringColumn(COMMENTS_COLUMN_COMMENTID, DataTypes.TIMEUUID) + .withColumn(COMMENTS_COLUMN_COMMENT, DataTypes.TEXT) + .withColumn(COMMENTS_COLUMN_VIDEOID, DataTypes.UUID) + .withClusteringOrder(COMMENTS_COLUMN_COMMENTID, ClusteringOrder.DESC) + .withComment("List comments on user page") + .build(); + } + + public static SimpleStatement stmtCreateTableCommentByVideo(CqlIdentifier kspace) { + return createTable(kspace, TABLENAME_COMMENTS_BY_VIDEO_).ifNotExists() + .withPartitionKey(COMMENTS_COLUMN_VIDEOID, DataTypes.UUID) + .withClusteringColumn(COMMENTS_COLUMN_COMMENTID, DataTypes.TIMEUUID) + .withColumn(COMMENTS_COLUMN_COMMENT, DataTypes.TEXT) + .withColumn(COMMENTS_COLUMN_USERID, DataTypes.UUID) + .withClusteringOrder(COMMENTS_COLUMN_COMMENTID, ClusteringOrder.DESC) + .withComment("List comments on user page") + .build(); + } + + public static BoundStatement bind(PreparedStatement preparedStatement, T entity, EntityHelper entityHelper) { + BoundStatementBuilder boundStatement = preparedStatement.boundStatementBuilder(); + entityHelper.set(entity, boundStatement, NullSavingStrategy.DO_NOT_SET); + return boundStatement.build(); + } + +} diff --git a/killrvideo-service-comments/src/test/java/com/killrvideo/service/comment/test/CommentDseDaoTest.java b/killrvideo-service-comments/src/test/java/com/killrvideo/service/comment/test/CommentDseDaoTest.java index 1acb256a..ef2f1e3a 100644 --- a/killrvideo-service-comments/src/test/java/com/killrvideo/service/comment/test/CommentDseDaoTest.java +++ b/killrvideo-service-comments/src/test/java/com/killrvideo/service/comment/test/CommentDseDaoTest.java @@ -1,9 +1,10 @@ package com.killrvideo.service.comment.test; -import static com.datastax.oss.driver.api.querybuilder.SchemaBuilder.createTable; import static com.killrvideo.dse.utils.DseUtils.createKeySpaceSimpleStrategy; import static com.killrvideo.dse.utils.DseUtils.isTableEmpty; import static com.killrvideo.dse.utils.DseUtils.truncateTable; +import static com.killrvideo.service.comment.dao.CommentDseDaoUtils.stmtCreateTableCommentByUser; +import static com.killrvideo.service.comment.dao.CommentDseDaoUtils.stmtCreateTableCommentByVideo; import java.net.InetSocketAddress; import java.util.UUID; @@ -18,8 +19,6 @@ import com.datastax.dse.driver.api.core.DseSession; import com.datastax.oss.driver.api.core.CqlIdentifier; -import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder; -import com.datastax.oss.driver.api.core.type.DataTypes; import com.datastax.oss.driver.internal.core.metadata.DefaultEndPoint; import com.killrvideo.dse.dao.DseSchema; import com.killrvideo.dse.dto.ResultListPage; @@ -34,8 +33,9 @@ /** * Integration Test for Comment Services. */ +@SuppressWarnings("unused") public class CommentDseDaoTest implements DseSchema { - + /* protected static CqlIdentifier dseKeyspace = CqlIdentifier.fromCql("killrvideo_test_comments"); protected static int dseKeyspaceRF = 1; protected static InetSocketAddress dseContactPoint = new InetSocketAddress("localhost", 9042);; @@ -72,7 +72,9 @@ public static void setupDse() { .addContactEndPoint(new DefaultEndPoint(dseContactPoint)) .withLocalDatacenter(dseLocalDc).build(); createKeySpaceSimpleStrategy(dseSession, dseKeyspace.asInternal(), dseKeyspaceRF); - createTableComments(dseSession); + dseSession.execute(stmtCreateTableCommentByUser(dseKeyspace)); + dseSession.execute(stmtCreateTableCommentByVideo(dseKeyspace)); + commentDseDao = new CommentDseDaoMapperBuilder(dseSession).build().commentDao(dseKeyspace); } @@ -185,25 +187,5 @@ public void should_find_record_when_searchByVideo() { // Then Assertions.assertEquals(1, res.getResults().size()); Assertions.assertEquals(UUID.fromString("6fd4df0a-74ca-4891-ae3b-0c16e37880ed"), res.getResults().get(0).getVideoid()); - } - - /** Javadoc in {@link CommentDseDao} */ - private static void createTableComments(DseSession dseSession) { - dseSession.execute(createTable(dseKeyspace, TABLENAME_COMMENTS_BY_USER_).ifNotExists() - .withPartitionKey(COMMENTS_COLUMN_USERID, DataTypes.UUID) - .withClusteringColumn(COMMENTS_COLUMN_COMMENTID, DataTypes.TIMEUUID) - .withColumn(COMMENTS_COLUMN_COMMENT, DataTypes.TEXT) - .withColumn(COMMENTS_COLUMN_VIDEOID, DataTypes.UUID) - .withClusteringOrder(COMMENTS_COLUMN_COMMENTID, ClusteringOrder.DESC) - .withComment("List comments on user page") - .build()); - dseSession.execute(createTable(dseKeyspace, TABLENAME_COMMENTS_BY_VIDEO_).ifNotExists() - .withPartitionKey(COMMENTS_COLUMN_VIDEOID, DataTypes.UUID) - .withClusteringColumn(COMMENTS_COLUMN_COMMENTID, DataTypes.TIMEUUID) - .withColumn(COMMENTS_COLUMN_COMMENT, DataTypes.TEXT) - .withColumn(COMMENTS_COLUMN_USERID, DataTypes.UUID) - .withClusteringOrder(COMMENTS_COLUMN_COMMENTID, ClusteringOrder.DESC) - .withComment("List comments on user page") - .build()); - } + }*/ } diff --git a/killrvideo-service-ratings/.factorypath b/killrvideo-service-ratings/.factorypath index 8bf58168..c579201f 100644 --- a/killrvideo-service-ratings/.factorypath +++ b/killrvideo-service-ratings/.factorypath @@ -1,137 +1,58 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - - - - + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/killrvideo-service-ratings/pom.xml b/killrvideo-service-ratings/pom.xml index 200f6fa0..996b842e 100644 --- a/killrvideo-service-ratings/pom.xml +++ b/killrvideo-service-ratings/pom.xml @@ -3,48 +3,31 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - - - 4.0.0 killrvideo-service-ratings + killrvideo-service-ratings Service Ratings - - - com.datastax killrvideo-parent 6.7.0 - - - + com.datastax killrvideo-commons ${project.version} + - io.grpc - grpc-all - - - commons-codec - commons-codec - - - org.apache.commons - commons-collections4 - - - org.springframework - spring-context + org.junit.jupiter + junit-jupiter + test + diff --git a/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dao/RatingDseDao.java b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dao/RatingDseDao.java index 8e4def87..c65820c8 100644 --- a/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dao/RatingDseDao.java +++ b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dao/RatingDseDao.java @@ -1,81 +1,20 @@ package com.killrvideo.service.rating.dao; -import static com.datastax.driver.core.querybuilder.QueryBuilder.update; - import java.util.Optional; import java.util.UUID; -import java.util.concurrent.CompletableFuture; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Repository; +import java.util.concurrent.CompletionStage; -import com.datastax.driver.core.BoundStatement; -import com.datastax.driver.core.ConsistencyLevel; -import com.datastax.driver.core.PreparedStatement; -import com.datastax.driver.core.RegularStatement; -import com.datastax.driver.core.querybuilder.QueryBuilder; -import com.datastax.driver.dse.DseSession; -import com.datastax.driver.mapping.Mapper; -import com.killrvideo.dse.dao.DseDaoSupport; -import com.killrvideo.service.rating.dto.VideoRating; -import com.killrvideo.service.rating.dto.VideoRatingByUser; -import com.killrvideo.utils.FutureUtils; +import com.datastax.oss.driver.api.mapper.annotations.Dao; +import com.datastax.oss.driver.api.mapper.annotations.QueryProvider; +import com.datastax.oss.driver.api.mapper.annotations.Select; +import com.killrvideo.service.rating.dto.VideoRatings; +import com.killrvideo.service.rating.dto.VideoRatingsByUser; /** - * Implementations of operation for Videos. - * - * @author DataStax Developer Advocates team. + * Operations against DSE (Cassandra Part) to implements ratings */ -@Repository("killrvideo.rating.dao.dse") -public class RatingDseDao extends DseDaoSupport { - - /** Logger for that class. */ - private static Logger LOGGER = LoggerFactory.getLogger(RatingDseDao.class); - - /** Dse Data Model concerns. */ - public static final String TABLENAME_VIDEOS_RATINGS = "video_ratings"; - public static final String TABLENAME_VIDEOS_RATINGS_BYUSER = "video_ratings_by_user"; - - /** Mapper to ease queries. */ - protected Mapper < VideoRating > mapperVideoRating; - protected Mapper < VideoRatingByUser > mapperVideoRatingByUser; - - /** Precompile statements to speed up queries. */ - private PreparedStatement updateRating; - - /** - * Default constructor. - */ - public RatingDseDao() { - super(); - } - - /** - * Allow explicit intialization for test purpose. - */ - public RatingDseDao(DseSession dseSession) { - super(dseSession); - } - - /** {@inheritDoc} */ - @Override - protected void initialize() { - // Mapper - mapperVideoRating = mappingManager.mapper(VideoRating.class); - mapperVideoRatingByUser = mappingManager.mapper(VideoRatingByUser.class); - - // Prepare requests - String videoRatingsTableName = mapperVideoRating.getTableMetadata().getName(); - String videoRatingsKeyspace = mapperVideoRating.getTableMetadata().getKeyspace().getName(); - - RegularStatement updateStatement = update(videoRatingsKeyspace, videoRatingsTableName) - .with(QueryBuilder.incr(VideoRating.COLUMN_RATING_COUNTER)) - .and(QueryBuilder.incr(VideoRating.COLUMN_RATING_TOTAL, QueryBuilder.bindMarker())) - .where(QueryBuilder.eq(VideoRating.COLUMN_VIDEOID, QueryBuilder.bindMarker())); - updateRating = dseSession.prepare(updateStatement); - updateRating.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM); - } +@Dao +public interface RatingDseDao { /** * Create a rating. @@ -87,43 +26,9 @@ protected void initialize() { * @param rating * current rating */ - public CompletableFuture rateVideo( UUID videoId, UUID userId, Integer rating) { - - // Param validations - assertNotNull("rateVideo", "videoId", videoId); - assertNotNull("rateVideo", "userId", userId); - assertNotNull("rateVideo", "rating", rating); - - // Create Queries - BoundStatement statement = updateRating.bind() - .setLong(VideoRating.COLUMN_RATING_TOTAL, rating) - .setUUID(VideoRating.COLUMN_VIDEOID, videoId); - - VideoRatingByUser entity = new VideoRatingByUser(videoId, userId, rating); - - // Logging at DEBUG - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Rating {} on video {} for user {}", rating, videoId, userId); - } - - /** - * Here, instead of using logged batch, we can insert both mutations asynchronously - * In case of error, we log the request into the mutation error log for replay later - * by another micro-service - * - * Something else to notice is I am using both a prepared statement with executeAsync() - * and a call to the mapper's saveAsync() methods. I could have kept things uniform - * and stuck with both prepared/bind statements, but I wanted to illustrate the combination - * and use the mapper for the second statement because it is a simple save operation with no - * options, increments, etc... A key point is in the case you see below both statements are actually - * prepared, the first one I did manually in a more traditional sense and in the second one the - * mapper will prepare the statement for you automagically. - */ - return CompletableFuture.allOf( - FutureUtils.asCompletableFuture(dseSession.executeAsync(statement)), - // asCompletableFuture(dseSession.executeAsync(mapperVideoRatingByUser.saveQuery(entity))), - FutureUtils.asCompletableFuture(mapperVideoRatingByUser.saveAsync(entity))); - } + @QueryProvider(providerClass = RatingDseDaoQueryProvider.class, + entityHelpers = { VideoRatingsByUser.class}) + CompletionStage rateVideo( UUID videoId, UUID userId, Integer rating); /** * VideoId matches the partition key set in the VideoRating class. @@ -133,10 +38,8 @@ public CompletableFuture rateVideo( UUID videoId, UUID userId, Integer rat * @return * find rating */ - public CompletableFuture< Optional < VideoRating > > findRating(UUID videoId) { - assertNotNull("findRating", "videoId", videoId); - return FutureUtils.asCompletableFuture(mapperVideoRating.getAsync(videoId)).thenApplyAsync(Optional::ofNullable); - } + @Select + CompletionStage< Optional < VideoRatings > > findRating(UUID videoId); /** * Find rating from videoid and userid. @@ -148,12 +51,7 @@ public CompletableFuture< Optional < VideoRating > > findRating(UUID videoId) { * @return * video rating is exist. */ - public CompletableFuture< Optional < VideoRatingByUser > > findUserRating(UUID videoId, UUID userid) { - assertNotNull("findUserRating", "videoId", videoId); - assertNotNull("findUserRating", "userid", userid); - return FutureUtils - .asCompletableFuture(mapperVideoRatingByUser.getAsync(videoId, userid)) - .thenApplyAsync(Optional::ofNullable); - } - + @Select + CompletionStage< Optional < VideoRatingsByUser > > findUserRating(UUID videoId, UUID userid); + } diff --git a/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dao/RatingDseDaoMapper.java b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dao/RatingDseDaoMapper.java new file mode 100644 index 00000000..5d84a513 --- /dev/null +++ b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dao/RatingDseDaoMapper.java @@ -0,0 +1,26 @@ +package com.killrvideo.service.rating.dao; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.mapper.annotations.Dao; +import com.datastax.oss.driver.api.mapper.annotations.DaoFactory; +import com.datastax.oss.driver.api.mapper.annotations.DaoKeyspace; +import com.datastax.oss.driver.api.mapper.annotations.Mapper; + +/** + * Annotated as {@link Mapper} will generate working {@link Dao}. + */ +@Mapper +public interface RatingDseDaoMapper { + + /** + * Initialization of Dao {@link RatingDseDao} + * + * @param keyspace + * working keyspace name + * @return + * instanciation with the mappers + */ + @DaoFactory + RatingDseDao ratingDao(@DaoKeyspace CqlIdentifier keyspace); + +} diff --git a/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dao/RatingDseDaoQueryProvider.java b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dao/RatingDseDaoQueryProvider.java new file mode 100644 index 00000000..5d65169b --- /dev/null +++ b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dao/RatingDseDaoQueryProvider.java @@ -0,0 +1,77 @@ +package com.killrvideo.service.rating.dao; + +import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.bindMarker; +import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.update; +import static com.datastax.oss.driver.api.querybuilder.relation.Relation.column; + +import java.util.UUID; +import java.util.concurrent.CompletionStage; + +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.cql.PreparedStatement; +import com.datastax.oss.driver.api.mapper.MapperContext; +import com.datastax.oss.driver.api.mapper.annotations.QueryProvider; +import com.datastax.oss.driver.api.mapper.entity.EntityHelper; +import com.datastax.oss.driver.api.querybuilder.QueryBuilder; +import com.killrvideo.dse.dao.DseSchema; +import com.killrvideo.dse.utils.DseUtils; +import com.killrvideo.service.rating.dto.VideoRatings; +import com.killrvideo.service.rating.dto.VideoRatingsByUser; + + +/** + * Implementation of specific queries for {@link RatingDseDao} interface. + * + * @author DataStax Developer Advocates team. + */ + +public class RatingDseDaoQueryProvider implements DseSchema { + + private final CqlSession dseSession; + + private final EntityHelper entityHelperVideoRatingByUser; + + private PreparedStatement psIncVideoRatingsCounters; + + private PreparedStatement psInsertVideoRatingsByUser; + + /** + * Constructor invoked by the DataStax driver based on Annotation {@link QueryProvider} + * set on class {@link RatingDseDao}. + * + * @param context + * context to extrat dse session + * @param helperVideo + * entity helper to interact with bean {@link VideoRatings} + * @param helperVideoByUser + * entity helper to interact with bean {@link VideoRatingsByUser} + */ + public RatingDseDaoQueryProvider(MapperContext context, + EntityHelper helperVideoByUser) { + this.dseSession = context.getSession(); + this.entityHelperVideoRatingByUser = helperVideoByUser; + this.psInsertVideoRatingsByUser = dseSession.prepare(helperVideoByUser.insert().asCql()); + this.psIncVideoRatingsCounters = dseSession.prepare(update(TABLENAME_VIDEO_RATINGS) + .increment(RATING_COLUMN_RATING_COUNTER_, QueryBuilder.bindMarker(RATING_COLUMN_RATING_COUNTER_)) + .increment(RATING_COLUMN_RATING_TOTAL_, QueryBuilder.bindMarker(RATING_COLUMN_RATING_TOTAL_)) + .where(column(RATING_COLUMN_VIDEOID_).isEqualTo(bindMarker(RATING_COLUMN_VIDEOID_))) + .build()); + } + + /** Javadoc in {@link RatingDseDao} */ + public CompletionStage rateVideo(UUID videoId, UUID userId, Integer rating) { + return dseSession + // (1) Increments counters in video_ratings + .executeAsync(psIncVideoRatingsCounters + .bind() + .setLong(RATING_COLUMN_RATING_COUNTER_, rating) + .setUuid(RATING_COLUMN_VIDEOID_, videoId)) + // (2) Then, add record in video_ratings_by_user + .thenApply(rs -> dseSession.executeAsync( + DseUtils.bind(psInsertVideoRatingsByUser, + new VideoRatingsByUser(videoId, userId, rating), + entityHelperVideoRatingByUser))) + // (3) Finally, mapped to null as CompletionStage + .thenApply(rs ->null); + } +} diff --git a/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dto/VideoRating.java b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dto/VideoRatings.java similarity index 66% rename from killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dto/VideoRating.java rename to killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dto/VideoRatings.java index a0b33cf4..c9968725 100644 --- a/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dto/VideoRating.java +++ b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dto/VideoRatings.java @@ -3,38 +3,28 @@ import java.io.Serializable; import java.util.UUID; -import com.datastax.driver.mapping.annotations.Column; -import com.datastax.driver.mapping.annotations.PartitionKey; -import com.datastax.driver.mapping.annotations.Table; -import com.killrvideo.model.CommonConstants; -import com.killrvideo.service.rating.dao.RatingDseDao; +import com.datastax.oss.driver.api.mapper.annotations.CqlName; +import com.datastax.oss.driver.api.mapper.annotations.Entity; +import com.datastax.oss.driver.api.mapper.annotations.PartitionKey; +import com.killrvideo.dse.dao.DseSchema; /** * Pojo representing DTO for table 'video_ratings'. - * - * @author DataStax Developer Advocates team. */ -@Table(name= - RatingDseDao.TABLENAME_VIDEOS_RATINGS, - keyspace= - CommonConstants.KILLRVIDEO_KEYSPACE) -public class VideoRating implements Serializable { +@Entity +public class VideoRatings implements Serializable, DseSchema { /** Serial. */ private static final long serialVersionUID = -8874199914791405808L; - /** Column names in the DB. */ - public static final String COLUMN_RATING_COUNTER = "rating_counter"; - public static final String COLUMN_RATING_TOTAL = "rating_total"; - public static final String COLUMN_VIDEOID = "videoid"; - @PartitionKey + @CqlName(RATING_COLUMN_VIDEOID) private UUID videoid; - @Column(name = COLUMN_RATING_COUNTER) + @CqlName(RATING_COLUMN_RATING_COUNTER) private Long ratingCounter; - @Column(name = COLUMN_RATING_TOTAL) + @CqlName(RATING_COLUMN_RATING_TOTAL) private Long ratingTotal; /** diff --git a/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dto/VideoRatingByUser.java b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dto/VideoRatingsByUser.java similarity index 73% rename from killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dto/VideoRatingByUser.java rename to killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dto/VideoRatingsByUser.java index efa1d0d8..fb9f4a92 100644 --- a/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dto/VideoRatingByUser.java +++ b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/dto/VideoRatingsByUser.java @@ -3,45 +3,43 @@ import java.io.Serializable; import java.util.UUID; -import com.datastax.driver.mapping.annotations.ClusteringColumn; -import com.datastax.driver.mapping.annotations.Column; -import com.datastax.driver.mapping.annotations.PartitionKey; -import com.datastax.driver.mapping.annotations.Table; -import com.killrvideo.model.CommonConstants; -import com.killrvideo.service.rating.dao.RatingDseDao; +import com.datastax.oss.driver.api.mapper.annotations.ClusteringColumn; +import com.datastax.oss.driver.api.mapper.annotations.CqlName; +import com.datastax.oss.driver.api.mapper.annotations.Entity; +import com.datastax.oss.driver.api.mapper.annotations.PartitionKey; +import com.killrvideo.dse.dao.DseSchema; /** * Pojo representing DTO for table 'video_ratings_by_user'. * * @author DataStax Developer Advocates team. */ -@Table(name= - RatingDseDao.TABLENAME_VIDEOS_RATINGS_BYUSER, - keyspace= - CommonConstants.KILLRVIDEO_KEYSPACE) -public class VideoRatingByUser implements Serializable { +@Entity +public class VideoRatingsByUser implements Serializable, DseSchema { /** Serial. */ private static final long serialVersionUID = 7124040203261999049L; @PartitionKey + @CqlName(RATING_COLUMN_VIDEOID) private UUID videoid; @ClusteringColumn + @CqlName(RATING_COLUMN_USERID) private UUID userid; - @Column + @CqlName(RATING_COLUMN_RATING) private int rating; /** * Default constructor (reflection) */ - public VideoRatingByUser() {} + public VideoRatingsByUser() {} /** * Constructor with all parameters. */ - public VideoRatingByUser(UUID videoid, UUID userid, int rating) { + public VideoRatingsByUser(UUID videoid, UUID userid, int rating) { this.videoid = videoid; this.userid = userid; this.rating = rating; diff --git a/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/grpc/RatingsServiceGrpcMapper.java b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/grpc/RatingsServiceGrpcMapper.java index cb0be4fa..ccc92c95 100644 --- a/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/grpc/RatingsServiceGrpcMapper.java +++ b/killrvideo-service-ratings/src/main/java/com/killrvideo/service/rating/grpc/RatingsServiceGrpcMapper.java @@ -4,8 +4,8 @@ import java.util.Optional; -import com.killrvideo.service.rating.dto.VideoRating; -import com.killrvideo.service.rating.dto.VideoRatingByUser; +import com.killrvideo.service.rating.dto.VideoRatings; +import com.killrvideo.service.rating.dto.VideoRatingsByUser; import killrvideo.ratings.RatingsServiceOuterClass.GetRatingResponse; import killrvideo.ratings.RatingsServiceOuterClass.GetUserRatingResponse; @@ -26,7 +26,7 @@ private RatingsServiceGrpcMapper() { /** * Mapping to generated GPRC beans. */ - public static GetRatingResponse maptoRatingResponse(VideoRating vr) { + public static GetRatingResponse maptoRatingResponse(VideoRatings vr) { return GetRatingResponse.newBuilder() .setVideoId(uuidToUuid(vr.getVideoid())) .setRatingsCount(Optional.ofNullable(vr.getRatingCounter()).orElse(0L)) @@ -37,7 +37,7 @@ public static GetRatingResponse maptoRatingResponse(VideoRating vr) { /** * Mapping to generated GPRC beans. */ - public static GetUserRatingResponse maptoUserRatingResponse(VideoRatingByUser vr) { + public static GetUserRatingResponse maptoUserRatingResponse(VideoRatingsByUser vr) { return GetUserRatingResponse.newBuilder() .setVideoId(uuidToUuid(vr.getVideoid())) .setUserId(uuidToUuid(vr.getUserid())) diff --git a/killrvideo-service-ratings/src/test/java/com/killrvideo/service/rating/test/RatingDseDaoTest.java b/killrvideo-service-ratings/src/test/java/com/killrvideo/service/rating/test/RatingDseDaoTest.java new file mode 100644 index 00000000..04bae3e5 --- /dev/null +++ b/killrvideo-service-ratings/src/test/java/com/killrvideo/service/rating/test/RatingDseDaoTest.java @@ -0,0 +1,128 @@ +package com.killrvideo.service.rating.test; + +import static com.datastax.oss.driver.api.querybuilder.SchemaBuilder.createTable; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; +import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder; +import com.datastax.oss.driver.api.core.type.DataTypes; +import com.killrvideo.dse.dao.DseSchema; + +/** + * Integration Test for Comment Services. + */ +@SuppressWarnings("unused") +public class RatingDseDaoTest implements DseSchema { + /* + protected static CqlIdentifier dseKeyspace = CqlIdentifier.fromCql("killrvideo_test_rating"); + protected static int dseKeyspaceRF = 1; + protected static InetSocketAddress dseContactPoint = new InetSocketAddress("localhost", 9042);; + protected static String dseLocalDc = "dc1"; + protected static DseSession dseSession = null; + protected static GenericContainer dseContainer = null; + + protected static RatingDseDao ratingDseDao = null; + + @BeforeAll + public static void setupDse() { + dseSession = DseSession.builder() + .addContactEndPoint(new DefaultEndPoint(dseContactPoint)) + .withLocalDatacenter(dseLocalDc).build(); + createKeySpaceSimpleStrategy(dseSession, dseKeyspace.asInternal(), dseKeyspaceRF); + dseSession.execute(stmtCreateTableVideoRating(dseKeyspace)); + dseSession.execute(stmtCreateTableVideoRatingByUser(dseKeyspace)); + ratingDseDao = new RatingDseDaoMapperBuilder(dseSession).build().ratingDao(dseKeyspace); + } + + @BeforeEach + public void truncate() { + truncateTable(dseSession, dseKeyspace, TABLENAME_VIDEO_RATINGS_); + truncateTable(dseSession, dseKeyspace, TABLENAME_VIDEO_RATINGS_BYUSER_); + } + + @AfterAll + public static void cleanUp() { + dropKeyspace(dseSession, dseKeyspace.asInternal()); + dseSession.close(); + } + + @Test + @DisplayName("Rate a videos") + public void should_create_record_when_table_empty() + throws InterruptedException, ExecutionException { + // Given + Assertions.assertTrue(isTableEmpty(dseSession, dseKeyspace, TABLENAME_VIDEO_RATINGS_)); + Assertions.assertTrue(isTableEmpty(dseSession, dseKeyspace, TABLENAME_VIDEO_RATINGS_BYUSER_)); + + // When + CompletionStage cs1 = ratingDseDao.rateVideo( + UUID.fromString("6fd4df0a-74ca-4891-ae3b-0c16e37880ed"), + UUID.fromString("2d32af9c-7889-4256-aedb-b458976f781c"), 5); + CompletionStage cs2 = ratingDseDao.rateVideo( + UUID.fromString("6fd4df0a-74ca-4891-ae3b-0c16e37880ee"), + UUID.fromString("2d32af9c-7889-4256-aedb-b458976f781c"), 4); + + // Then, wait up to request completed + cs1.toCompletableFuture().get(); + cs2.toCompletableFuture().get(); + Assertions.assertFalse(isTableEmpty(dseSession, dseKeyspace, TABLENAME_VIDEO_RATINGS_)); + Assertions.assertFalse(isTableEmpty(dseSession, dseKeyspace, TABLENAME_VIDEO_RATINGS_BYUSER_)); + } + + @Test + @DisplayName("Find rating with invalid videoid is empty") + public void should_return_optionalEmpty_if_invalid_id() + throws InterruptedException, ExecutionException { + Assertions.assertTrue(ratingDseDao.findRating(UUID.fromString("6fd4df0a-74ca-4891-ae3b-0c16e37880ef")) + .toCompletableFuture().get() + .isEmpty()); + } + + @Test + @DisplayName("Find rating for user with invalid videoid/userid is empty") + public void should_return_optionalEmpty_if_invalid_couple() + throws InterruptedException, ExecutionException { + Assertions.assertTrue(ratingDseDao.findUserRating( + UUID.fromString("6fd4df0a-74ca-4891-ae3b-0c16e37880ee"), + UUID.fromString("2d32af9c-7889-4256-aedb-b458976f781e")) + .toCompletableFuture().get() + .isEmpty()); + } + + @Test + @DisplayName("Find rating based on ids") + public void should_findRating_of_exists() + throws InterruptedException, ExecutionException { + // Given + UUID videoid = UUID.fromString("6fd4df0a-74ca-4891-ae3b-0c16e37880ed"); + UUID userid = UUID.fromString("2d32af9c-7889-4256-aedb-b458976f781c"); + ratingDseDao.rateVideo(videoid, userid, 5).toCompletableFuture().get(); + // When + Optional ratings = ratingDseDao.findRating(videoid).toCompletableFuture().get(); + Optional ratingByUser = ratingDseDao.findUserRating(videoid, userid).toCompletableFuture().get(); + // Then + Assertions.assertFalse(ratings.isEmpty()); + Assertions.assertFalse(ratingByUser.isEmpty()); + Assertions.assertEquals(5, ratings.get().getRatingCounter().intValue()); + Assertions.assertEquals(5, ratingByUser.get().getRating()); + } + + */ + + public static SimpleStatement stmtCreateTableVideoRating(CqlIdentifier kspace) { + return createTable(kspace, TABLENAME_VIDEO_RATINGS_).ifNotExists() + .withPartitionKey(RATING_COLUMN_VIDEOID_, DataTypes.UUID) + .withColumn(RATING_COLUMN_RATING_COUNTER_, DataTypes.COUNTER) + .withColumn(RATING_COLUMN_RATING_TOTAL_, DataTypes.COUNTER) + .build(); + } + + public static SimpleStatement stmtCreateTableVideoRatingByUser(CqlIdentifier kspace) { + return createTable(kspace, TABLENAME_VIDEO_RATINGS_BYUSER_).ifNotExists() + .withPartitionKey(RATING_COLUMN_VIDEOID_, DataTypes.UUID) + .withClusteringColumn(RATING_COLUMN_USERID_, DataTypes.UUID) + .withColumn(RATING_COLUMN_RATING_, DataTypes.INT) + .withClusteringOrder(RATING_COLUMN_USERID_, ClusteringOrder.ASC) + .build(); + } +} diff --git a/killrvideo-service-search/.factorypath b/killrvideo-service-search/.factorypath index 66b136b8..c579201f 100644 --- a/killrvideo-service-search/.factorypath +++ b/killrvideo-service-search/.factorypath @@ -1,136 +1,58 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - - - - + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - + + + + + + + + diff --git a/killrvideo-service-search/pom.xml b/killrvideo-service-search/pom.xml index d801ff1a..a81e9efa 100644 --- a/killrvideo-service-search/pom.xml +++ b/killrvideo-service-search/pom.xml @@ -6,19 +6,25 @@ killrvideo-service-search + killrvideo-service-search Service Search - - + + com.datastax killrvideo-parent 6.7.0 - - + + com.datastax killrvideo-commons ${project.version} + + org.junit.jupiter + junit-jupiter + test + + diff --git a/killrvideo-service-search/src/main/java/com/killrvideo/service/search/dao/SearchDseDao.java b/killrvideo-service-search/src/main/java/com/killrvideo/service/search/dao/SearchDseDao.java index c5d22c85..938d5510 100644 --- a/killrvideo-service-search/src/main/java/com/killrvideo/service/search/dao/SearchDseDao.java +++ b/killrvideo-service-search/src/main/java/com/killrvideo/service/search/dao/SearchDseDao.java @@ -1,280 +1,72 @@ package com.killrvideo.service.search.dao; -import java.util.HashSet; import java.util.Optional; -import java.util.Set; import java.util.TreeSet; -import java.util.concurrent.CompletableFuture; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.concurrent.CompletionStage; -import javax.annotation.PostConstruct; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Repository; - -import com.datastax.driver.core.BoundStatement; -import com.datastax.driver.core.ConsistencyLevel; -import com.datastax.driver.core.PagingState; -import com.datastax.driver.core.PreparedStatement; -import com.datastax.driver.core.ResultSet; -import com.datastax.driver.core.ResultSetFuture; -import com.datastax.driver.core.Row; -import com.datastax.driver.core.querybuilder.QueryBuilder; -import com.datastax.driver.dse.DseSession; -import com.datastax.driver.mapping.Mapper; -import com.google.common.reflect.TypeToken; -import com.killrvideo.dse.dao.DseDaoSupport; +import com.datastax.oss.driver.api.mapper.annotations.Dao; +import com.datastax.oss.driver.api.mapper.annotations.QueryProvider; import com.killrvideo.dse.dto.ResultListPage; import com.killrvideo.dse.dto.Video; -import com.killrvideo.utils.FutureUtils; /** - * Implementations of operation for Videos. - * + * Implementing + * * @author DataStax Developer Advocates team. */ -@Repository -public class SearchDseDao extends DseDaoSupport { - - /** Logger for that class. */ - private static Logger LOGGER = LoggerFactory.getLogger(SearchDseDao.class); - - /** Mapper to ease queries. */ - protected Mapper < Video > mapperVideo; - - /** Precompile statements to speed up queries. */ - private PreparedStatement findSuggestedTags; - - private PreparedStatement findVideosByTags; - - /** - * Create a set of sentence conjunctions and other "undesirable" - * words we will use later to exclude from search results. - * Had to use .split() below because of the following conversation: - * https://github.com/spring-projects/spring-boot/issues/501 - */ - @Value("#{'${killrvideo.search.ignoredWords}'.split(',')}") - private Set ignoredWords = new HashSet<>(); - - /** - * Wrap search queries with "paging":"driver" to dynamically enable - * paging to ensure we pull back all available results in the application. - * https://docs.datastax.com/en/dse/6.0/cql/cql/cql_using/search_index/cursorsDeepPaging.html#cursorsDeepPaging__using-paging-with-cql-solr-queries-solrquery-Rim2GsbY - */ - final private String pagingDriverStart = "{\"q\":\""; - final private String pagingDriverEnd = "\", \"paging\":\"driver\"}"; - - /** - * Default constructor. - */ - public SearchDseDao() { - super(); - } - - /** - * Allow explicit intialization for test purpose. - */ - public SearchDseDao(DseSession dseSession) { - super(dseSession); - } - - /** {@inheritDoc} */ - @PostConstruct - protected void initialize () { - mapperVideo = mappingManager.mapper(Video.class); - - // Using Mapper and annotated bean to get constants value - String keyspaceVideo = mapperVideo.getTableMetadata().getKeyspace().getName(); - String tableNameVideo = mapperVideo.getTableMetadata().getName(); - - // Statement for tags - findSuggestedTags = dseSession.prepare(QueryBuilder - .select("name", "tags", "description") - .from(keyspaceVideo, tableNameVideo) - .where(QueryBuilder.eq("solr_query", QueryBuilder.bindMarker()))); - findSuggestedTags.setConsistencyLevel(ConsistencyLevel.LOCAL_ONE); - - // Statement for videos - findVideosByTags = dseSession.prepare(QueryBuilder - .select().all().from(keyspaceVideo, tableNameVideo) - .where(QueryBuilder.eq("solr_query", QueryBuilder.bindMarker()))); - findVideosByTags.setConsistencyLevel(ConsistencyLevel.LOCAL_ONE); - } - - /** - * Do a Solr query against DSE search to find videos using Solr's ExtendedDisMax query parser. Query the - * name, tags, and description fields in the videos table giving a boost to matches in the name and tags - * fields as opposed to the description field - * More info on ExtendedDisMax: http://wiki.apache.org/solr/ExtendedDisMax - * - * Notice the "paging":"driver" parameter. This is to ensure we dynamically - * enable pagination regardless of our nodes dse.yaml setting. - * https://docs.datastax.com/en/dse/5.1/dse-dev/datastax_enterprise/search/cursorsDeepPaging.html#cursorsDeepPaging__srchCursorCQL - */ - public CompletableFuture < ResultListPage