├── slides.pdf ├── pics ├── splash.png ├── astra-data.png ├── astra-token.png ├── swagger-docs.png ├── launch-graphql.png ├── launch-swagger.png ├── astra-cqlconsole.png ├── create-keyspace-1.png ├── create-keyspace-2.png ├── graphql-readdata.png ├── playground-home.png ├── swagger-schemas.png ├── graphql-insertdata.png ├── graphql-createkeyspace.png ├── graphql-createtables.png └── stargate-connect-restapi.png ├── sample-code ├── killrvideo-api-grpc │ ├── src │ │ ├── test │ │ │ ├── resources │ │ │ │ ├── killrvideo.properties │ │ │ │ ├── service │ │ │ │ │ ├── 1_UserManagementService.feature │ │ │ │ │ ├── 4_RatingService.feature │ │ │ │ │ ├── 5_StatisticsService.feature │ │ │ │ │ ├── 6_SearchService.feature │ │ │ │ │ ├── 7_SuggestedVideosService.feature │ │ │ │ │ ├── 3_CommentsService.feature │ │ │ │ │ └── 2_VideoCatalogService.feature │ │ │ │ └── schema.cql │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── killrvideo │ │ │ │ └── grpc │ │ │ │ └── test │ │ │ │ └── CommentServiceGrpcTest.java │ │ └── main │ │ │ ├── resources │ │ │ ├── banner.txt │ │ │ ├── logback.xml │ │ │ ├── proto │ │ │ │ ├── common │ │ │ │ │ └── common_types.proto │ │ │ │ └── comments │ │ │ │ │ └── comments_service.proto │ │ │ └── application.properties │ │ │ └── java │ │ │ └── com │ │ │ └── killrvideo │ │ │ └── grpc │ │ │ ├── KillrVideoGrpcApi.java │ │ │ ├── utils │ │ │ ├── GrpcMapper.java │ │ │ ├── AbstractGrpcHelper.java │ │ │ └── CommentGrpcHelper.java │ │ │ ├── KillrVideoGrpcClient.java │ │ │ ├── GrpcServer.java │ │ │ └── CommentsGrpcService.java │ └── pom.xml ├── killrvideo-api-rest │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── logback.xml │ │ │ └── application.properties │ │ │ └── java │ │ │ └── com │ │ │ └── killrvideo │ │ │ └── rest │ │ │ ├── error │ │ │ └── RestExceptionHandler.java │ │ │ ├── KillrVideoRestApi.java │ │ │ ├── config │ │ │ ├── HomeResource.java │ │ │ └── SwaggerConfig.java │ │ │ └── resource │ │ │ ├── VideoResource.java │ │ │ ├── UserResource.java │ │ │ └── CommentResource.java │ └── pom.xml ├── killrvideo-api-graphql │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── application.properties │ │ │ ├── sample_queries.json │ │ │ ├── introspection_query.graphql │ │ │ └── KillrVideo.graphqls │ │ │ └── java │ │ │ └── com │ │ │ └── killrvideo │ │ │ └── graphql │ │ │ ├── KillrVideoGraphQLApi.java │ │ │ ├── domain │ │ │ ├── ResultPageCommentGQL.java │ │ │ └── CommentGQL.java │ │ │ └── api │ │ │ ├── KillrvideoMutation.java │ │ │ └── KillrvideoQuery.java │ └── pom.xml └── pom.xml ├── demo-app ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── datastax │ │ │ │ └── astra │ │ │ │ ├── order │ │ │ │ ├── OrderRepository.java │ │ │ │ ├── OrderSchemaless.java │ │ │ │ ├── OrderPrimaryKey.java │ │ │ │ └── Order.java │ │ │ │ ├── AstraBootifulApplication.java │ │ │ │ └── conf │ │ │ │ └── AstraApplicationConfiguration.java │ │ └── resources │ │ │ ├── application.properties │ │ │ ├── sample-ddl.cql │ │ │ └── logback.xml │ └── test │ │ └── java │ │ └── com │ │ └── datastax │ │ └── astra │ │ ├── DemoDataSet.java │ │ ├── Test01_CreateAstraInstance.java │ │ ├── Test03_WorkingWithRestApi.java │ │ ├── Test04_WorkingWithDocumentApi.java │ │ ├── Video.java │ │ └── Test02_WorkingWithCassandra.java ├── pom.xml ├── pom.xml.versionsBackup ├── mvnw.cmd └── mvnw ├── .gitignore ├── sample-docker └── docker-compose.yml └── README.md /slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/slides.pdf -------------------------------------------------------------------------------- /pics/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/splash.png -------------------------------------------------------------------------------- /pics/astra-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/astra-data.png -------------------------------------------------------------------------------- /pics/astra-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/astra-token.png -------------------------------------------------------------------------------- /pics/swagger-docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/swagger-docs.png -------------------------------------------------------------------------------- /pics/launch-graphql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/launch-graphql.png -------------------------------------------------------------------------------- /pics/launch-swagger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/launch-swagger.png -------------------------------------------------------------------------------- /pics/astra-cqlconsole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/astra-cqlconsole.png -------------------------------------------------------------------------------- /pics/create-keyspace-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/create-keyspace-1.png -------------------------------------------------------------------------------- /pics/create-keyspace-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/create-keyspace-2.png -------------------------------------------------------------------------------- /pics/graphql-readdata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/graphql-readdata.png -------------------------------------------------------------------------------- /pics/playground-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/playground-home.png -------------------------------------------------------------------------------- /pics/swagger-schemas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/swagger-schemas.png -------------------------------------------------------------------------------- /pics/graphql-insertdata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/graphql-insertdata.png -------------------------------------------------------------------------------- /pics/graphql-createkeyspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/graphql-createkeyspace.png -------------------------------------------------------------------------------- /pics/graphql-createtables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/graphql-createtables.png -------------------------------------------------------------------------------- /pics/stargate-connect-restapi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/conf-grpc-rest-graphql-data-apis/main/pics/stargate-connect-restapi.png -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/test/resources/killrvideo.properties: -------------------------------------------------------------------------------- 1 | 2 | spring.output.ansi.enabled=ALWAYS 3 | 4 | # Application 5 | killrvideo.application.name=killrvideo 6 | killrvideo.application.instance.id=0 7 | 8 | 9 | 10 | # GRPC 11 | grpc.port=8899 12 | 13 | -------------------------------------------------------------------------------- /demo-app/src/main/java/com/datastax/astra/order/OrderRepository.java: -------------------------------------------------------------------------------- 1 | package com.datastax.astra.order; 2 | 3 | import org.springframework.data.cassandra.repository.ReactiveCassandraRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | @Repository 7 | public interface OrderRepository extends ReactiveCassandraRepository { 8 | 9 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | -------------------------------------------------------------------------------- /demo-app/src/main/java/com/datastax/astra/AstraBootifulApplication.java: -------------------------------------------------------------------------------- 1 | package com.datastax.astra; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class AstraBootifulApplication { 8 | 9 | /** No args needed. */ 10 | public static void main(String[] args) { 11 | SpringApplication.run(AstraBootifulApplication.class, args); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /demo-app/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Credentials 2 | astra.clientId=TWRvjlcrgfZYfhcxGZhUlAZH 3 | astra.clientSecret=7xKSrZPLbWxDJ0WXybX.L9buQuAw,2y-it2v3jl1TARPZCgn1rIXRJX.BQ-TAhOeTS_26pkqd2,m8HtOT5bZcxwn7ysP9pUSW0MrO47Y+OEPx_mM-j8I2DGpniJb6AHy 4 | astra.applicationToken=AstraCS:TWRvjlcrgfZYfhcxGZhUlAZH:2174fb7dacfd706a2d14d168706022010e99a7bb7cd133050f46ee0d523b386d 5 | 6 | # Target Database 7 | astra.cloudRegion=eu-central-1 8 | astra.databaseId=f420bc37-b22e-44fc-8371-72fe2202f07d 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ____ __.__.__ .__ ____ ____.__ .___ 2 | | |/ _|__| | | |______\ \ / /|__| __| _/____ ____ 3 | | < | | | | |\_ __ \ Y / | |/ __ |/ __ \/ _ \ 4 | | | \| | |_| |_| | \/\ / | / /_/ \ ___( <_> ) 5 | |____|__ \__|____/____/__| \___/ |__\____ |\___ >____/ 6 | \/ \/ \/ 7 | Brought to you by DataStax Evangelist team 8 | For information, please visit https://killrvideo.github.io 9 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{HH:mm:ss.SSS} %magenta(%-5level) %cyan(%-45logger) : %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-rest/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{HH:mm:ss.SSS} %magenta(%-5level) %cyan(%-45logger) : %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo-app/src/main/java/com/datastax/astra/order/OrderSchemaless.java: -------------------------------------------------------------------------------- 1 | package com.datastax.astra.order; 2 | 3 | import java.time.Instant; 4 | import java.util.UUID; 5 | 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class OrderSchemaless { 14 | 15 | private UUID orderId; 16 | private UUID productId; 17 | private Integer productQuantity; 18 | private String productName; 19 | private Float productPrice; 20 | private Instant addedToOrderTimestamp; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/main/resources/proto/common/common_types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package killrvideo.common; 4 | option csharp_namespace = "KillrVideo.Protobuf"; 5 | 6 | // Represents a v4 UUID/GUID 7 | message Uuid { 8 | // Use string for simplicity sake since most programming languages provide a way 9 | // to parse to/from a UUID string (e.g. 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX') 10 | string value = 1; 11 | } 12 | 13 | // Represents a v1 UUID/GUID (i.e. time-based UUID) 14 | message TimeUuid { 15 | // Just like Uuid, use string to represent TimeUuids (see Uuid comment) 16 | string value = 1; 17 | } -------------------------------------------------------------------------------- /sample-code/killrvideo-api-graphql/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------- 2 | # Application 3 | # --------------------------------------------------------------------------------- 4 | spring.application.name=Killrvideo-API-GraphQL 5 | server.port=8083 6 | server.servlet.context-path=/gql 7 | 8 | # --------------------------------------------------------------------------------- 9 | # Cassandra 10 | # --------------------------------------------------------------------------------- 11 | dse.contactPoints=127.0.0.1 12 | dse.port=9042 13 | dse.localdc=dc1 14 | dse.keyspace=killrvideo 15 | dse.username= 16 | dse.password= 17 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-rest/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------- 2 | # Application 3 | # --------------------------------------------------------------------------------- 4 | spring.application.name=Killrvideo-API-GraphQL 5 | server.port=8081 6 | server.servlet.context-path=/rest 7 | 8 | # --------------------------------------------------------------------------------- 9 | # Cassandra 10 | # --------------------------------------------------------------------------------- 11 | dse.contactPoints=127.0.0.1 12 | dse.port=9042 13 | dse.localdc=dc1 14 | dse.keyspace=killrvideo 15 | dse.username= 16 | dse.password= 17 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/main/java/com/killrvideo/grpc/KillrVideoGrpcApi.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.grpc; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.context.annotation.ComponentScan; 6 | 7 | @ComponentScan(basePackages="com.killrvideo") 8 | @EnableAutoConfiguration 9 | public class KillrVideoGrpcApi { 10 | 11 | /** 12 | * As SpringBoot application, this is the "main" class 13 | */ 14 | public static void main(String[] args) { 15 | SpringApplication app = new SpringApplication(KillrVideoGrpcApi.class); 16 | app.run(args); 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /sample-code/killrvideo-api-graphql/src/main/java/com/killrvideo/graphql/KillrVideoGraphQLApi.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.graphql; 2 | 3 | import org.springframework.boot.Banner; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 6 | import org.springframework.context.annotation.ComponentScan; 7 | 8 | @ComponentScan(basePackages="com.killrvideo") 9 | @EnableAutoConfiguration 10 | public class KillrVideoGraphQLApi { 11 | 12 | /** 13 | * As SpringBoot application, this is the "main" class 14 | */ 15 | public static void main(String[] args) { 16 | SpringApplication app = new SpringApplication(KillrVideoGraphQLApi.class); 17 | app.setBannerMode(Banner.Mode.OFF); 18 | app.run(args); 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /sample-code/killrvideo-api-graphql/src/main/resources/sample_queries.json: -------------------------------------------------------------------------------- 1 | 2 | query { 3 | getVideoComments( 4 | videoid: "701d6204-29e0-4eba-9411-2212437ad98e" 5 | pageSize: 2) { 6 | listOfResults { 7 | dateOfComment 8 | comment 9 | } 10 | nextPage 11 | } 12 | } 13 | 14 | query { 15 | getUserComments( 16 | userid: "47ae8d7c-1e7a-4953-ac27-adb310e8b522" 17 | pageSize: 2) { 18 | listOfResults { 19 | dateOfComment 20 | comment 21 | } 22 | nextPage 23 | } 24 | } 25 | 26 | mutation { 27 | commentOnVideo( 28 | commentid : "ba9eed80-959a-11e8-ba6d-49593414a589" 29 | videoid: "701d6204-29e0-4eba-9411-2212437ad98e" 30 | userid: "57f66e0f-afdb-43da-9237-30c888297516", 31 | text: "Test From GraphQL") { 32 | userid 33 | videoid 34 | commentid 35 | dateOfComment 36 | } 37 | } -------------------------------------------------------------------------------- /sample-code/killrvideo-api-rest/src/main/java/com/killrvideo/rest/error/RestExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.rest.error; 2 | 3 | import org.springframework.core.annotation.Order; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.web.bind.annotation.ControllerAdvice; 6 | import org.springframework.web.bind.annotation.ExceptionHandler; 7 | import org.springframework.web.bind.annotation.ResponseStatus; 8 | 9 | @ControllerAdvice(basePackages = {"com.killrvideo.rest.resource"}) 10 | @Order(0) 11 | public class RestExceptionHandler { 12 | 13 | @ExceptionHandler(value = IllegalArgumentException.class) 14 | @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "bad request") 15 | public void badRequestHandler() { 16 | // Not necessary to handle this exception 17 | } 18 | 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /demo-app/src/test/java/com/datastax/astra/DemoDataSet.java: -------------------------------------------------------------------------------- 1 | package com.datastax.astra; 2 | 3 | import com.dstx.astra.sdk.devops.CloudProviderType; 4 | 5 | public class DemoDataSet { 6 | 7 | // 8 | public static String clientId = "TWRvjlcrgfZYfhcxGZhUlAZH"; 9 | public static String clientSecret = "7xKSrZPLbWxDJ0WXybX.L9buQuAw,2y-it2v3jl1TARPZCgn1rIXRJX.BQ-TAhOeTS_26pkqd2,m8HtOT5bZcxwn7ysP9pUSW0MrO47Y+OEPx_mM-j8I2DGpniJb6AHy"; 10 | public static String appToken = "AstraCS:TWRvjlcrgfZYfhcxGZhUlAZH:2174fb7dacfd706a2d14d168706022010e99a7bb7cd133050f46ee0d523b386d"; 11 | 12 | public static String dbName = "free_db"; 13 | public static String ksName = "keyspace1"; 14 | public static CloudProviderType aws = CloudProviderType.AWS; 15 | public static String awsEast1 = "us-east-1"; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /demo-app/src/main/resources/sample-ddl.cql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS starter_orders ( 2 | order_id uuid, 3 | product_id uuid, 4 | product_quantity int, 5 | product_name text, 6 | product_price decimal, 7 | added_to_order_at timestamp, 8 | PRIMARY KEY ((order_id), product_id) 9 | ) WITH CLUSTERING ORDER BY (product_id DESC); 10 | 11 | 12 | INSERT INTO starter_orders(order_id, product_id, product_quantity, product_name, product_price, added_to_order_at) 13 | VALUES(uuid(), uuid(), 1, 'iPhone', 699.99, toTimestamp(now())); 14 | INSERT INTO starter_orders(order_id, product_id, product_quantity, product_name, product_price, added_to_order_at) 15 | VALUES(uuid(), uuid(), 1, 'iPad', 799.99, toTimestamp(now())); 16 | INSERT INTO starter_orders(order_id, product_id, product_quantity, product_name, product_price, added_to_order_at) 17 | VALUES(uuid(), uuid(), 1, 'Macbook', 2399.3, toTimestamp(now())); 18 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------- 2 | # Application 3 | # --------------------------------------------------------------------------------- 4 | spring.application.name=Killrvideo-API-Rest 5 | server.port=8082 6 | server.servlet.context-path=/grpc 7 | 8 | # --------------------------------------------------------------------------------- 9 | # Cassandra 10 | # --------------------------------------------------------------------------------- 11 | dse.contactPoints=127.0.0.1 12 | dse.port=9042 13 | dse.localdc=dc1 14 | dse.keyspace=killrvideo 15 | dse.username= 16 | dse.password= 17 | 18 | # --------------------------------------------------------------------------------- 19 | # GRPC 20 | # --------------------------------------------------------------------------------- 21 | grpc.port=8899 22 | 23 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-rest/src/main/java/com/killrvideo/rest/KillrVideoRestApi.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.rest; 2 | 3 | import org.springframework.boot.Banner; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 6 | import org.springframework.context.annotation.ComponentScan; 7 | 8 | @ComponentScan(basePackages= { "com.killrvideo" }) 9 | @EnableAutoConfiguration( exclude = { 10 | org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration.class, 11 | org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration.class 12 | } ) 13 | public class KillrVideoRestApi { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication app = new SpringApplication(KillrVideoRestApi.class); 17 | app.setBannerMode(Banner.Mode.OFF); 18 | app.run(args); 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /sample-code/killrvideo-api-rest/src/main/java/com/killrvideo/rest/config/HomeResource.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.rest.config; 2 | 3 | import org.springframework.web.bind.annotation.RequestMapping; 4 | import org.springframework.web.bind.annotation.RequestMethod; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | /** 8 | * Landing page on '/' 9 | * 10 | * @author DataStax Evangelist Team 11 | */ 12 | @RestController 13 | @RequestMapping("/") 14 | public class HomeResource { 15 | 16 | @RequestMapping(value = "/", method = RequestMethod.GET, produces = "text/html") 17 | public String sayHello() { 18 | return new StringBuilder("" 19 | + "" 20 | + " " 21 | + " " 22 | + " " 23 | + " ").toString(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /demo-app/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{HH:mm:ss.SSS} %magenta(%-5level) %cyan(%-47logger) : %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /demo-app/src/main/java/com/datastax/astra/order/OrderPrimaryKey.java: -------------------------------------------------------------------------------- 1 | package com.datastax.astra.order; 2 | 3 | import java.io.Serializable; 4 | import java.util.UUID; 5 | 6 | import org.springframework.data.cassandra.core.cql.PrimaryKeyType; 7 | import org.springframework.data.cassandra.core.mapping.PrimaryKeyClass; 8 | import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn; 9 | 10 | import lombok.AllArgsConstructor; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | 14 | @PrimaryKeyClass 15 | @Data 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | public class OrderPrimaryKey implements Serializable { 19 | 20 | private static final long serialVersionUID = 6680396414187946005L; 21 | 22 | @PrimaryKeyColumn(name = "order_id", ordinal = 0, type = PrimaryKeyType.PARTITIONED) 23 | private UUID orderId; 24 | 25 | @PrimaryKeyColumn(name = "product_id", ordinal = 1, type = PrimaryKeyType.CLUSTERED) 26 | private UUID productId; 27 | } -------------------------------------------------------------------------------- /demo-app/src/main/java/com/datastax/astra/conf/AstraApplicationConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.datastax.astra.conf; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.InitializingBean; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import com.dstx.astra.sdk.AstraClient; 9 | 10 | @Repository 11 | public class AstraApplicationConfiguration implements InitializingBean { 12 | 13 | /** Logger for our Client. */ 14 | private static final Logger LOGGER = LoggerFactory.getLogger(AstraApplicationConfiguration.class); 15 | 16 | /** hold ref to the client. */ 17 | private AstraClient astraClient; 18 | 19 | public AstraApplicationConfiguration(AstraClient astraClient) { 20 | this.astraClient = astraClient; 21 | } 22 | 23 | 24 | /** {@inheritDoc} */ 25 | @Override 26 | public void afterPropertiesSet() throws Exception { 27 | LOGGER.info("Released version from Cassandra {}", astraClient.cqlSession() 28 | .execute("SELECT release_version FROM system.local") 29 | .one() 30 | .getString("release_version")); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/test/resources/service/1_UserManagementService.feature: -------------------------------------------------------------------------------- 1 | @user_scenarios 2 | @user_scenarios_only 3 | Feature: User Management 4 | 5 | Scenario: Creating a non-existing user 6 | Given user with email johndoe@gmail.com does not exist 7 | When I create 1 users with email johndoe@gmail.com and password 123abc 8 | Then I should be able to login with johndoe@gmail.com/123abc 9 | 10 | Scenario: Fails creating an existing user 11 | Given user with credentials helensue@gmail.com/456def already exists 12 | When I create 1 users with email helensue@gmail.com and password 123abc 13 | Then I receive the 'Exception creating user because it already exists' error message for helensue@gmail.com account 14 | 15 | Scenario: Concurrent users creation 16 | Given user with email richarsmith@gmail.com does not exist 17 | When I create 2 users with email richarsmith@gmail.com and password 123abc 18 | Then I receive the 'Exception creating user because it already exists' error message for richarsmith@gmail.com account 19 | 20 | Scenario: Get user profile 21 | Given user with credentials janedoe@gmail.com/789ghi already exists 22 | When I get profile of janedoe@gmail.com 23 | Then the profile janedoe@gmail.com exists -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/test/resources/service/4_RatingService.feature: -------------------------------------------------------------------------------- 1 | @video_scenarios 2 | @user_scenarios 3 | @ratings_scenarios 4 | Feature: Video Rating Management 5 | 6 | Users (userXXX) have their id randomized 7 | 8 | Background: 9 | Given those users already exist: user1, user2, user3 10 | 11 | 12 | Scenario: Get video rating for unrated video 13 | When user1 submit Youtube videos: 14 | | id | name | description | tags | url | 15 | | video1 | b-wing-ucs.mp4 | Lego Star Wars B-Wing UCS | lego,star wars,space| https://www.youtube.com/watch?v=zcryCEQfTwY | 16 | Then video1 has 0 ratings and total 0 stars 17 | And user1 rating for video1 has 0 stars 18 | 19 | 20 | Scenario: Rate video and get video rating 21 | When user1 submit Youtube videos: 22 | | id | name | description | tags | url | 23 | | video2 | y-wing-ucs.mp4 | Lego Star Wars Y-Wing UCS | lego,star wars,space| https://www.youtube.com/watch?v=TRyja74EXp0 | 24 | And user1 rates video2 4 stars 25 | And user2 rates video2 2 stars 26 | And user3 rates video2 5 stars 27 | Then video2 has 3 ratings and total 11 stars 28 | And user3 rating for video2 has 5 stars 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /demo-app/src/main/java/com/datastax/astra/order/Order.java: -------------------------------------------------------------------------------- 1 | package com.datastax.astra.order; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | 6 | import org.springframework.data.cassandra.core.mapping.CassandraType; 7 | import org.springframework.data.cassandra.core.mapping.Column; 8 | import org.springframework.data.cassandra.core.mapping.PrimaryKey; 9 | import org.springframework.data.cassandra.core.mapping.Table; 10 | 11 | import lombok.AllArgsConstructor; 12 | import lombok.Data; 13 | import lombok.NoArgsConstructor; 14 | 15 | @Table(value = "starter_orders") 16 | @Data 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class Order implements Serializable { 20 | 21 | private static final long serialVersionUID = -7527198439031945035L; 22 | 23 | @PrimaryKey 24 | private OrderPrimaryKey key; 25 | 26 | @Column("product_quantity") 27 | @CassandraType(type = CassandraType.Name.INT) 28 | private Integer productQuantity; 29 | 30 | @Column("product_name") 31 | @CassandraType(type = CassandraType.Name.TEXT) 32 | private String productName; 33 | 34 | @CassandraType(type = CassandraType.Name.DECIMAL) 35 | @Column("product_price") 36 | private Float productPrice; 37 | 38 | @CassandraType(type = CassandraType.Name.TIMESTAMP) 39 | @Column("added_to_order_at") 40 | private Instant addedToOrderTimestamp; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-graphql/src/main/java/com/killrvideo/graphql/domain/ResultPageCommentGQL.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.graphql.domain; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class ResultPageCommentGQL { 7 | 8 | private List < CommentGQL > listOfResults = new ArrayList<>(); 9 | 10 | private String nextPage; 11 | 12 | /** 13 | * Getter accessor for attribute 'listOfResults'. 14 | * 15 | * @return 16 | * current value of 'listOfResults' 17 | */ 18 | public List getListOfResults() { 19 | return listOfResults; 20 | } 21 | 22 | /** 23 | * Setter accessor for attribute 'listOfResults'. 24 | * @param listOfResults 25 | * new value for 'listOfResults ' 26 | */ 27 | public void setListOfResults(List listOfResults) { 28 | this.listOfResults = listOfResults; 29 | } 30 | 31 | /** 32 | * Getter accessor for attribute 'nextPage'. 33 | * 34 | * @return 35 | * current value of 'nextPage' 36 | */ 37 | public String getNextPage() { 38 | return nextPage; 39 | } 40 | 41 | /** 42 | * Setter accessor for attribute 'nextPage'. 43 | * @param nextPage 44 | * new value for 'nextPage ' 45 | */ 46 | public void setNextPage(String nextPage) { 47 | this.nextPage = nextPage; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/test/java/com/killrvideo/grpc/test/CommentServiceGrpcTest.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.grpc.test; 2 | 3 | import java.util.UUID; 4 | 5 | import org.junit.Test; 6 | 7 | import com.killrvideo.grpc.KillrVideoGrpcClient; 8 | import com.killrvideo.grpc.utils.GrpcMapper; 9 | 10 | import killrvideo.comments.CommentsServiceOuterClass.GetUserCommentsRequest; 11 | import killrvideo.comments.CommentsServiceOuterClass.GetUserCommentsResponse; 12 | import killrvideo.comments.CommentsServiceOuterClass.UserComment; 13 | 14 | /** 15 | * Sample Operation on GRPC. 16 | * 17 | * @author DataStax Evangelist Team 18 | */ 19 | public class CommentServiceGrpcTest { 20 | 21 | String MY_USER_ID = "2f2f64ee-6a08-4351-8b54-d86f695f4775"; 22 | 23 | @Test 24 | public void testListCommentForUser() throws Exception { 25 | KillrVideoGrpcClient killrVideoClient = new KillrVideoGrpcClient("127.0.0.1", 8899); 26 | 27 | GetUserCommentsRequest gcReq = GetUserCommentsRequest.newBuilder() 28 | .setUserId(GrpcMapper.uuidToUuid(UUID.fromString(MY_USER_ID))) 29 | .setPageSize(10) 30 | .build(); 31 | 32 | GetUserCommentsResponse gcRes = killrVideoClient.getCommentService().getUserComments(gcReq); 33 | Thread.sleep(1000); 34 | 35 | for (UserComment gRpcComment : gcRes.getCommentsList()) { 36 | System.out.println(gRpcComment.getComment()); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-graphql/src/main/resources/introspection_query.graphql: -------------------------------------------------------------------------------- 1 | query IntrospectionQuery { 2 | __schema { 3 | queryType { name } 4 | mutationType { name } 5 | subscriptionType { name } 6 | types { 7 | ...FullType 8 | } 9 | directives { 10 | name 11 | description 12 | args { 13 | ...InputValue 14 | } 15 | onOperation 16 | onFragment 17 | onField 18 | } 19 | } 20 | } 21 | 22 | fragment FullType on __Type { 23 | kind 24 | name 25 | description 26 | fields(includeDeprecated: true) { 27 | name 28 | description 29 | args { 30 | ...InputValue 31 | } 32 | type { 33 | ...TypeRef 34 | } 35 | isDeprecated 36 | deprecationReason 37 | } 38 | inputFields { 39 | ...InputValue 40 | } 41 | interfaces { 42 | ...TypeRef 43 | } 44 | enumValues(includeDeprecated: true) { 45 | name 46 | description 47 | isDeprecated 48 | deprecationReason 49 | } 50 | possibleTypes { 51 | ...TypeRef 52 | } 53 | } 54 | 55 | fragment InputValue on __InputValue { 56 | name 57 | description 58 | type { ...TypeRef } 59 | defaultValue 60 | } 61 | 62 | fragment TypeRef on __Type { 63 | kind 64 | name 65 | ofType { 66 | kind 67 | name 68 | ofType { 69 | kind 70 | name 71 | ofType { 72 | kind 73 | name 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/test/resources/service/5_StatisticsService.feature: -------------------------------------------------------------------------------- 1 | @video_scenarios 2 | @user_scenarios 3 | @stats_scenarios 4 | Feature: Video Statistics Management 5 | 6 | Users (userXXX) have their id randomized 7 | 8 | Background: 9 | Given those users already exist: user1 10 | 11 | 12 | Scenario: Record playback and get number of plays 13 | When user1 submit Youtube videos: 14 | | id | name | description | tags | url | 15 | | video1 | b-wing-ucs.mp4 | Lego Star Wars B-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=zcryCEQfTwY | 16 | | video2 | y-wing-ucs.mp4 | Lego Star Wars Y-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=TRyja74EXp0 | 17 | | video3 | x-wing-ucs.mp4 | Lego Star Wars X-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=YMJvcYDtYJQ | 18 | | video4 | tie-fighter-ucs.mp4 | Lego Star Wars Tie Fighter UCS | lego,star wars,space | https://www.youtube.com/watch?v=Kcph-70owiM | 19 | | video5 | mil-falcon-ucs.mp4 | Lego Star Wars Millennium Falcon UCS | lego,star wars,space | https://www.youtube.com/watch?v=Q9BhA9POhBQ | 20 | And video1 is watched 13 times 21 | And video2 is watched 17 times 22 | And video3 is watched 9 times 23 | And video4 is watched 21 times 24 | And video5 is watched 15 times 25 | Then video1,video2,video3,video4,video5 statistics shows 13,17,9,21,15 plays 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-graphql/src/main/resources/KillrVideo.graphqls: -------------------------------------------------------------------------------- 1 | # Killrvideo GraphQL API 2 | schema { 3 | # Read-only operations : fetch, search... (Every GraphQL service has a query type) 4 | query: Query 5 | 6 | # Write operations ; insert, update delete (Every GraphQL service may or may not have a mutation type) 7 | mutation: Mutation 8 | } 9 | 10 | # Represent a video comment in GraphQL format 11 | type CommentGQL { 12 | #Unique identifier for a user (tech id) 13 | commentid: ID! 14 | # Unique identifier for a user (required) 15 | userid: String! 16 | # Unique identifier for a video (required) 17 | videoid: String! 18 | # Text of the comment 19 | comment: String 20 | # Insertion Date 21 | dateOfComment: String 22 | } 23 | 24 | type ResultPageCommentGQL { 25 | listOfResults: [CommentGQL]! 26 | nextPage: String 27 | } 28 | 29 | # Searches and read-only operations on KillrVideo Keyspace 30 | type Query { 31 | 32 | # Search in table comment_by_video, eventually with Pagination. 33 | getVideoComments(videoid: String!, commentid: String, pageSize: Int , pageState: String): ResultPageCommentGQL! 34 | 35 | # Search in table comment_by_user, eventually with Pagination. 36 | getUserComments(userid: String!, commentid: String, pageSize: Int , pageState: String): ResultPageCommentGQL! 37 | } 38 | 39 | # Operation that will updated data in DB 40 | type Mutation { 41 | 42 | # Add a comment for dedicated video and known user. 43 | commentOnVideo(commentid: String!, videoid: String!, userid: String!, text: String!): CommentGQL! 44 | 45 | } 46 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/main/java/com/killrvideo/grpc/utils/GrpcMapper.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.grpc.utils; 2 | 3 | import java.time.Instant; 4 | import java.util.Date; 5 | import java.util.UUID; 6 | 7 | import com.google.protobuf.Timestamp; 8 | 9 | import killrvideo.common.CommonTypes.TimeUuid; 10 | import killrvideo.common.CommonTypes.Uuid; 11 | 12 | /** 13 | * Convert Grpc Attribute to common types. 14 | * 15 | * @author DataStax evangelist team. 16 | */ 17 | public class GrpcMapper { 18 | 19 | /** 20 | * Conversion. 21 | */ 22 | public static Timestamp instantToTimeStamp(Instant instant) { 23 | return Timestamp.newBuilder().setSeconds(instant.getEpochSecond()).setNanos(instant.getNano()).build(); 24 | } 25 | 26 | /** 27 | * Conversion. 28 | */ 29 | public static Timestamp epochTimeToTimeStamp(long epoch) { 30 | return Timestamp.newBuilder().setSeconds(epoch).build(); 31 | } 32 | 33 | /** 34 | * Conversion. 35 | */ 36 | public static Timestamp dateToTimestamp(Date date) { 37 | return instantToTimeStamp(date.toInstant()); 38 | } 39 | 40 | /** 41 | * Conversion. 42 | */ 43 | public static Date dateFromTimestamp(Timestamp timestamp) { 44 | return Date.from(Instant.ofEpochSecond(timestamp.getSeconds())); 45 | } 46 | 47 | /** 48 | * Conversion. 49 | */ 50 | public static TimeUuid uuidToTimeUuid(UUID uuid) { 51 | return TimeUuid.newBuilder().setValue(uuid.toString()).build(); 52 | } 53 | 54 | /** 55 | * Conversion. 56 | */ 57 | public static Uuid uuidToUuid(UUID uuid) { 58 | return Uuid.newBuilder().setValue(uuid.toString()).build(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-graphql/src/main/java/com/killrvideo/graphql/api/KillrvideoMutation.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.graphql.api; 2 | 3 | 4 | import java.util.Date; 5 | import java.util.UUID; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.util.Assert; 10 | 11 | import com.coxautodev.graphql.tools.GraphQLMutationResolver; 12 | import com.killrvideo.dse.dao.CommentDseDao; 13 | import com.killrvideo.dse.model.Comment; 14 | import com.killrvideo.graphql.domain.CommentGQL; 15 | 16 | @Component 17 | public class KillrvideoMutation implements GraphQLMutationResolver { 18 | 19 | @Autowired 20 | private CommentDseDao commentDseDao; 21 | 22 | /** 23 | * Add a comment for dedicated video and known user. 24 | * 25 | * @param videoid 26 | * unique video identifier (required) 27 | * @param userid 28 | * unique user identifier (required) 29 | * @param text 30 | * current comment 31 | * @return 32 | */ 33 | public CommentGQL commentOnVideo(String commentid, String videoid, String userid, String text) { 34 | Assert.hasText(commentid, "Comment identifier is required here"); 35 | Assert.hasText(videoid, "Video identifier is required here"); 36 | Assert.hasText(userid, "User identifier is required here"); 37 | Assert.hasText(text, "Comment text is required here"); 38 | 39 | Comment newComment = new Comment(); 40 | newComment.setCommentid(UUID.fromString(commentid)); 41 | newComment.setVideoid(UUID.fromString(videoid)); 42 | newComment.setUserid(UUID.fromString(userid)); 43 | newComment.setComment(text); 44 | newComment.setDateOfComment(new Date()); 45 | commentDseDao.insertComment(newComment); 46 | return new CommentGQL(newComment); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/main/java/com/killrvideo/grpc/KillrVideoGrpcClient.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.grpc; 2 | 3 | import org.springframework.util.Assert; 4 | 5 | import io.grpc.ManagedChannel; 6 | import io.grpc.ManagedChannelBuilder; 7 | import killrvideo.comments.CommentsServiceGrpc; 8 | import killrvideo.comments.CommentsServiceGrpc.CommentsServiceBlockingStub; 9 | 10 | /** 11 | * As Unit Test or cany consumer you may want USE the runing GRPC API. 12 | * 13 | * @author DataStax Evangelist Team 14 | */ 15 | public class KillrVideoGrpcClient { 16 | 17 | /** Grpc Endpoint */ 18 | private ManagedChannel grpcEndPoint; 19 | 20 | /** Clients for different services in GRPC. */ 21 | public CommentsServiceBlockingStub commentServiceGrpcClient; 22 | 23 | /** 24 | * Connection to GRPC Server. 25 | * 26 | * @param grpcServer 27 | * current grpc hostname 28 | * @param grpcPort 29 | * current grpc portnumber 30 | */ 31 | public KillrVideoGrpcClient(String grpcServer, int grpcPort) { 32 | this(ManagedChannelBuilder.forAddress(grpcServer, grpcPort).usePlaintext(true).build()); 33 | } 34 | 35 | /** 36 | * Extension point for your own GRPC channel. 37 | * 38 | * @param grpcEnpoint 39 | * current GRPC Channe 40 | */ 41 | public KillrVideoGrpcClient(ManagedChannel grpcEnpoint) { 42 | this.grpcEndPoint = grpcEnpoint; 43 | initServiceClients(); 44 | } 45 | 46 | /** 47 | * Init item 48 | */ 49 | public void initServiceClients() { 50 | Assert.notNull(grpcEndPoint, "GrpcEnpoint must be setup"); 51 | commentServiceGrpcClient = CommentsServiceGrpc.newBlockingStub(grpcEndPoint); 52 | } 53 | 54 | /** 55 | * Getter accessor for attribute 'commentServiceGrpcClient'. 56 | * 57 | * @return 58 | * current value of 'commentServiceGrpcClient' 59 | */ 60 | public CommentsServiceBlockingStub getCommentService() { 61 | return commentServiceGrpcClient; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/test/resources/service/6_SearchService.feature: -------------------------------------------------------------------------------- 1 | @video_scenarios 2 | @user_scenarios 3 | @search_scenarios 4 | Feature: Video Search Management 5 | 6 | Users (userXXX) have their id randomized 7 | 8 | Background: 9 | Given those users already exist: user1 10 | 11 | Scenario: Search video by tag 12 | When user1 submit Youtube videos: 13 | | id | name | description | tags | url | 14 | | video1 | b-wing-ucs.mp4 | Lego Star Wars B-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=zcryCEQfTwY | 15 | | video2 | 2017-police-station.mp4 | Lego 2017 Police Staton | lego,police | https://www.youtube.com/watch?v=fpKyfwnkKko | 16 | | video3 | every-breath-you-take.mp4 | The Police - Every breath you take | police,pop,eighties | https://www.youtube.com/watch?v=OMOGaugKpzs | 17 | | video4 | yesterday.mp4 | The Beattles - Yesterday | pop,beattles,top | https://www.youtube.com/watch?v=Ho2e0zvGEWE | 18 | Then searching videos with tag police gives: video2, video3 19 | 20 | 21 | Scenario: Get tags suggestions 22 | When user1 submit Youtube videos: 23 | | id | name | description | tags | url | 24 | | video1 | b-wing-ucs.mp4 | Lego Star Wars B-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=zcryCEQfTwY | 25 | | video2 | 2017-police-station.mp4 | Lego 2017 Police Staton | lego,police | https://www.youtube.com/watch?v=fpKyfwnkKko | 26 | | video3 | every-breath-you-take.mp4 | The Police - Every breath you take | police,pop,eighties | https://www.youtube.com/watch?v=OMOGaugKpzs | 27 | | video4 | yesterday.mp4 | The Beattles - Yesterday | pop,beattles,top | https://www.youtube.com/watch?v=Ho2e0zvGEWE | 28 | Then I should be suggested tags police,pop for the word po 29 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/main/java/com/killrvideo/grpc/GrpcServer.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.grpc; 2 | 3 | import javax.annotation.PostConstruct; 4 | import javax.annotation.PreDestroy; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.stereotype.Component; 11 | 12 | import io.grpc.Server; 13 | import io.grpc.ServerBuilder; 14 | import io.grpc.ServerServiceDefinition; 15 | 16 | /** 17 | * Startup a GRPC server on expected port and register all services. 18 | * 19 | * @author DataStax evangelist team. 20 | */ 21 | @Component 22 | public class GrpcServer { 23 | 24 | /** Some logger. */ 25 | private static final Logger LOGGER = LoggerFactory.getLogger(GrpcServer.class); 26 | 27 | /** Listening Port for GRPC. */ 28 | @Value("${grpc.port: 8899}") 29 | private int grpcPort; 30 | 31 | @Autowired 32 | private CommentsGrpcService commentService; 33 | 34 | /** 35 | * GRPC Server to set up. 36 | */ 37 | private Server server; 38 | 39 | @PostConstruct 40 | public void start() throws Exception { 41 | LOGGER.info("Initializing Grpc Server..."); 42 | 43 | // Binding Services 44 | final ServerServiceDefinition commentService = this.commentService.bindService(); 45 | 46 | // Reference Service in Server 47 | server = ServerBuilder.forPort(grpcPort) 48 | .addService(commentService) 49 | .build(); 50 | 51 | /** 52 | * Declare a shutdown hook otherwise the JVM 53 | * cannot be stop since the Grpc server 54 | * is listening on a port forever 55 | */ 56 | Runtime.getRuntime().addShutdownHook(new Thread() { 57 | public void run() { 58 | LOGGER.info("Calling shutdown for GrpcServer"); 59 | server.shutdown(); 60 | } 61 | }); 62 | 63 | // Start Grpc listener 64 | server.start(); 65 | LOGGER.info("[OK] Grpc Server started on port: '{}'", grpcPort); 66 | } 67 | 68 | @PreDestroy 69 | public void stop() { 70 | server.shutdown(); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/test/resources/service/7_SuggestedVideosService.feature: -------------------------------------------------------------------------------- 1 | @video_scenarios 2 | @user_scenarios 3 | @suggested_videos_scenarios 4 | Feature: Recommendation Engine 5 | 6 | Users (userXXX) have their id randomized 7 | 8 | Background: 9 | Given those users already exist: user1 10 | 11 | Scenario: Get related videos 12 | When user1 submit Youtube videos: 13 | | id | name | description | tags | url | 14 | | video1 | b-wing-ucs.mp4 | Lego Star Wars B-Wing UCS | lego,police,space | https://www.youtube.com/watch?v=zcryCEQfTwY | 15 | | video2 | x-wing-ucs.mp4 | Lego 2017 Police Staton | lego,police | https://www.youtube.com/watch?v=fpKyfwnkKko | 16 | | video3 | tie-fighter-ucs.mp4 | Lego fighter for police | police,pop,eighties | https://www.youtube.com/watch?v=OMOGaugKpzs | 17 | | video4 | yesterday.mp4 | The Beattles - Yesterday | pop,beattles,top | https://www.youtube.com/watch?v=Ho2e0zvGEWE | 18 | | video5 | mars-curiosity.mp4 | The Curious Life of a Mars Rover | space,mars,rover | https://www.youtube.com/watch?v=7zpojhD4hpI | 19 | Then target video2 should have related : video1, video3 20 | 21 | Scenario: Get suggested videos for user 22 | When user1 submit Youtube videos: 23 | | id | name | description | tags | url | 24 | | video1 | b-wing-ucs.mp4 | Lego Star Wars B-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=zcryCEQfTwY | 25 | | video2 | 2017-police-station.mp4 | Lego 2017 Police Staton | lego,police | https://www.youtube.com/watch?v=fpKyfwnkKko | 26 | | video3 | every-breath-you-take.mp4 | The Police - Every breath you take | police,pop,eighties | https://www.youtube.com/watch?v=OMOGaugKpzs | 27 | | video4 | yesterday.mp4 | The Beattles - Yesterday | pop,beattles,top | https://www.youtube.com/watch?v=Ho2e0zvGEWE | 28 | | video5 | mars-curiosity.mp4 | The Curious Life of a Mars Rover | space,mars,rover | https://www.youtube.com/watch?v=7zpojhD4hpI | 29 | Then user1 who likes video2 should be suggested: video1, video3 -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/main/resources/proto/comments/comments_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package killrvideo.comments; 4 | option csharp_namespace = "KillrVideo.Comments"; 5 | 6 | import "google/protobuf/timestamp.proto"; 7 | import "common/common_types.proto"; 8 | 9 | // Manages comments 10 | service CommentsService { 11 | // Add a new comment to a video 12 | rpc CommentOnVideo(CommentOnVideoRequest) returns (CommentOnVideoResponse); 13 | 14 | // Get comments made by a user 15 | rpc GetUserComments(GetUserCommentsRequest) returns (GetUserCommentsResponse); 16 | 17 | // Get comments made on a video 18 | rpc GetVideoComments(GetVideoCommentsRequest) returns (GetVideoCommentsResponse); 19 | } 20 | 21 | // Add a comment to a video 22 | message CommentOnVideoRequest { 23 | killrvideo.common.Uuid video_id = 1; 24 | killrvideo.common.Uuid user_id = 2; 25 | killrvideo.common.TimeUuid comment_id = 3; 26 | string comment = 4; 27 | } 28 | 29 | // Response to adding a comment to a video 30 | message CommentOnVideoResponse { 31 | } 32 | 33 | // Get a page of comments made by a specific user 34 | message GetUserCommentsRequest { 35 | killrvideo.common.Uuid user_id = 1; 36 | int32 page_size = 2; 37 | killrvideo.common.TimeUuid starting_comment_id = 3; 38 | string paging_state = 16; 39 | } 40 | 41 | // Response when getting a page of comments made by a user 42 | message GetUserCommentsResponse { 43 | killrvideo.common.Uuid user_id = 1; 44 | repeated UserComment comments = 2; 45 | string paging_state = 3; 46 | } 47 | 48 | // A comment made by a user 49 | message UserComment { 50 | killrvideo.common.TimeUuid comment_id = 1; 51 | killrvideo.common.Uuid video_id = 2; 52 | string comment = 3; 53 | google.protobuf.Timestamp comment_timestamp = 4; 54 | } 55 | 56 | // Request for getting a page of comments on a video 57 | message GetVideoCommentsRequest { 58 | killrvideo.common.Uuid video_id = 1; 59 | int32 page_size = 2; 60 | killrvideo.common.TimeUuid starting_comment_id = 3; 61 | string paging_state = 16; 62 | } 63 | 64 | // Response when getting a page of comments for a video 65 | message GetVideoCommentsResponse { 66 | killrvideo.common.Uuid video_id = 1; 67 | repeated VideoComment comments = 2; 68 | string paging_state = 3; 69 | } 70 | 71 | // A comment on a video 72 | message VideoComment { 73 | killrvideo.common.TimeUuid comment_id = 1; 74 | killrvideo.common.Uuid user_id = 2; 75 | string comment = 3; 76 | google.protobuf.Timestamp comment_timestamp = 4; 77 | } 78 | -------------------------------------------------------------------------------- /demo-app/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.datastax.astra 6 | demo-app 7 | 0.1.0 8 | + Demo App for ASTRA SDK 9 | Demo project for Astra Spring Boot Starter leveraging the SDk 10 | 11 | 12 | org.springframework.boot 13 | spring-boot-starter-parent 14 | 2.4.2 15 | 16 | 17 | 18 | 19 | 11 20 | 21 | 22 | 23 | 24 | 25 | 26 | com.datastax.astra 27 | astra-spring-boot-starter 28 | 0.1.0 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-data-cassandra-reactive 34 | 35 | 36 | 37 | org.projectlombok 38 | lombok 39 | true 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-test 44 | test 45 | 46 | 47 | org.junit.vintage 48 | junit-vintage-engine 49 | 50 | 51 | 52 | 53 | org.junit.jupiter 54 | junit-jupiter-engine 55 | test 56 | 57 | 58 | org.junit.platform 59 | junit-platform-runner 60 | test 61 | 62 | 63 | 64 | io.projectreactor 65 | reactor-test 66 | test 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-maven-plugin 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /demo-app/pom.xml.versionsBackup: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.datastax.astra 6 | astra-spring-boot-sample-app 7 | 0.0.1-SNAPSHOT 8 | + astra-spring-boot-sample-app 9 | Demo project for Astra Spring Boot Starter leveraging the SDk 10 | 11 | 12 | org.springframework.boot 13 | spring-boot-starter-parent 14 | 2.4.2 15 | 16 | 17 | 18 | 19 | 11 20 | 21 | 22 | 23 | 24 | 25 | 26 | com.datastax.astra 27 | astra-spring-boot-starter 28 | 2021.1-SNAPSHOT 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-data-cassandra-reactive 34 | 35 | 36 | 37 | org.projectlombok 38 | lombok 39 | true 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-test 44 | test 45 | 46 | 47 | org.junit.vintage 48 | junit-vintage-engine 49 | 50 | 51 | 52 | 53 | org.junit.jupiter 54 | junit-jupiter-engine 55 | test 56 | 57 | 58 | org.junit.platform 59 | junit-platform-runner 60 | test 61 | 62 | 63 | 64 | io.projectreactor 65 | reactor-test 66 | test 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-maven-plugin 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-rest/src/main/java/com/killrvideo/rest/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.rest.config; 2 | 3 | /* 4 | * #%L 5 | * ff4j-spring-boot-web-api 6 | * %% 7 | * Copyright (C) 2013 - 2016 FF4J 8 | * %% 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | * #L% 21 | */ 22 | 23 | import org.springframework.context.annotation.Bean; 24 | import org.springframework.context.annotation.Configuration; 25 | 26 | import com.killrvideo.rest.KillrVideoRestApi; 27 | 28 | import springfox.documentation.builders.ApiInfoBuilder; 29 | import springfox.documentation.builders.PathSelectors; 30 | import springfox.documentation.builders.RequestHandlerSelectors; 31 | import springfox.documentation.service.ApiInfo; 32 | import springfox.documentation.spi.DocumentationType; 33 | import springfox.documentation.spring.web.plugins.Docket; 34 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 35 | 36 | /** 37 | * Documentation of the API 38 | * 39 | * @author DataStax Evangelist Team 40 | */ 41 | @Configuration 42 | @EnableSwagger2 43 | public class SwaggerConfig { 44 | 45 | @Bean 46 | public Docket api() { 47 | return new Docket(DocumentationType.SWAGGER_2) 48 | .groupName("KillrVideo") 49 | .select() 50 | .apis(RequestHandlerSelectors.basePackage("com.killrvideo.rest.resource")) 51 | .paths(PathSelectors.regex("/api/v1.*")) 52 | .build() 53 | .apiInfo(apiInfo()) 54 | .useDefaultResponseMessages(false); 55 | } 56 | 57 | /** 58 | * Initialization of documentation 59 | * 60 | * @return static infos 61 | */ 62 | private ApiInfo apiInfo() { 63 | ApiInfoBuilder builder = new ApiInfoBuilder(); 64 | builder.title("KillrVideo REST Api"); 65 | builder.description("CRUD operations against DSE as Backend for killrvideo.com"); 66 | builder.version(KillrVideoRestApi.class.getPackage().getImplementationVersion()); 67 | builder.license("DataStax Academy Tutorials"); 68 | builder.licenseUrl("https://academy.datastax.com/"); 69 | return builder.build(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-rest/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 4.0.0 10 | killrvideo-api-rest 11 | + killrvideo-api-rest 12 | Exposition Rest API on top of killrvideo services 13 | 14 | 15 | 16 | 17 | 18 | com.datastax 19 | killrvideo-parent 20 | 6 21 | 22 | 23 | 24 | 25 | 26 | 27 | 2.8.0 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | com.datastax 37 | killrvideo-dse 38 | ${project.version} 39 | 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-starter-web 45 | 46 | 47 | 48 | 49 | io.springfox 50 | springfox-swagger2 51 | ${swagger.version} 52 | 53 | 54 | io.springfox 55 | springfox-swagger-ui 56 | ${swagger.version} 57 | 58 | 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-devtools 63 | true 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.springframework.boot 73 | spring-boot-maven-plugin 74 | ${spring-boot.version} 75 | 76 | ${start-class} 77 | 78 | 79 | 80 | 81 | repackage 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-rest/src/main/java/com/killrvideo/rest/resource/VideoResource.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.rest.resource; 2 | 3 | import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; 4 | import static org.springframework.web.bind.annotation.RequestMethod.GET; 5 | 6 | import java.util.Optional; 7 | import java.util.UUID; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RequestParam; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | import com.killrvideo.dse.dao.CommentDseDao; 18 | import com.killrvideo.dse.dao.dto.QueryCommentByVideo; 19 | import com.killrvideo.dse.dao.dto.ResultListPage; 20 | import com.killrvideo.dse.model.Comment; 21 | 22 | import io.swagger.annotations.ApiOperation; 23 | import io.swagger.annotations.ApiParam; 24 | import io.swagger.annotations.ApiResponse; 25 | import io.swagger.annotations.ApiResponses; 26 | 27 | @RestController 28 | @RequestMapping("/api/v1/videos/{videouuid}") 29 | public class VideoResource { 30 | 31 | /** Logger for that class. */ 32 | private static Logger LOGGER = LoggerFactory.getLogger(VideoResource.class); 33 | 34 | @Autowired 35 | private CommentDseDao commentDao; 36 | 37 | @RequestMapping(value = "/comments", method = GET, produces = APPLICATION_JSON_VALUE) 38 | @ApiOperation(value = "List comment for a specified video", response = ResultListPage.class) 39 | @ApiResponses(@ApiResponse(code = 200, message = "Retrieve comments for a dedicated video")) 40 | public ResultListPage< Comment > getComments( 41 | @ApiParam(name="videouuid", value="Unique identifier for a video", required=true ) 42 | @PathVariable(value = "videouuid") String videouuid, 43 | @ApiParam(name="pageSize", value="Requested page size, default is 10", required=false ) 44 | @RequestParam("pageSize") Optional pageSize, 45 | @ApiParam(name="pageState", value="Use to retrieve next pages", required=false ) 46 | @RequestParam("pageState") Optional pageState, 47 | @ApiParam(name="startviduuid", value="starting point for next page", required=false ) 48 | @RequestParam("startviduuid") Optional commentuuid) { 49 | 50 | QueryCommentByVideo qcbv = new QueryCommentByVideo(); 51 | qcbv.setVideoId(UUID.fromString(videouuid)); 52 | qcbv.setPageState(pageState); 53 | pageSize.ifPresent(qcbv::setPageSize); 54 | if (commentuuid.isPresent()) { 55 | qcbv.setCommentId(commentuuid.map(UUID::fromString)); 56 | } 57 | 58 | LOGGER.info("Retrieving comments for a video."); 59 | return commentDao.findCommentsByVideoId(qcbv); 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /demo-app/src/test/java/com/datastax/astra/Test01_CreateAstraInstance.java: -------------------------------------------------------------------------------- 1 | package com.datastax.astra; 2 | 3 | import static com.datastax.astra.DemoDataSet.appToken; 4 | import static com.datastax.astra.DemoDataSet.aws; 5 | import static com.datastax.astra.DemoDataSet.awsEast1; 6 | import static com.datastax.astra.DemoDataSet.clientId; 7 | import static com.datastax.astra.DemoDataSet.clientSecret; 8 | import static com.datastax.astra.DemoDataSet.dbName; 9 | import static com.datastax.astra.DemoDataSet.ksName; 10 | 11 | import org.junit.Assert; 12 | import org.junit.jupiter.api.Test; 13 | 14 | import com.dstx.astra.sdk.AstraClient; 15 | import com.dstx.astra.sdk.devops.DatabaseStatusType; 16 | import com.dstx.astra.sdk.devops.DatabaseTierType; 17 | import com.dstx.astra.sdk.devops.req.DatabaseCreationRequest; 18 | /** 19 | * Creating 20 | * @author Cedrick LUNVEN (@clunven) 21 | */ 22 | public class Test01_CreateAstraInstance { 23 | 24 | public static final String ANSI_RESET = "\u001B[0m"; 25 | public static final String ANSI_GREEN = "\u001B[32m"; 26 | 27 | @Test 28 | public void createInstance() throws InterruptedException { 29 | /* 30 | String databaseId = AstraClient.builder() 31 | .appToken(appToken) 32 | .clientId(clientId) 33 | .clientSecret(clientSecret) 34 | .build().apiDevops() 35 | .createDatabase(DatabaseCreationRequest.builder() 36 | .name(dbName) 37 | .tier(DatabaseTierType.serverless) 38 | .cloudProvider(aws).cloudRegion(awsEast1) 39 | .keyspace(ksName) 40 | .build()); 41 | */ 42 | 43 | AstraClient astraClient = AstraClient.builder() 44 | .appToken(appToken) 45 | .clientId(clientId) 46 | .clientSecret(clientSecret) 47 | .build(); 48 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Astra Client Set"); 49 | 50 | String id = astraClient.apiDevops().createDatabase(DatabaseCreationRequest.builder() 51 | .name(dbName) 52 | .tier(DatabaseTierType.serverless) 53 | .cloudProvider(aws).cloudRegion(awsEast1) 54 | .keyspace(ksName) 55 | .build()); 56 | 57 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Database [" + dbName + "] id=" + id); 58 | System.out.print(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Initializing "); 59 | while(!DatabaseStatusType.ACTIVE.equals(astraClient.apiDevops().findDatabaseById(id).get().getStatus())) { 60 | System.out.print(ANSI_GREEN + "\u25a0" +ANSI_RESET); 61 | Thread.sleep(5000); 62 | } 63 | Assert.assertEquals(DatabaseStatusType.ACTIVE, astraClient.apiDevops().findDatabaseById(id).get().getStatus()); 64 | System.out.println(ANSI_GREEN + "\n[OK]" + ANSI_RESET + " - DB is active"); 65 | } 66 | 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-rest/src/main/java/com/killrvideo/rest/resource/UserResource.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.rest.resource; 2 | 3 | import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; 4 | import static org.springframework.web.bind.annotation.RequestMethod.GET; 5 | 6 | import java.util.Optional; 7 | import java.util.UUID; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RequestParam; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | import com.killrvideo.dse.dao.CommentDseDao; 18 | import com.killrvideo.dse.dao.dto.QueryCommentByUser; 19 | import com.killrvideo.dse.dao.dto.ResultListPage; 20 | import com.killrvideo.dse.model.Comment; 21 | 22 | import io.swagger.annotations.Api; 23 | import io.swagger.annotations.ApiOperation; 24 | import io.swagger.annotations.ApiParam; 25 | import io.swagger.annotations.ApiResponse; 26 | import io.swagger.annotations.ApiResponses; 27 | 28 | @RestController 29 | @RequestMapping("/api/v1/users/{useruuid}") 30 | @Api(value = "Resource User", description = "Operation on single User") 31 | public class UserResource { 32 | 33 | /** Logger for that class. */ 34 | private static Logger LOGGER = LoggerFactory.getLogger(UserResource.class); 35 | 36 | @Autowired 37 | private CommentDseDao commentDao; 38 | 39 | @RequestMapping(value = "/comments", method = GET, produces = APPLICATION_JSON_VALUE) 40 | @ApiOperation(value = "List comment for a specified user", response = ResultListPage.class) 41 | @ApiResponses( 42 | @ApiResponse(code = 200, message = "Retrieve comments for a dedicated user") 43 | ) 44 | public ResultListPage< Comment > getComments( 45 | @ApiParam(name="useruuid", value="Unique identifier for a user", required=true ) 46 | @PathVariable(value = "useruuid") String useruuid, 47 | @ApiParam(name="pageSize", value="Requested page size, default is 10", required=false ) 48 | @RequestParam("pageSize") Optional pageSize, 49 | @ApiParam(name="pageState", value="Use to retrieve next pages", required=false ) 50 | @RequestParam("pageState") Optional pageState, 51 | @ApiParam(name="commentuuid", value="starting point for next page", required=false ) 52 | @RequestParam("commentuuid") Optional commentuuid) { 53 | 54 | // Parsing Result 55 | QueryCommentByUser qcbu = new QueryCommentByUser(); 56 | qcbu.setUserId(UUID.fromString(useruuid)); 57 | qcbu.setPageState(pageState); 58 | pageSize.ifPresent(qcbu::setPageSize); 59 | if (commentuuid.isPresent()) { 60 | qcbu.setCommentId(commentuuid.map(UUID::fromString)); 61 | } 62 | 63 | // Async DAO invocation 64 | LOGGER.info("Retrieving comments for a user "); 65 | return commentDao.findCommentsByUserId(qcbu); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/test/resources/service/3_CommentsService.feature: -------------------------------------------------------------------------------- 1 | @video_scenarios 2 | @user_scenarios 3 | @comments_scenarios 4 | Feature: Comments Management 5 | 6 | Users (userXXX) have their id randomized. 7 | 8 | Background: 9 | Given those users already exist: user1, user2 10 | And user1 submit Youtube videos: 11 | | id | name | description | tags | url | 12 | | video1 | b-wing-ucs.mp4 | Lego Star Wars B-Wing UCS | lego,star wars,space| https://www.youtube.com/watch?v=zcryCEQfTwY | 13 | And user2 submit Youtube videos: 14 | | id | name | description | tags | url | 15 | | video2 | y-wing-ucs.mp4 | Lego Star Wars Y-Wing UCS | lego,star wars,space| https://www.youtube.com/watch?v=TRyja74EXp0 | 16 | 17 | 18 | Scenario: Creating a comment on a video and see it on video comments page 19 | #video1 is created by user1 20 | When we have the following comments: 21 | | user | video | comment | 22 | | user1 | video1 | This Y-Wing is awesome! | 23 | Then I can see the comment 'This Y-Wing is awesome!' on video1 24 | 25 | 26 | Scenario: Creating a comment on a video and see it on user comments page 27 | #video1 is created by user1 28 | When we have the following comments: 29 | | user | video | comment | 30 | | user1 | video1 | This Y-Wing is awesome! | 31 | Then user1 can see the comment 'This Y-Wing is awesome!' on his own comments 32 | 33 | 34 | Scenario: Creating a comment on a video and see it with paging on video comments 35 | #video2 is created by user2. 36 | #To test paging, we hard-coded page size == 1 so that it is easier to reason about. 37 | #For ex: 2nd comment = 2nd page, 3rd comment = 3rd page etc ... 38 | When we have the following comments: 39 | | user | video | comment | 40 | | user2 | video2 | This is my own B-Wing UCS. Cost me a lot of bucks | 41 | | user1 | video2 | This B-Wing is really big | 42 | | user2 | video2 | Almost 60cm long! | 43 | | user1 | video2 | Love the design of the cockpit | 44 | Then I can see the comment 'Love the design of the cockpit' on video2 at page 4 45 | 46 | 47 | Scenario: Creating a comment on a video and see it with paging on user comments 48 | #video2 is created by user2. 49 | #To test paging, we hard-coded page size == 1 so that it is easier to reason about. 50 | #For ex: 2nd comment = 2nd page, 3rd comment = 3rd page etc ... 51 | When we have the following comments: 52 | | user | video | comment | 53 | | user2 | video2 | This is my own B-Wing UCS. Cost me a lot of bucks | 54 | | user1 | video2 | This B-Wing is really big | 55 | | user2 | video2 | Almost 60cm long! | 56 | | user1 | video2 | Love the design of the cockpit | 57 | Then user2 can see the comment 'Almost 60cm long!' on his own comments at page 2 -------------------------------------------------------------------------------- /sample-code/killrvideo-api-rest/src/main/java/com/killrvideo/rest/resource/CommentResource.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.rest.resource; 2 | 3 | import static java.lang.Boolean.TRUE; 4 | import static org.springframework.http.HttpStatus.NO_CONTENT; 5 | import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; 6 | import static org.springframework.web.bind.annotation.RequestMethod.DELETE; 7 | import static org.springframework.web.bind.annotation.RequestMethod.PUT; 8 | 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.RequestBody; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | import com.killrvideo.dse.dao.CommentDseDao; 18 | import com.killrvideo.dse.model.Comment; 19 | 20 | import io.swagger.annotations.Api; 21 | import io.swagger.annotations.ApiOperation; 22 | import io.swagger.annotations.ApiResponse; 23 | import io.swagger.annotations.ApiResponses; 24 | 25 | /** 26 | * Exposition as REST API. 27 | * 28 | * @author DataStax Evangelist Team 29 | */ 30 | @RestController 31 | @Api(value = "/api/v1/comments/{commentuuid}", description = "CRUD operations working on comments") 32 | @RequestMapping("/api/v1/comments/{commentuuid}") 33 | public class CommentResource { 34 | 35 | @Autowired 36 | private CommentDseDao commentDao; 37 | 38 | @RequestMapping(method = PUT, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE) 39 | @ApiOperation(value = "Create or update a comment based on a given UUID", response = ResponseEntity.class) 40 | @ApiResponses({ 41 | @ApiResponse(code = 400, message = "Comment uuid is blank (or), fields are not correctly filled"), 42 | @ApiResponse(code = 201, message = "Comment has been created"), 43 | @ApiResponse(code = 204, message = "No content, no changes made to the comment")}) 44 | public ResponseEntity createOrUpdateFeature(@PathVariable(value = "commentuuid") String commentUuid, @RequestBody Comment comment) { 45 | commentDao.insertComment(comment); 46 | return new ResponseEntity(TRUE, HttpStatus.CREATED); 47 | } 48 | 49 | @SuppressWarnings("rawtypes") 50 | @RequestMapping(method = DELETE) 51 | @ApiOperation(value = "Delete a comment", response = ResponseEntity.class) 52 | @ApiResponses({ 53 | @ApiResponse(code = 204, message = "No content, comment is deleted"), 54 | @ApiResponse(code = 404, message = "Comment not found") 55 | }) 56 | public ResponseEntity deleteFeature(@PathVariable(value = "commentuuid") String commentUUid, @RequestBody Comment comment) { 57 | commentDao.deleteComment(comment); 58 | return new ResponseEntity(NO_CONTENT); 59 | } 60 | 61 | /** 62 | * Getter accessor for attribute 'commentDao'. 63 | * 64 | * @return 65 | * current value of 'commentDao' 66 | */ 67 | public CommentDseDao getCommentDao() { 68 | return commentDao; 69 | } 70 | 71 | /** 72 | * Setter accessor for attribute 'commentDao'. 73 | * @param commentDao 74 | * new value for 'commentDao ' 75 | */ 76 | public void setCommentDao(CommentDseDao commentDao) { 77 | this.commentDao = commentDao; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /demo-app/src/test/java/com/datastax/astra/Test03_WorkingWithRestApi.java: -------------------------------------------------------------------------------- 1 | package com.datastax.astra; 2 | 3 | import static com.datastax.astra.DemoDataSet.appToken; 4 | import static com.datastax.astra.DemoDataSet.awsEast1; 5 | import static com.datastax.astra.DemoDataSet.clientId; 6 | import static com.datastax.astra.DemoDataSet.clientSecret; 7 | import static com.datastax.astra.DemoDataSet.ksName; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | import org.junit.jupiter.api.BeforeAll; 13 | import org.junit.jupiter.api.Test; 14 | 15 | import com.dstx.astra.sdk.AstraClient; 16 | 17 | import io.stargate.sdk.rest.domain.CreateTable; 18 | import io.stargate.sdk.rest.domain.Ordering; 19 | import io.stargate.sdk.rest.domain.SearchTableQuery; 20 | 21 | public class Test03_WorkingWithRestApi { 22 | 23 | public static final String ANSI_RESET = "\u001B[0m"; 24 | public static final String ANSI_GREEN = "\u001B[32m"; 25 | 26 | private static AstraClient astraClient; 27 | 28 | // 29 | private static String databaseId = "cfa040d4-cfd4-489f-9dd2-352cb01801f1"; 30 | 31 | @BeforeAll 32 | public static void init() { 33 | astraClient = AstraClient.builder() 34 | .appToken(appToken).clientId(clientId).clientSecret(clientSecret) 35 | .databaseId(databaseId).cloudProviderRegion(awsEast1) 36 | .keyspace(ksName) 37 | .build(); 38 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Astra Client Set"); 39 | } 40 | 41 | 42 | @Test 43 | public void listKeyspaces() { 44 | astraClient.apiRest().keyspaceNames() 45 | .forEach(System.out::println); 46 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Keyspaces listed"); 47 | } 48 | 49 | @Test 50 | public void createKeyspace() { 51 | 52 | astraClient.apiDevops().createKeyspace(databaseId, "keyspace2"); 53 | 54 | 55 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Keyspace created"); 56 | } 57 | 58 | 59 | @Test 60 | public void createTable() { 61 | astraClient.apiRest().keyspace("keyspace2").table("users").create( 62 | CreateTable.builder() 63 | .addPartitionKey("firstname", "text") 64 | .addClusteringKey("lastname", "text", Ordering.ASC) 65 | .addColumn("email", "text") 66 | .addColumn("color", "text") 67 | .ifNotExist(true) 68 | .build()); 69 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Table created"); 70 | } 71 | 72 | @Test 73 | public void insertRow() { 74 | Map record = new HashMap<>(); 75 | record.put("firstname", "Mookie"); 76 | record.put("lastname", "Betts"); 77 | record.put("email", "mookie.betts@gmail.com"); 78 | record.put("color", "blue"); 79 | astraClient.apiRest().keyspace("keyspace2").table("users").upsert(record); 80 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Row Inserted"); 81 | } 82 | 83 | @Test 84 | public void readData() { 85 | astraClient.apiRest().keyspace("keyspace2") 86 | .table("users").search(SearchTableQuery.builder().build()) 87 | .getResults().stream().forEach(System.out::println); 88 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - List records"); 89 | } 90 | 91 | 92 | 93 | } 94 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-graphql/src/main/java/com/killrvideo/graphql/api/KillrvideoQuery.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.graphql.api; 2 | 3 | import java.util.Optional; 4 | import java.util.UUID; 5 | import java.util.stream.Collectors; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.coxautodev.graphql.tools.GraphQLQueryResolver; 11 | import com.killrvideo.dse.dao.CommentDseDao; 12 | import com.killrvideo.dse.dao.dto.QueryCommentByUser; 13 | import com.killrvideo.dse.dao.dto.QueryCommentByVideo; 14 | import com.killrvideo.dse.dao.dto.ResultListPage; 15 | import com.killrvideo.dse.model.Comment; 16 | import com.killrvideo.graphql.domain.CommentGQL; 17 | import com.killrvideo.graphql.domain.ResultPageCommentGQL; 18 | 19 | /** 20 | * Read only queries GraphQL query resolver. 21 | */ 22 | @Component 23 | public class KillrvideoQuery implements GraphQLQueryResolver { 24 | 25 | @Autowired 26 | private CommentDseDao commentDseDao; 27 | 28 | /** 29 | * Retrieve comment for a dedicated VIDEOS. 30 | * 31 | * @param videoid 32 | * video id 33 | * @param commentid 34 | * comment id 35 | * @param pageSize 36 | * page size 37 | * @param pageState 38 | * state for other pages. 39 | * @return 40 | */ 41 | public ResultPageCommentGQL getVideoComments (String videoid, String commentid, int pageSize, String pageState) { 42 | 43 | // Mapping GraphQl => DAO 44 | QueryCommentByVideo qcbv = new QueryCommentByVideo(); 45 | qcbv.setVideoId(UUID.fromString(videoid)); 46 | qcbv.setPageSize(pageSize); 47 | if (pageState != null) { 48 | qcbv.setPageState(Optional.of(pageState)); 49 | } 50 | 51 | // Invoke DAO 52 | ResultListPage resultComments = commentDseDao.findCommentsByVideoId(qcbv); 53 | 54 | // DTO => GraphQL beans 55 | ResultPageCommentGQL result = new ResultPageCommentGQL(); 56 | resultComments.getPagingState().ifPresent(result::setNextPage); 57 | result.setListOfResults( 58 | resultComments.getResults().stream() 59 | .map(CommentGQL::new) 60 | .collect(Collectors.toList())); 61 | return result; 62 | } 63 | 64 | /** 65 | * Retrieve comment for a dedicated USER. 66 | * 67 | * @param videoid 68 | * video id 69 | * @param commentid 70 | * comment id 71 | * @param pageSize 72 | * page size 73 | * @param pageState 74 | * state for other pages. 75 | * @return 76 | */ 77 | public ResultPageCommentGQL getUserComments (String userid, String commentid, int pageSize, String pageState) { 78 | // Mapping GraphQl => DAO 79 | QueryCommentByUser qcbu = new QueryCommentByUser(); 80 | qcbu.setUserId(UUID.fromString(userid)); 81 | qcbu.setPageSize(pageSize); 82 | if (pageState != null) { 83 | qcbu.setPageState(Optional.of(pageState)); 84 | } 85 | 86 | // Invoke DAO 87 | ResultListPage resultComments = commentDseDao.findCommentsByUserId(qcbu); 88 | 89 | // DTO => GraphQL beans 90 | ResultPageCommentGQL result = new ResultPageCommentGQL(); 91 | resultComments.getPagingState().ifPresent(result::setNextPage); 92 | result.setListOfResults( 93 | resultComments.getResults().stream() 94 | .map(CommentGQL::new) 95 | .collect(Collectors.toList())); 96 | return result; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /demo-app/src/test/java/com/datastax/astra/Test04_WorkingWithDocumentApi.java: -------------------------------------------------------------------------------- 1 | package com.datastax.astra; 2 | 3 | import static com.datastax.astra.DemoDataSet.appToken; 4 | import static com.datastax.astra.DemoDataSet.awsEast1; 5 | import static com.datastax.astra.DemoDataSet.clientId; 6 | import static com.datastax.astra.DemoDataSet.clientSecret; 7 | import static com.datastax.astra.DemoDataSet.ksName; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.UUID; 12 | 13 | import org.junit.jupiter.api.BeforeAll; 14 | import org.junit.jupiter.api.Test; 15 | 16 | import com.dstx.astra.sdk.AstraClient; 17 | 18 | import io.stargate.sdk.doc.ApiDocument; 19 | 20 | public class Test04_WorkingWithDocumentApi { 21 | 22 | public static final String ANSI_RESET = "\u001B[0m"; 23 | public static final String ANSI_GREEN = "\u001B[32m"; 24 | 25 | private static AstraClient astraClient; 26 | 27 | // 28 | private static String databaseId = "cfa040d4-cfd4-489f-9dd2-352cb01801f1"; 29 | 30 | @BeforeAll 31 | public static void init() { 32 | astraClient = AstraClient.builder() 33 | .appToken(appToken).clientId(clientId).clientSecret(clientSecret) 34 | .databaseId(databaseId).cloudProviderRegion(awsEast1) 35 | .keyspace(ksName) 36 | .build(); 37 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Astra Client Set"); 38 | } 39 | 40 | @Test 41 | public void createNamespace() { 42 | astraClient.apiDevops().createNamespace(databaseId, "namespace1"); 43 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Namespace created"); 44 | } 45 | 46 | @Test 47 | public void listNamespaces() { 48 | astraClient.apiDocument().namespaceNames() 49 | .forEach(System.out::println); 50 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Namespace listed"); 51 | } 52 | 53 | @Test 54 | public void createCollection() { 55 | astraClient.apiDocument().namespace("namespace1").collection("videos").create(); 56 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Collection created"); 57 | } 58 | 59 | @Test 60 | public void createDocument() { 61 | Video video = new Video(); 62 | video.setVideoid(UUID.fromString("e466f561-4ea4-4eb7-8dcc-126e0fbfd573")); 63 | video.setEmail("clunven@sample.com"); 64 | video.setUpload("2020-02-26 15:09:22 +00:00"); 65 | video.setUrl("http://google.fr"); 66 | video.getFrames().add(1); 67 | video.getFrames().add(2); 68 | video.getFrames().add(3); 69 | video.getFrames().add(4); 70 | video.getTags().add("cassandra"); 71 | video.getTags().add("accelerate"); 72 | video.getTags().add("2020"); 73 | Map map1 = new HashMap<>(); 74 | map1.put("width", 1); 75 | map1.put("height", 1); 76 | video.getFormats().put("mp4", map1); 77 | video.getFormats().put("ogg", map1); 78 | 79 | String docId = astraClient.apiDocument() 80 | .namespace("namespace1").collection("videos") 81 | .createNewDocument(video); 82 | 83 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Video Inserted id=" + docId); 84 | } 85 | 86 | @Test 87 | public void retrieveDocuments() { 88 | astraClient.apiDocument() 89 | .namespace("namespace1") 90 | .collection("videos") 91 | .findAll(Video.class) 92 | .getResults() 93 | .stream() 94 | .map(ApiDocument::getDocument) 95 | .map(Video::toString) 96 | .forEach(System.out::println); 97 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - List document"); 98 | } 99 | 100 | 101 | 102 | } 103 | -------------------------------------------------------------------------------- /demo-app/src/test/java/com/datastax/astra/Video.java: -------------------------------------------------------------------------------- 1 | package com.datastax.astra; 2 | 3 | import java.io.Serializable; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.UUID; 9 | 10 | 11 | public class Video implements Serializable { 12 | 13 | /** Serial.*/ 14 | private static final long serialVersionUID = 835129431475080845L; 15 | 16 | private UUID videoid; 17 | private String email; 18 | private String upload; 19 | private String url; 20 | private List frames = new ArrayList<>(); 21 | private List tags= new ArrayList<>(); 22 | private Map > formats= new HashMap<>(); 23 | 24 | public Video() {} 25 | /** 26 | * Getter accessor for attribute 'videoid'. 27 | * 28 | * @return 29 | * current value of 'videoid' 30 | */ 31 | public UUID getVideoid() { 32 | return videoid; 33 | } 34 | /** 35 | * Setter accessor for attribute 'videoid'. 36 | * @param videoid 37 | * new value for 'videoid ' 38 | */ 39 | public void setVideoid(UUID videoid) { 40 | this.videoid = videoid; 41 | } 42 | /** 43 | * Getter accessor for attribute 'email'. 44 | * 45 | * @return 46 | * current value of 'email' 47 | */ 48 | public String getEmail() { 49 | return email; 50 | } 51 | /** 52 | * Setter accessor for attribute 'email'. 53 | * @param email 54 | * new value for 'email ' 55 | */ 56 | public void setEmail(String email) { 57 | this.email = email; 58 | } 59 | /** 60 | * Getter accessor for attribute 'upload'. 61 | * 62 | * @return 63 | * current value of 'upload' 64 | */ 65 | public String getUpload() { 66 | return upload; 67 | } 68 | /** 69 | * Setter accessor for attribute 'upload'. 70 | * @param upload 71 | * new value for 'upload ' 72 | */ 73 | public void setUpload(String upload) { 74 | this.upload = upload; 75 | } 76 | /** 77 | * Getter accessor for attribute 'url'. 78 | * 79 | * @return 80 | * current value of 'url' 81 | */ 82 | public String getUrl() { 83 | return url; 84 | } 85 | /** 86 | * Setter accessor for attribute 'url'. 87 | * @param url 88 | * new value for 'url ' 89 | */ 90 | public void setUrl(String url) { 91 | this.url = url; 92 | } 93 | /** 94 | * Getter accessor for attribute 'frames'. 95 | * 96 | * @return 97 | * current value of 'frames' 98 | */ 99 | public List getFrames() { 100 | return frames; 101 | } 102 | /** 103 | * Setter accessor for attribute 'frames'. 104 | * @param frames 105 | * new value for 'frames ' 106 | */ 107 | public void setFrames(List frames) { 108 | this.frames = frames; 109 | } 110 | /** 111 | * Getter accessor for attribute 'tags'. 112 | * 113 | * @return 114 | * current value of 'tags' 115 | */ 116 | public List getTags() { 117 | return tags; 118 | } 119 | /** 120 | * Setter accessor for attribute 'tags'. 121 | * @param tags 122 | * new value for 'tags ' 123 | */ 124 | public void setTags(List tags) { 125 | this.tags = tags; 126 | } 127 | /** 128 | * Getter accessor for attribute 'formats'. 129 | * 130 | * @return 131 | * current value of 'formats' 132 | */ 133 | public Map> getFormats() { 134 | return formats; 135 | } 136 | /** 137 | * Setter accessor for attribute 'formats'. 138 | * @param formats 139 | * new value for 'formats ' 140 | */ 141 | public void setFormats(Map> formats) { 142 | this.formats = formats; 143 | } 144 | /** {@inheritDoc} */ 145 | @Override 146 | public String toString() { 147 | return "Video [videoid=" + videoid + ", email=" + email + ", upload=" + upload + ", url=" + url + ", frames=" + frames 148 | + ", tags=" + tags + ", formats=" + formats + "]"; 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/main/java/com/killrvideo/grpc/utils/AbstractGrpcHelper.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.grpc.utils; 2 | 3 | import java.time.Duration; 4 | import java.time.Instant; 5 | 6 | import org.slf4j.Logger; 7 | 8 | import io.grpc.Status; 9 | import io.grpc.stub.StreamObserver; 10 | 11 | /** 12 | * Mutualization in validator code. 13 | * 14 | * @author DataStax evangelist team. 15 | */ 16 | public abstract class AbstractGrpcHelper { 17 | 18 | /** 19 | * Enable default constructor. 20 | */ 21 | public AbstractGrpcHelper() {} 22 | 23 | /** 24 | * Init error builder. 25 | * 26 | * @param request 27 | * current request 28 | * @return 29 | * current error message 30 | */ 31 | protected StringBuilder initErrorString(Object request) { 32 | return new StringBuilder("Validation error for '" + request.toString() + "' : \n"); 33 | } 34 | 35 | /** 36 | * Deduplicate condition evaluation. 37 | * 38 | * @param assertion 39 | * current condition 40 | * @param fieldName 41 | * fieldName to evaluate 42 | * @param request 43 | * GRPC reauest 44 | * @param errorMessage 45 | * concatenation of error messages 46 | * @return 47 | */ 48 | protected boolean notEmpty(boolean assertion, String fieldName, String request, StringBuilder errorMessage) { 49 | if (assertion) { 50 | errorMessage.append("\t\t"); 51 | errorMessage.append(fieldName); 52 | errorMessage.append("should be provided for comment on "); 53 | errorMessage.append(request); 54 | errorMessage.append("\n"); 55 | } 56 | return !assertion; 57 | } 58 | 59 | /** 60 | * Add error message if assertion is violated. 61 | * 62 | * @param assertion 63 | * current assertion 64 | * @param fieldName 65 | * current field name 66 | * @param request 67 | * current request 68 | * @param errorMessage 69 | * current error message 70 | * @return 71 | * if the correction is OK. 72 | */ 73 | protected boolean positive(boolean assertion, String fieldName, String request, StringBuilder errorMessage) { 74 | if (assertion) { 75 | errorMessage.append("\t\t"); 76 | errorMessage.append(fieldName); 77 | errorMessage.append("should be strictly positive for "); 78 | errorMessage.append(request); 79 | errorMessage.append("\n"); 80 | } 81 | return !assertion; 82 | } 83 | 84 | /** 85 | * Utility to validate Grpc Input. 86 | * 87 | * @param streamObserver 88 | * grpc observer 89 | * @param errorMessage 90 | * error mressage 91 | * @param isValid 92 | * validation of that 93 | * @return 94 | * ok 95 | */ 96 | protected boolean validate(Logger logger, StreamObserver streamObserver, StringBuilder errorMessage, boolean isValid) { 97 | if (isValid) { 98 | return true; 99 | } else { 100 | final String description = errorMessage.toString(); 101 | logger.error(description); 102 | streamObserver.onError(Status.INVALID_ARGUMENT.withDescription(description).asRuntimeException()); 103 | streamObserver.onCompleted(); 104 | return false; 105 | } 106 | } 107 | 108 | /** 109 | * Utility to TRACE. 110 | * 111 | * @param method 112 | * current operation 113 | * @param start 114 | * timestamp for starting 115 | */ 116 | public void traceSuccess(Logger logger, String method, Instant starts) { 117 | if (logger.isDebugEnabled()) { 118 | logger.debug("End successfully '{}' in {} millis", method, Duration.between(starts, Instant.now()).getNano()/1000); 119 | } 120 | } 121 | 122 | /** 123 | * Utility to TRACE. 124 | * 125 | * @param method 126 | * current operation 127 | * @param start 128 | * timestamp for starting 129 | */ 130 | public void traceError(Logger logger, String method, Instant starts, Throwable t) { 131 | logger.error("An error occured in {} after {}", method, Duration.between(starts, Instant.now()), t); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-graphql/src/main/java/com/killrvideo/graphql/domain/CommentGQL.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.graphql.domain; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | import java.util.UUID; 6 | 7 | import com.killrvideo.dse.model.Comment; 8 | 9 | /** 10 | * In KillrVideo each video holds a list of comments. 11 | */ 12 | public class CommentGQL implements Serializable { 13 | 14 | /** 15 | * Serial. 16 | */ 17 | private static final long serialVersionUID = -4032110812123661790L; 18 | 19 | /** 20 | * Unique identifier for a user (tech id) 21 | */ 22 | protected String userid; 23 | 24 | /** 25 | * Unique identifier for a video (tech id) 26 | */ 27 | protected String videoid; 28 | 29 | /** 30 | * Text of the comment 31 | */ 32 | protected String comment; 33 | 34 | /** 35 | * Unique identifier for a comment, generated by the system (tech id) 36 | */ 37 | protected String commentid; 38 | 39 | /** 40 | * Insertion date of the comment 41 | */ 42 | private Date dateOfComment; 43 | 44 | 45 | public CommentGQL() { 46 | } 47 | 48 | public CommentGQL(Comment dto) { 49 | this.comment = dto.getComment(); 50 | this.commentid = dto.getCommentid().toString(); 51 | this.userid = dto.getUserid().toString(); 52 | this.videoid = dto.getVideoid().toString(); 53 | this.dateOfComment = dto.getDateOfComment(); 54 | } 55 | 56 | public Comment toCommentDto() { 57 | Comment c = new Comment(); 58 | c.setComment(this.comment); 59 | c.setDateOfComment(this.dateOfComment); 60 | c.setCommentid(UUID.fromString(this.getCommentid())); 61 | c.setVideoid(UUID.fromString(this.getVideoid())); 62 | c.setUserid(UUID.fromString(this.getUserid())); 63 | return c; 64 | } 65 | 66 | /** 67 | * Getter accessor for attribute 'userid'. 68 | * 69 | * @return 70 | * current value of 'userid' 71 | */ 72 | public String getUserid() { 73 | return userid; 74 | } 75 | 76 | /** 77 | * Setter accessor for attribute 'userid'. 78 | * @param userid 79 | * new value for 'userid ' 80 | */ 81 | public void setUserid(String userid) { 82 | this.userid = userid; 83 | } 84 | 85 | /** 86 | * Getter accessor for attribute 'videoid'. 87 | * 88 | * @return 89 | * current value of 'videoid' 90 | */ 91 | public String getVideoid() { 92 | return videoid; 93 | } 94 | 95 | /** 96 | * Setter accessor for attribute 'videoid'. 97 | * @param videoid 98 | * new value for 'videoid ' 99 | */ 100 | public void setVideoid(String videoid) { 101 | this.videoid = videoid; 102 | } 103 | 104 | /** 105 | * Getter accessor for attribute 'comment'. 106 | * 107 | * @return 108 | * current value of 'comment' 109 | */ 110 | public String getComment() { 111 | return comment; 112 | } 113 | 114 | /** 115 | * Setter accessor for attribute 'comment'. 116 | * @param comment 117 | * new value for 'comment ' 118 | */ 119 | public void setComment(String comment) { 120 | this.comment = comment; 121 | } 122 | 123 | /** 124 | * Getter accessor for attribute 'commentid'. 125 | * 126 | * @return 127 | * current value of 'commentid' 128 | */ 129 | public String getCommentid() { 130 | return commentid; 131 | } 132 | 133 | /** 134 | * Setter accessor for attribute 'commentid'. 135 | * @param commentid 136 | * new value for 'commentid ' 137 | */ 138 | public void setCommentid(String commentid) { 139 | this.commentid = commentid; 140 | } 141 | 142 | /** 143 | * Getter accessor for attribute 'dateOfComment'. 144 | * 145 | * @return 146 | * current value of 'dateOfComment' 147 | */ 148 | public Date getDateOfComment() { 149 | return dateOfComment; 150 | } 151 | 152 | /** 153 | * Setter accessor for attribute 'dateOfComment'. 154 | * @param dateOfComment 155 | * new value for 'dateOfComment ' 156 | */ 157 | public void setDateOfComment(Date dateOfComment) { 158 | this.dateOfComment = dateOfComment; 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-graphql/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 4.0.0 10 | killrvideo-api-graphql 11 | + killrvideo-api-graphql 12 | Exposition Rest API on top of killrvideo services 13 | 14 | 15 | 16 | 17 | 18 | com.datastax 19 | killrvideo-parent 20 | 6 21 | 22 | 23 | 24 | 9.2 25 | 6.1.2 26 | 5.2.3 27 | 5.0.1 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | com.datastax 37 | killrvideo-dse 38 | ${project.version} 39 | 40 | 41 | 42 | 43 | com.graphql-java 44 | graphql-java 45 | ${graphql-java.version} 46 | 47 | 48 | com.graphql-java 49 | graphql-java-servlet 50 | ${graphql-java-servlet.version} 51 | 52 | 53 | com.graphql-java 54 | graphql-java-tools 55 | ${graphql-java-tools.version} 56 | 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-web 62 | 63 | 64 | com.graphql-java 65 | graphql-spring-boot-starter 66 | ${graphql-springboot-starter.version} 67 | 68 | 69 | com.graphql-java 70 | graphiql-spring-boot-starter 71 | ${graphql-springboot-starter.version} 72 | 73 | 74 | 75 | io.netty 76 | netty-all 77 | 78 | 79 | 80 | 81 | org.springframework.boot 82 | spring-boot-devtools 83 | true 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | org.codehaus.mojo 97 | build-helper-maven-plugin 98 | ${build-helper-maven-plugin.version} 99 | 100 | 101 | generate-sources 102 | 103 | add-source 104 | 105 | 106 | 107 | target/generated-sources/protobuf/java 108 | target/generated-sources/protobuf/grpc-java 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | org.springframework.boot 118 | spring-boot-maven-plugin 119 | ${spring-boot.version} 120 | 121 | ${start-class} 122 | 123 | 124 | 125 | 126 | repackage 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /sample-docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | 5 | dc1n1: 6 | image: cassandra:3.11.11 7 | networks: 8 | - backend 9 | ports: 10 | - 9052:9042 11 | mem_limit: 2G 12 | environment: 13 | - HEAP_NEWSIZE=128M 14 | - MAX_HEAP_SIZE=1024M 15 | - CASSANDRA_SEEDS=dc1n1 16 | - CASSANDRA_CLUSTER_NAME=stargatefull 17 | - CASSANDRA_DC=DC1 18 | - CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch 19 | 20 | dc1n2: 21 | image: cassandra:3.11.11 22 | command: /bin/bash -c "echo 'Waiting for seed node' && sleep 30 && /docker-entrypoint.sh cassandra -f" 23 | networks: 24 | - backend 25 | mem_limit: 2G 26 | depends_on: 27 | - dc1n1 28 | environment: 29 | - HEAP_NEWSIZE=128M 30 | - MAX_HEAP_SIZE=1024M 31 | - CASSANDRA_SEEDS=dc1n1 32 | - CASSANDRA_CLUSTER_NAME=stargatefull 33 | - CASSANDRA_DC=DC1 34 | - CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch 35 | 36 | dc1n3: 37 | image: cassandra:3.11.11 38 | command: /bin/bash -c "echo 'Waiting for seed node' && sleep 80 && /docker-entrypoint.sh cassandra -f" 39 | networks: 40 | - backend 41 | mem_limit: 2G 42 | depends_on: 43 | - dc1n1 44 | environment: 45 | - HEAP_NEWSIZE=128M 46 | - MAX_HEAP_SIZE=1024M 47 | - CASSANDRA_SEEDS=dc1n1 48 | - CASSANDRA_CLUSTER_NAME=stargatefull 49 | - CASSANDRA_DC=DC1 50 | - CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch 51 | 52 | 53 | dc2n1: 54 | image: cassandra:3.11.11 55 | networks: 56 | - backend 57 | ports: 58 | - 9062:9042 59 | mem_limit: 2G 60 | environment: 61 | - HEAP_NEWSIZE=128M 62 | - MAX_HEAP_SIZE=1024M 63 | - CASSANDRA_SEEDS=dc2n1,dc1n1 64 | - CASSANDRA_CLUSTER_NAME=stargatefull 65 | - CASSANDRA_DC=DC2 66 | - CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch 67 | 68 | dc2n2: 69 | image: cassandra:3.11.11 70 | command: /bin/bash -c "echo 'Waiting for seed node' && sleep 30 && /docker-entrypoint.sh cassandra -f" 71 | networks: 72 | - backend 73 | mem_limit: 2G 74 | depends_on: 75 | - dc2n1 76 | environment: 77 | - HEAP_NEWSIZE=128M 78 | - MAX_HEAP_SIZE=1024M 79 | - CASSANDRA_SEEDS=dc2n1,dc1n1 80 | - CASSANDRA_CLUSTER_NAME=stargatefull 81 | - CASSANDRA_DC=DC2 82 | - CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch 83 | 84 | dc2n3: 85 | image: cassandra:3.11.11 86 | command: /bin/bash -c "echo 'Waiting for seed node' && sleep 80 && /docker-entrypoint.sh cassandra -f" 87 | networks: 88 | - backend 89 | mem_limit: 2G 90 | depends_on: 91 | - dc2n1 92 | environment: 93 | - HEAP_NEWSIZE=128M 94 | - MAX_HEAP_SIZE=1024M 95 | - CASSANDRA_SEEDS=dc2n1,dc1n1 96 | - CASSANDRA_CLUSTER_NAME=stargatefull 97 | - CASSANDRA_DC=DC2 98 | - CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch 99 | 100 | #Stargate Nodes DC1 101 | dc1s1: 102 | image: stargateio/stargate-3_11:v1.0.41 103 | depends_on: 104 | - dc1n1 105 | networks: 106 | - backend 107 | ports: 108 | - 9053:9042 109 | - 8080:8080 110 | - 8081:8081 111 | - 8082:8082 112 | - 8083:8090 113 | mem_limit: 2G 114 | environment: 115 | - JAVA_OPTS="-Xmx2G" 116 | - CLUSTER_NAME=stargatefull 117 | - CLUSTER_VERSION=3.11 118 | - CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch 119 | - ENABLE_AUTH=true 120 | - RACK_NAME=rack1 121 | - DATACENTER_NAME=DC1 122 | - SEED=dc1n1 123 | 124 | dc1s2: 125 | image: stargateio/stargate-3_11:v1.0.41 126 | depends_on: 127 | - dc1n1 128 | networks: 129 | - backend 130 | ports: 131 | - 9054:9042 132 | - 9090:8080 133 | - 9091:8081 134 | - 9092:8082 135 | - 9093:8090 136 | mem_limit: 2G 137 | environment: 138 | - JAVA_OPTS="-Xmx2G" 139 | - CLUSTER_NAME=stargatefull 140 | - CLUSTER_VERSION=3.11 141 | - CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch 142 | - ENABLE_AUTH=true 143 | - RACK_NAME=rack1 144 | - DATACENTER_NAME=DC1 145 | - SEED=dc1n1 146 | 147 | #Stargate Nodes DC2 148 | dc2s1: 149 | image: stargateio/stargate-3_11:v1.0.41 150 | depends_on: 151 | - dc2n1 152 | networks: 153 | - backend 154 | ports: 155 | - 9063:9042 156 | - 6060:8080 157 | - 6061:8081 158 | - 6062:8082 159 | - 6063:8090 160 | mem_limit: 2G 161 | environment: 162 | - JAVA_OPTS="-Xmx2G" 163 | - CLUSTER_NAME=stargatefull 164 | - CLUSTER_VERSION=3.11 165 | - CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch 166 | - ENABLE_AUTH=true 167 | - RACK_NAME=rack1 168 | - DATACENTER_NAME=DC2 169 | - SEED=dc2n1 170 | 171 | dc2s2: 172 | image: stargateio/stargate-3_11:v1.0.41 173 | depends_on: 174 | - dc2n1 175 | networks: 176 | - backend 177 | ports: 178 | - 9064:9042 179 | - 7070:8080 180 | - 7071:8081 181 | - 7072:8082 182 | - 7073:8090 183 | mem_limit: 2G 184 | environment: 185 | - JAVA_OPTS="-Xmx2G" 186 | - CLUSTER_NAME=stargatefull 187 | - CLUSTER_VERSION=3.11 188 | - CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch 189 | - ENABLE_AUTH=true 190 | - RACK_NAME=rack1 191 | - DATACENTER_NAME=DC2 192 | - SEED=dc2n1 193 | 194 | networks: 195 | backend: 196 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/test/resources/schema.cql: -------------------------------------------------------------------------------- 1 | CREATE KEYSPACE IF NOT EXISTS killrvideo WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}; 2 | 3 | CREATE TABLE IF NOT EXISTS killrvideo.user_credentials ( 4 | email text, 5 | pass text, 6 | userid uuid, 7 | PRIMARY KEY (email) 8 | ); 9 | 10 | CREATE TABLE IF NOT EXISTS killrvideo.users ( 11 | userid uuid, 12 | firstname text, 13 | lastname text, 14 | email text, 15 | created_date timestamp, 16 | PRIMARY KEY (userid) 17 | ); 18 | 19 | CREATE TABLE IF NOT EXISTS killrvideo.videos ( 20 | videoid uuid, 21 | userid uuid, 22 | name text, 23 | description text, 24 | location text, 25 | location_type int, 26 | preview_image_location text, 27 | tags set, 28 | added_date timestamp, 29 | PRIMARY KEY (videoid) 30 | ); 31 | 32 | CREATE TABLE IF NOT EXISTS killrvideo.user_videos ( 33 | userid uuid, 34 | added_date timestamp, 35 | videoid uuid, 36 | name text, 37 | preview_image_location text, 38 | PRIMARY KEY (userid, added_date, videoid) 39 | ) WITH CLUSTERING ORDER BY (added_date DESC, videoid ASC); 40 | 41 | CREATE TABLE IF NOT EXISTS killrvideo.latest_videos ( 42 | yyyymmdd text, 43 | added_date timestamp, 44 | videoid uuid, 45 | userid uuid, 46 | name text, 47 | preview_image_location text, 48 | PRIMARY KEY (yyyymmdd, added_date, videoid) 49 | ) WITH CLUSTERING ORDER BY (added_date DESC, videoid ASC); 50 | 51 | CREATE TABLE IF NOT EXISTS killrvideo.video_ratings ( 52 | videoid uuid, 53 | rating_counter counter, 54 | rating_total counter, 55 | PRIMARY KEY (videoid) 56 | ); 57 | 58 | CREATE TABLE IF NOT EXISTS killrvideo.video_ratings_by_user ( 59 | videoid uuid, 60 | userid uuid, 61 | rating int, 62 | PRIMARY KEY (videoid, userid) 63 | ); 64 | 65 | CREATE TABLE IF NOT EXISTS killrvideo.video_playback_stats ( 66 | videoid uuid, 67 | views counter, 68 | PRIMARY KEY (videoid) 69 | ); 70 | 71 | CREATE TABLE IF NOT EXISTS killrvideo.video_recommendations ( 72 | userid uuid, 73 | added_date timestamp, 74 | videoid uuid, 75 | rating float, 76 | authorid uuid, 77 | name text, 78 | preview_image_location text, 79 | PRIMARY KEY(userid, added_date, videoid) 80 | ) WITH CLUSTERING ORDER BY (added_date DESC, videoid ASC); 81 | 82 | CREATE TABLE IF NOT EXISTS killrvideo.video_recommendations_by_video ( 83 | videoid uuid, 84 | userid uuid, 85 | rating float, 86 | added_date timestamp STATIC, 87 | authorid uuid STATIC, 88 | name text STATIC, 89 | preview_image_location text STATIC, 90 | PRIMARY KEY(videoid, userid) 91 | ); 92 | 93 | CREATE TABLE IF NOT EXISTS killrvideo.videos_by_tag ( 94 | tag text, 95 | videoid uuid, 96 | added_date timestamp, 97 | userid uuid, 98 | name text, 99 | preview_image_location text, 100 | tagged_date timestamp, 101 | PRIMARY KEY (tag, videoid) 102 | ); 103 | 104 | CREATE TABLE IF NOT EXISTS killrvideo.tags_by_letter ( 105 | first_letter text, 106 | tag text, 107 | PRIMARY KEY (first_letter, tag) 108 | ); 109 | 110 | CREATE TABLE IF NOT EXISTS killrvideo.comments_by_video ( 111 | videoid uuid, 112 | commentid timeuuid, 113 | userid uuid, 114 | comment text, 115 | PRIMARY KEY (videoid, commentid) 116 | ) WITH CLUSTERING ORDER BY (commentid DESC); 117 | 118 | CREATE TABLE IF NOT EXISTS killrvideo.comments_by_user ( 119 | userid uuid, 120 | commentid timeuuid, 121 | videoid uuid, 122 | comment text, 123 | PRIMARY KEY (userid, commentid) 124 | ) WITH CLUSTERING ORDER BY (commentid DESC); 125 | 126 | CREATE TABLE IF NOT EXISTS killrvideo.uploaded_video_destinations ( 127 | upload_url text, 128 | assetid text, 129 | filename text, 130 | locatorid text, 131 | PRIMARY KEY (upload_url) 132 | ); 133 | 134 | CREATE TABLE IF NOT EXISTS killrvideo.uploaded_video_jobs ( 135 | videoid uuid, 136 | upload_url text, 137 | jobid text, 138 | PRIMARY KEY (videoid) 139 | ); 140 | 141 | CREATE TABLE IF NOT EXISTS killrvideo.uploaded_video_jobs_by_jobid ( 142 | jobid text, 143 | videoid uuid, 144 | upload_url text, 145 | PRIMARY KEY (jobid) 146 | ); 147 | 148 | CREATE TABLE IF NOT EXISTS killrvideo.encoding_job_notifications ( 149 | videoid uuid, 150 | status_date timestamp, 151 | etag text, 152 | jobid text, 153 | newstate text, 154 | oldstate text, 155 | PRIMARY KEY (videoid, status_date, etag) 156 | ) WITH CLUSTERING ORDER BY (status_date DESC, etag ASC); 157 | 158 | CREATE TABLE IF NOT EXISTS killrvideo.sample_data_job_log ( 159 | job_name text, 160 | scheduled_run_time timestamp, 161 | actual_run_time timestamp, 162 | PRIMARY KEY (job_name, scheduled_run_time, actual_run_time) 163 | ) WITH CLUSTERING ORDER BY (scheduled_run_time DESC, actual_run_time DESC) 164 | AND default_time_to_live = 1209600; 165 | 166 | CREATE TABLE IF NOT EXISTS killrvideo.sample_data_leases ( 167 | name text, 168 | owner text, 169 | PRIMARY KEY (name) 170 | ) WITH default_time_to_live = 180; 171 | 172 | CREATE TABLE IF NOT EXISTS killrvideo.sample_data_users ( 173 | userid uuid, 174 | PRIMARY KEY(userid) 175 | ); 176 | 177 | CREATE TABLE IF NOT EXISTS killrvideo.sample_data_videos ( 178 | videoid uuid, 179 | PRIMARY KEY (videoid) 180 | ); 181 | 182 | CREATE TABLE IF NOT EXISTS killrvideo.sample_data_youtube_videos ( 183 | sourceid text, 184 | published_at timestamp, 185 | youtube_video_id text, 186 | name text, 187 | description text, 188 | used boolean, 189 | PRIMARY KEY (sourceid, published_at, youtube_video_id) 190 | ) WITH CLUSTERING ORDER BY (published_at DESC, youtube_video_id ASC); -------------------------------------------------------------------------------- /demo-app/src/test/java/com/datastax/astra/Test02_WorkingWithCassandra.java: -------------------------------------------------------------------------------- 1 | package com.datastax.astra; 2 | 3 | import static com.datastax.astra.DemoDataSet.appToken; 4 | import static com.datastax.astra.DemoDataSet.awsEast1; 5 | import static com.datastax.astra.DemoDataSet.clientId; 6 | import static com.datastax.astra.DemoDataSet.clientSecret; 7 | import static com.datastax.astra.DemoDataSet.ksName; 8 | 9 | import java.util.UUID; 10 | 11 | import org.junit.jupiter.api.BeforeAll; 12 | import org.junit.jupiter.api.Test; 13 | 14 | import com.datastax.oss.driver.api.core.cql.SimpleStatement; 15 | import com.datastax.oss.driver.api.core.type.DataTypes; 16 | import com.datastax.oss.driver.api.querybuilder.SchemaBuilder; 17 | import com.dstx.astra.sdk.AstraClient; 18 | 19 | public class Test02_WorkingWithCassandra { 20 | 21 | public static final String ANSI_RESET = "\u001B[0m"; 22 | public static final String ANSI_GREEN = "\u001B[32m"; 23 | private static AstraClient astraClient; 24 | 25 | // 26 | private static String databaseId = "cfa040d4-cfd4-489f-9dd2-352cb01801f1"; 27 | 28 | @BeforeAll 29 | public static void init() { 30 | astraClient = AstraClient.builder() 31 | .appToken(appToken).clientId(clientId).clientSecret(clientSecret) 32 | .databaseId(databaseId).cloudProviderRegion(awsEast1) 33 | .keyspace(ksName) 34 | .build(); 35 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Astra Client Set"); 36 | } 37 | 38 | @Test 39 | public void createEntitiies() { 40 | 41 | astraClient.cqlSession().execute(SchemaBuilder 42 | .createType("video_format").ifNotExists() 43 | .withField("width", DataTypes.INT) 44 | .withField("height", DataTypes.INT) 45 | .build()); 46 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - UDT Created"); 47 | 48 | astraClient.cqlSession().execute(SchemaBuilder 49 | .createTable("videos") 50 | .ifNotExists() 51 | .withPartitionKey("videoid", DataTypes.UUID) 52 | .withColumn("title", DataTypes.TEXT) 53 | .withColumn("upload", DataTypes.TIMESTAMP) 54 | .withColumn("email", DataTypes.TEXT) 55 | .withColumn("url", DataTypes.TEXT) 56 | .withColumn("tags", DataTypes.setOf(DataTypes.TEXT)) 57 | .withColumn("frames", DataTypes.listOf(DataTypes.INT)) 58 | .withColumn("formats", DataTypes.mapOf(DataTypes.TEXT, 59 | SchemaBuilder.udt("video_format", true))).build()); 60 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Table Created"); 61 | 62 | } 63 | 64 | @Test 65 | public void useTheDataModel() { 66 | astraClient.cqlSession().execute("" 67 | + "INSERT INTO videos(videoid, email, title, upload, url, tags, frames, formats)\n" 68 | + "VALUES(uuid(), 'clu@sample.com', 'sample video', \n" 69 | + " toTimeStamp(now()), 'http://google.fr',\n" 70 | + " { 'cassandra','accelerate','2020'},\n" 71 | + " [ 1, 2, 3, 4], \n" 72 | + " { 'mp4':{width:1,height:1},'ogg':{width:1,height:1}});"); 73 | 74 | astraClient.cqlSession().execute("" 75 | + "INSERT INTO videos(videoid, email, title, upload, url)\n" 76 | + "VALUES(uuid(), 'clu@sample.com', 'video2', toTimeStamp(now()), 'http://google.fr');"); 77 | 78 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Data Inserted"); 79 | 80 | astraClient.cqlSession().execute("" 81 | + "INSERT INTO videos JSON '{\n" 82 | + " \"videoid\":\"e466f561-4ea4-4eb7-8dcc-126e0fbfd573\",\n" 83 | + " \"email\":\"clunven@sample.com\",\n" 84 | + " \"title\":\"A Second videos\",\n" 85 | + " \"upload\":\"2020-02-26 15:09:22 +00:00\",\n" 86 | + " \"url\": \"http://google.fr\",\n" 87 | + " \"frames\": [1,2,3,4],\n" 88 | + " \"tags\": [ \"cassandra\",\"accelerate\", \"2020\"],\n" 89 | + " \"formats\": { \n" 90 | + " \"mp4\": {\"width\":1,\"height\":1},\n" 91 | + " \"ogg\": {\"width\":1,\"height\":1}\n" 92 | + " }\n" 93 | + "}';"); 94 | 95 | UUID videoid4 = UUID.randomUUID(); 96 | astraClient.cqlSession().execute(SimpleStatement.builder("INSERT INTO videos JSON ? ") 97 | .addPositionalValue("{" 98 | + "\"videoid\":\""+ videoid4.toString() + "\"," 99 | + "\"email\":\"clu@sample.com\"," 100 | + "\"title\":\"sample video\"," 101 | + "\"upload\":\"2020-02-26 15:09:22 +00:00\"," 102 | + "\"url\":\"http://google.fr\"," 103 | + "\"frames\": [1,2,3,4]," 104 | + "\"tags\": [\"cassandra\",\"accelerate\", \"2020\"]," 105 | + "\"formats\": {" 106 | + " \"mp4\":{\"width\":1,\"height\":1}," 107 | + " \"ogg\":{\"width\":1,\"height\":1}" 108 | + "}}") 109 | .build()); 110 | 111 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - More Data Inserted as JSON"); 112 | 113 | astraClient.cqlSession().execute("select * from videos;") 114 | .all().stream() 115 | .forEach(row -> { System.out.println(ANSI_GREEN + "+ [ROW]" + ANSI_RESET + " - id=" + row.getUuid("videoid"));}); 116 | 117 | System.out.println(ANSI_GREEN + "[OK]" + ANSI_RESET + " - Data Retrieved"); 118 | 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 4.0.0 10 | killrvideo-api-grpc 11 | + killrvideo-api-grpc 12 | Exposing some Microservices as GRPC 13 | 14 | 15 | 16 | 17 | 18 | com.datastax 19 | killrvideo-parent 20 | 6 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | com.datastax 31 | killrvideo-dse 32 | ${project.version} 33 | 34 | 35 | 36 | 37 | 38 | io.grpc 39 | grpc-all 40 | 41 | 42 | com.google.protobuf 43 | protobuf-java 44 | 45 | 46 | org.apache.commons 47 | commons-lang3 48 | 49 | 50 | commons-codec 51 | commons-codec 52 | 53 | 54 | org.apache.commons 55 | commons-collections4 56 | 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter 62 | 63 | 64 | 65 | 66 | org.springframework.boot 67 | spring-boot-devtools 68 | true 69 | 70 | 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-starter-test 75 | test 76 | 77 | 78 | junit 79 | junit 80 | test 81 | 82 | 83 | info.cukes 84 | cucumber-java 85 | test 86 | 87 | 88 | info.cukes 89 | cucumber-spring 90 | test 91 | 92 | 93 | info.cukes 94 | cucumber-junit 95 | test 96 | 97 | 98 | org.assertj 99 | assertj-core 100 | test 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | org.codehaus.mojo 114 | build-helper-maven-plugin 115 | ${build-helper-maven-plugin.version} 116 | 117 | 118 | generate-sources 119 | 120 | add-source 121 | 122 | 123 | 124 | target/generated-sources/protobuf/java 125 | target/generated-sources/protobuf/grpc-java 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | org.xolstice.maven.plugins 135 | protobuf-maven-plugin 136 | ${protobuf-maven-plugin.version} 137 | 138 | ${basedir}/src/main/resources/proto 139 | com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} 140 | grpc-java 141 | io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc.version}:exe:${os.detected.classifier} 142 | 143 | 144 | 145 | 146 | compile 147 | compile-custom 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | org.springframework.boot 156 | spring-boot-maven-plugin 157 | ${spring-boot.version} 158 | 159 | ${start-class} 160 | 161 | 162 | 163 | 164 | repackage 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/test/resources/service/2_VideoCatalogService.feature: -------------------------------------------------------------------------------- 1 | @video_scenarios 2 | @user_scenarios 3 | @video_scenarios_only 4 | Feature: Video Catalog Management 5 | 6 | Users (userXXX) have their id randomized. 7 | 8 | Background: 9 | Given those users already exist: user1, user2 10 | 11 | 12 | Scenario: Submit youtube video and retrieve it by id 13 | When user1 submit Youtube videos: 14 | | id | name | description | tags | url | 15 | | video1 | b-wing-ucs.mp4 | Lego Star Wars B-Wing UCS | lego,star wars,space| https://www.youtube.com/watch?v=zcryCEQfTwY | 16 | Then I can retrieve video1 by id 17 | 18 | 19 | Scenario: Submit youtube videos and get their preview 20 | When user1 submit Youtube videos: 21 | | id | name | description | tags | url | 22 | | video1 | b-wing-ucs.mp4 | Lego Star Wars B-Wing UCS | lego,star wars,space| https://www.youtube.com/watch?v=zcryCEQfTwY | 23 | | video2 | y-wing-ucs.mp4 | Lego Star Wars Y-Wing UCS | lego,star wars,space| https://www.youtube.com/watch?v=TRyja74EXp0 | 24 | Then I can get preview of: video1, video2 25 | 26 | Scenario: user1 submits youtube videos and see them in his videos preview 27 | When user1 submit Youtube videos: 28 | | id | name | description | tags | url | 29 | | video1 | b-wing-ucs.mp4 | Lego Star Wars B-Wing UCS | lego,star wars,space| https://www.youtube.com/watch?v=zcryCEQfTwY | 30 | | video2 | y-wing-ucs.mp4 | Lego Star Wars Y-Wing UCS | lego,star wars,space| https://www.youtube.com/watch?v=TRyja74EXp0 | 31 | Then user1 videos preview contains: video2, video1 32 | 33 | Scenario: Submit youtube videos and see them in latest videos preview 34 | When user1 submit Youtube videos: 35 | | id | name | description | tags | url | 36 | | video1 | b-wing-ucs.mp4 | Lego Star Wars B-Wing UCS | lego,star wars,space| https://www.youtube.com/watch?v=zcryCEQfTwY | 37 | | video2 | y-wing-ucs.mp4 | Lego Star Wars Y-Wing UCS | lego,star wars,space| https://www.youtube.com/watch?v=TRyja74EXp0 | 38 | Then latest videos preview contains: video2, video1 39 | 40 | 41 | Scenario: Submit youtube videos and get latest videos with paging using starting video id & added date 42 | #Page size = 2 43 | When user1 submit Youtube videos: 44 | | id | name | description | tags | url | 45 | | video1 | b-wing-ucs.mp4 | Lego Star Wars B-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=zcryCEQfTwY | 46 | | video2 | y-wing-ucs.mp4 | Lego Star Wars Y-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=TRyja74EXp0 | 47 | | video3 | x-wing-ucs.mp4 | Lego Star Wars X-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=YMJvcYDtYJQ | 48 | | video4 | tie-fighter-ucs.mp4 | Lego Star Wars Tie Fighter UCS | lego,star wars,space | https://www.youtube.com/watch?v=Kcph-70owiM | 49 | | video5 | mil-falcon-ucs.mp4 | Lego Star Wars Millennium Falcon UCS | lego,star wars,space | https://www.youtube.com/watch?v=Q9BhA9POhBQ | 50 | Then latest videos preview starting from video3 contains: video3, video2 51 | 52 | 53 | Scenario: Submit youtube videos and get latest videos with paging using paging state 54 | #page size = 3 so that page 1 contains video5, video4 & video3, page 2 contains video2 & video1 (DESC ORDER) 55 | When user1 submit Youtube videos: 56 | | id | name | description | tags | url | 57 | | video1 | b-wing-ucs.mp4 | Lego Star Wars B-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=zcryCEQfTwY | 58 | | video2 | y-wing-ucs.mp4 | Lego Star Wars Y-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=TRyja74EXp0 | 59 | | video3 | x-wing-ucs.mp4 | Lego Star Wars X-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=YMJvcYDtYJQ | 60 | | video4 | tie-fighter-ucs.mp4 | Lego Star Wars Tie Fighter UCS | lego,star wars,space | https://www.youtube.com/watch?v=Kcph-70owiM | 61 | | video5 | mil-falcon-ucs.mp4 | Lego Star Wars Millennium Falcon UCS | lego,star wars,space | https://www.youtube.com/watch?v=Q9BhA9POhBQ | 62 | Then latest videos preview at page 2 contains: video2, video1 63 | 64 | 65 | Scenario: user1 submits youtube videos and get see them in his videos preview with paging using starting video id & added date 66 | #page size = 2 67 | When user1 submit Youtube videos: 68 | | id | name | description | tags | url | 69 | | video1 | b-wing-ucs.mp4 | Lego Star Wars B-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=zcryCEQfTwY | 70 | | video2 | y-wing-ucs.mp4 | Lego Star Wars Y-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=TRyja74EXp0 | 71 | | video3 | x-wing-ucs.mp4 | Lego Star Wars X-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=YMJvcYDtYJQ | 72 | | video4 | tie-fighter-ucs.mp4 | Lego Star Wars Tie Fighter UCS | lego,star wars,space | https://www.youtube.com/watch?v=Kcph-70owiM | 73 | | video5 | mil-falcon-ucs.mp4 | Lego Star Wars Millennium Falcon UCS | lego,star wars,space | https://www.youtube.com/watch?v=Q9BhA9POhBQ | 74 | Then user1 videos preview starting from video3 contains: video3, video2 75 | 76 | 77 | Scenario: user1 submits youtube videos and get see them in his videos preview with paging state 78 | #page size = 2 so that page 1 contains video5 & video4, page 2 contains video3 & video2 ... (DESC ORDER) 79 | When user1 submit Youtube videos: 80 | | id | name | description | tags | url | 81 | | video1 | b-wing-ucs.mp4 | Lego Star Wars B-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=zcryCEQfTwY | 82 | | video2 | y-wing-ucs.mp4 | Lego Star Wars Y-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=TRyja74EXp0 | 83 | | video3 | x-wing-ucs.mp4 | Lego Star Wars X-Wing UCS | lego,star wars,space | https://www.youtube.com/watch?v=YMJvcYDtYJQ | 84 | | video4 | tie-fighter-ucs.mp4 | Lego Star Wars Tie Fighter UCS | lego,star wars,space | https://www.youtube.com/watch?v=Kcph-70owiM | 85 | | video5 | mil-falcon-ucs.mp4 | Lego Star Wars Millennium Falcon UCS | lego,star wars,space | https://www.youtube.com/watch?v=Q9BhA9POhBQ | 86 | Then user1 videos preview at page 3 contains: video1 -------------------------------------------------------------------------------- /demo-app/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/main/java/com/killrvideo/grpc/CommentsGrpcService.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.grpc; 2 | 3 | import static java.util.UUID.fromString; 4 | 5 | import java.time.Instant; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | 12 | import com.killrvideo.dse.dao.CommentDseDao; 13 | import com.killrvideo.dse.dao.dto.QueryCommentByUser; 14 | import com.killrvideo.dse.dao.dto.QueryCommentByVideo; 15 | import com.killrvideo.dse.model.Comment; 16 | import com.killrvideo.grpc.utils.CommentGrpcHelper; 17 | 18 | import io.grpc.Status; 19 | import io.grpc.stub.StreamObserver; 20 | import killrvideo.comments.CommentsServiceGrpc.CommentsServiceImplBase; 21 | import killrvideo.comments.CommentsServiceOuterClass.CommentOnVideoRequest; 22 | import killrvideo.comments.CommentsServiceOuterClass.CommentOnVideoResponse; 23 | import killrvideo.comments.CommentsServiceOuterClass.GetUserCommentsRequest; 24 | import killrvideo.comments.CommentsServiceOuterClass.GetUserCommentsResponse; 25 | import killrvideo.comments.CommentsServiceOuterClass.GetVideoCommentsRequest; 26 | import killrvideo.comments.CommentsServiceOuterClass.GetVideoCommentsResponse; 27 | 28 | /** 29 | * Exposition of comment services with GPRC Technology & Protobuf Interface 30 | * 31 | * @author DataStax evangelist team. 32 | */ 33 | @Service 34 | public class CommentsGrpcService extends CommentsServiceImplBase { 35 | 36 | /** Loger for that class. */ 37 | private static Logger LOGGER = LoggerFactory.getLogger(CommentsGrpcService.class); 38 | 39 | @Autowired 40 | private CommentGrpcHelper helper; 41 | 42 | /** Communications and queries to DSE (Comment). */ 43 | @Autowired 44 | private CommentDseDao dseCommentDao; 45 | 46 | /** {@inheritDoc} */ 47 | @Override 48 | public void commentOnVideo(final CommentOnVideoRequest grpcReq, StreamObserver grpcResObserver) { 49 | 50 | // Validate Parameters 51 | helper.validateGrpcRequestCommentOnVideo(LOGGER, grpcReq, grpcResObserver); 52 | 53 | // Stands as stopwatch for logging and messaging 54 | final Instant starts = Instant.now(); 55 | 56 | // Mapping GRPC => Domain (Dao) 57 | Comment q = new Comment(); 58 | q.setVideoid(fromString(grpcReq.getVideoId().getValue())); 59 | q.setCommentid(fromString(grpcReq.getCommentId().getValue())); 60 | q.setUserid(fromString(grpcReq.getUserId().getValue())); 61 | q.setComment(grpcReq.getComment()); 62 | if (LOGGER.isDebugEnabled()) { 63 | LOGGER.debug("Insert comment on video {} for user {} : {}", q.getVideoid(), q.getUserid(), q); 64 | } 65 | 66 | // ASYNCHRONOUS works with ComputableFuture 67 | dseCommentDao.insertCommentAsync(q).whenComplete((result, error) -> { 68 | if (error != null) { 69 | helper.traceError(LOGGER, "commentOnVideo", starts, error); 70 | grpcResObserver.onError(Status.INTERNAL.withCause(error).asRuntimeException()); 71 | } else { 72 | helper.traceSuccess(LOGGER, "commentOnVideo", starts); 73 | grpcResObserver.onNext(CommentOnVideoResponse.newBuilder().build()); 74 | grpcResObserver.onCompleted(); 75 | } 76 | }); 77 | 78 | /* SYNCHRONOUS with the classic try/catch 79 | try { 80 | dseCommentDao.insertComment(q); 81 | helper.traceSuccess(LOGGER, "commentOnVideo", starts); 82 | grpcResObserver.onNext(CommentOnVideoResponse.newBuilder().build()); 83 | grpcResObserver.onCompleted(); 84 | } catch(Throwable t) { 85 | helper.traceError(LOGGER, "commentOnVideo", starts, t); 86 | grpcResObserver.onError(t); 87 | }*/ 88 | 89 | } 90 | 91 | /** {@inheritDoc} */ 92 | @Override 93 | public void getVideoComments(final GetVideoCommentsRequest grpcReq, StreamObserver responseObserver) { 94 | 95 | // Parameter validations 96 | helper.validateGrpcRequestGetVideoComment(LOGGER, grpcReq, responseObserver); 97 | 98 | // Stands as stopwatch for logging and messaging 99 | final Instant starts = Instant.now(); 100 | 101 | // Mapping GRPC => Domain (Dao) : Dedicated bean creating for flexibility 102 | QueryCommentByVideo query = helper.mapFromGrpcVideoCommentToDseQuery(grpcReq); 103 | 104 | // ASYNCHRONOUS works with ComputableFuture 105 | dseCommentDao.findCommentsByVideosIdAsync(query).whenComplete((result, error) -> { 106 | if (result != null) { 107 | helper.traceSuccess(LOGGER, "getVideoComments", starts); 108 | responseObserver.onNext(helper.mapFromDseVideoCommentToGrpcResponse(result)); 109 | responseObserver.onCompleted(); 110 | } else if (error != null){ 111 | helper.traceError(LOGGER,"getVideoComments", starts, error); 112 | responseObserver.onError(error); 113 | } 114 | }); 115 | 116 | /* Synchronous 117 | try { 118 | helper.traceSuccess(LOGGER, "getVideoComments", starts); 119 | responseObserver.onNext(helper.mapFromDseVideoCommentToGrpcResponse(dseCommentDao.findCommentsByVideoId(query))); 120 | responseObserver.onCompleted(); 121 | } catch(Throwable t) { 122 | helper.traceError(LOGGER, "getVideoComments", starts, t); 123 | responseObserver.onError(t); 124 | } 125 | */ 126 | } 127 | 128 | /** {@inheritDoc} */ 129 | @Override 130 | public void getUserComments(final GetUserCommentsRequest grpcReq, StreamObserver responseObserver) { 131 | 132 | // GRPC Parameters Validation 133 | helper.validateGrpcRequest_GetUserComments(LOGGER, grpcReq, responseObserver); 134 | 135 | // Stands as stopwatch for logging and messaging 136 | final Instant starts = Instant.now(); 137 | 138 | // Mapping GRPC => Domain (Dao) : Dedicated bean creating for flexibility 139 | QueryCommentByUser query = helper.mapFromGrpcUserCommentToDseQuery(grpcReq); 140 | if (LOGGER.isDebugEnabled()) { 141 | LOGGER.debug("Listing comment for user {}", query.getUserId()); 142 | } 143 | 144 | // ASYNCHRONOUS works with ComputableFuture 145 | dseCommentDao.findCommentsByUserIdAsync(query).whenComplete((result, error) -> { 146 | if (result != null) { 147 | helper.traceSuccess(LOGGER, "getUserComments", starts); 148 | responseObserver.onNext(helper.mapFromDseUserCommentToGrpcResponse(result)); 149 | responseObserver.onCompleted(); 150 | } else if (error != null){ 151 | helper.traceError(LOGGER,"getUserComments", starts, error); 152 | responseObserver.onError(error); 153 | } 154 | }); 155 | 156 | /*synchronous 157 | try { 158 | helper.traceSuccess(LOGGER, "getUserComments", starts); 159 | responseObserver.onNext(helper.mapFromDseUserCommentToGrpcResponse(dseCommentDao.findCommentsByUserId(query))); 160 | responseObserver.onCompleted(); 161 | } catch(Throwable t) { 162 | helper.traceError(LOGGER,"getUserComments", starts, t); 163 | responseObserver.onError(t); 164 | }*/ 165 | } 166 | 167 | } 168 | -------------------------------------------------------------------------------- /sample-code/killrvideo-api-grpc/src/main/java/com/killrvideo/grpc/utils/CommentGrpcHelper.java: -------------------------------------------------------------------------------- 1 | package com.killrvideo.grpc.utils; 2 | 3 | import static com.killrvideo.grpc.utils.GrpcMapper.dateToTimestamp; 4 | import static com.killrvideo.grpc.utils.GrpcMapper.uuidToTimeUuid; 5 | import static com.killrvideo.grpc.utils.GrpcMapper.uuidToUuid; 6 | import static org.apache.commons.lang3.StringUtils.isBlank; 7 | 8 | import java.util.Optional; 9 | import java.util.UUID; 10 | 11 | import org.slf4j.Logger; 12 | import org.springframework.stereotype.Component; 13 | import org.springframework.util.Assert; 14 | 15 | import com.killrvideo.dse.dao.dto.QueryCommentByUser; 16 | import com.killrvideo.dse.dao.dto.QueryCommentByVideo; 17 | import com.killrvideo.dse.dao.dto.ResultListPage; 18 | import com.killrvideo.dse.model.Comment; 19 | 20 | import io.grpc.stub.StreamObserver; 21 | import killrvideo.comments.CommentsServiceOuterClass; 22 | import killrvideo.comments.CommentsServiceOuterClass.CommentOnVideoRequest; 23 | import killrvideo.comments.CommentsServiceOuterClass.GetUserCommentsRequest; 24 | import killrvideo.comments.CommentsServiceOuterClass.GetUserCommentsResponse; 25 | import killrvideo.comments.CommentsServiceOuterClass.GetVideoCommentsRequest; 26 | import killrvideo.comments.CommentsServiceOuterClass.GetVideoCommentsResponse; 27 | 28 | /** 29 | * Validation of inputs and mapping 30 | * 31 | * @author DataStax evangelist team. 32 | */ 33 | @Component 34 | public class CommentGrpcHelper extends AbstractGrpcHelper { 35 | 36 | /** 37 | * Validate comment On video comment query. 38 | * 39 | * @param request 40 | * current GRPC Request 41 | * @param streamObserver 42 | * response async 43 | * @return 44 | * true if the query is valid 45 | */ 46 | public void validateGrpcRequestCommentOnVideo(Logger logger, CommentOnVideoRequest request, StreamObserver streamObserver) { 47 | StringBuilder errorMessage = initErrorString(request); 48 | boolean isValid = 49 | notEmpty(!request.hasUserId() || isBlank(request.getUserId().getValue()), "userId", "video request",errorMessage) && 50 | notEmpty(!request.hasVideoId() || isBlank(request.getVideoId().getValue()), "videoId", "video request",errorMessage) && 51 | notEmpty(!request.hasCommentId() || isBlank(request.getCommentId().getValue()), "commentId", "video request",errorMessage) && 52 | notEmpty(isBlank(request.getComment()), "comment", "video request",errorMessage); 53 | Assert.isTrue(validate(logger, streamObserver, errorMessage, isValid), "Invalid parameter for 'commentOnVideo'"); 54 | } 55 | 56 | /** 57 | * Validate get video comment query. 58 | * 59 | * @param request 60 | * current GRPC Request 61 | * @param streamObserver 62 | * response async 63 | * @return 64 | * true if the query is valid 65 | */ 66 | public void validateGrpcRequestGetVideoComment(Logger logger, GetVideoCommentsRequest request, StreamObserver streamObserver) { 67 | final StringBuilder errorMessage = initErrorString(request); 68 | boolean isValid = true; 69 | 70 | if (!request.hasVideoId() || isBlank(request.getVideoId().getValue())) { 71 | errorMessage.append("\t\tvideo id should be provided for get video comment request\n"); 72 | isValid = false; 73 | } 74 | 75 | if (request.getPageSize() <= 0) { 76 | errorMessage.append("\t\tpage size should be strictly positive for get video comment request"); 77 | isValid = false; 78 | } 79 | 80 | Assert.isTrue(validate(logger, streamObserver, errorMessage, isValid), "Invalid parameter for 'getVideoComments'"); 81 | } 82 | 83 | /** 84 | * Validate get user comment query. 85 | * 86 | * @param request 87 | * current GRPC Request 88 | * @param streamObserver 89 | * response async 90 | * @return 91 | * true if the query is valid 92 | */ 93 | public void validateGrpcRequest_GetUserComments(Logger logger, GetUserCommentsRequest request, StreamObserver streamObserver) { 94 | final StringBuilder errorMessage = initErrorString(request); 95 | boolean isValid = 96 | notEmpty(!request.hasUserId() || isBlank(request.getUserId().getValue()), "userId", "comment request",errorMessage) && 97 | positive(request.getPageSize() <= 0, "page size", "comment request",errorMessage); 98 | Assert.isTrue(validate(logger, streamObserver, errorMessage, isValid), "Invalid parameter for 'getUserComments'"); 99 | } 100 | 101 | // --- Mappings --- 102 | 103 | /** 104 | * Utility from exposition to Dse query. 105 | * 106 | * @param grpcReq 107 | * grpc Request 108 | * @return 109 | * query bean for Dao 110 | */ 111 | public QueryCommentByUser mapFromGrpcUserCommentToDseQuery(GetUserCommentsRequest grpcReq) { 112 | QueryCommentByUser targetQuery = new QueryCommentByUser(); 113 | if (grpcReq.hasStartingCommentId() && 114 | !isBlank(grpcReq.getStartingCommentId().getValue())) { 115 | targetQuery.setCommentId(Optional.of(UUID.fromString(grpcReq.getStartingCommentId().getValue()))); 116 | } 117 | targetQuery.setUserId(UUID.fromString(grpcReq.getUserId().getValue())); 118 | targetQuery.setPageSize(grpcReq.getPageSize()); 119 | targetQuery.setPageState(Optional.ofNullable(grpcReq.getPagingState())); 120 | return targetQuery; 121 | } 122 | 123 | // Map from CommentDseDao response bean to expected GRPC object. 124 | public GetVideoCommentsResponse mapFromDseVideoCommentToGrpcResponse(ResultListPage dseRes) { 125 | final GetVideoCommentsResponse.Builder builder = GetVideoCommentsResponse.newBuilder(); 126 | for (Comment c : dseRes.getResults()) { 127 | builder.setVideoId(uuidToUuid(c.getVideoid())); 128 | builder.addComments(CommentsServiceOuterClass.VideoComment.newBuilder() 129 | .setComment(c.getComment()) 130 | .setUserId(uuidToUuid(c.getUserid())) 131 | .setCommentId(uuidToTimeUuid(c.getCommentid())) 132 | .setCommentTimestamp(dateToTimestamp(c.getDateOfComment())) 133 | .build()); 134 | } 135 | dseRes.getPagingState().ifPresent(builder::setPagingState); 136 | return builder.build(); 137 | } 138 | 139 | // Map from CommentDseDao response bean to expected GRPC object. 140 | public GetUserCommentsResponse mapFromDseUserCommentToGrpcResponse(ResultListPage dseRes) { 141 | final GetUserCommentsResponse.Builder builder = GetUserCommentsResponse.newBuilder(); 142 | for (Comment c : dseRes.getResults()) { 143 | builder.setUserId(uuidToUuid(c.getUserid())); 144 | builder.addComments(CommentsServiceOuterClass.UserComment.newBuilder() 145 | .setComment(c.getComment()) 146 | .setCommentId(uuidToTimeUuid(c.getCommentid())) 147 | .setVideoId(uuidToUuid(c.getVideoid())) 148 | .setCommentTimestamp(dateToTimestamp(c.getDateOfComment())) 149 | .build()); 150 | } 151 | dseRes.getPagingState().ifPresent(builder::setPagingState); 152 | return builder.build(); 153 | } 154 | 155 | /** 156 | * Utility from exposition to Dse query. 157 | * 158 | * @param grpcReq 159 | * grpc Request 160 | * @return 161 | * query bean for Dao 162 | */ 163 | public QueryCommentByVideo mapFromGrpcVideoCommentToDseQuery(GetVideoCommentsRequest grpcReq) { 164 | QueryCommentByVideo targetQuery = new QueryCommentByVideo(); 165 | if (grpcReq.hasStartingCommentId() && 166 | !isBlank(grpcReq.getStartingCommentId().getValue())) { 167 | targetQuery.setCommentId(Optional.of(UUID.fromString(grpcReq.getStartingCommentId().getValue()))); 168 | } 169 | targetQuery.setVideoId(UUID.fromString(grpcReq.getVideoId().getValue())); 170 | targetQuery.setPageSize(grpcReq.getPageSize()); 171 | targetQuery.setPageState(Optional.ofNullable(grpcReq.getPagingState())); 172 | return targetQuery; 173 | } 174 | 175 | } 176 | -------------------------------------------------------------------------------- /demo-app/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | fi 118 | 119 | if [ -z "$JAVA_HOME" ]; then 120 | javaExecutable="`which javac`" 121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 122 | # readlink(1) is not available as standard on Solaris 10. 123 | readLink=`which readlink` 124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 125 | if $darwin ; then 126 | javaHome="`dirname \"$javaExecutable\"`" 127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 128 | else 129 | javaExecutable="`readlink -f \"$javaExecutable\"`" 130 | fi 131 | javaHome="`dirname \"$javaExecutable\"`" 132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 133 | JAVA_HOME="$javaHome" 134 | export JAVA_HOME 135 | fi 136 | fi 137 | fi 138 | 139 | if [ -z "$JAVACMD" ] ; then 140 | if [ -n "$JAVA_HOME" ] ; then 141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 142 | # IBM's JDK on AIX uses strange locations for the executables 143 | JAVACMD="$JAVA_HOME/jre/sh/java" 144 | else 145 | JAVACMD="$JAVA_HOME/bin/java" 146 | fi 147 | else 148 | JAVACMD="`which java`" 149 | fi 150 | fi 151 | 152 | if [ ! -x "$JAVACMD" ] ; then 153 | echo "Error: JAVA_HOME is not defined correctly." >&2 154 | echo " We cannot execute $JAVACMD" >&2 155 | exit 1 156 | fi 157 | 158 | if [ -z "$JAVA_HOME" ] ; then 159 | echo "Warning: JAVA_HOME environment variable is not set." 160 | fi 161 | 162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 163 | 164 | # traverses directory structure from process work directory to filesystem root 165 | # first directory with .mvn subdirectory is considered project base directory 166 | find_maven_basedir() { 167 | 168 | if [ -z "$1" ] 169 | then 170 | echo "Path not specified to find_maven_basedir" 171 | return 1 172 | fi 173 | 174 | basedir="$1" 175 | wdir="$1" 176 | while [ "$wdir" != '/' ] ; do 177 | if [ -d "$wdir"/.mvn ] ; then 178 | basedir=$wdir 179 | break 180 | fi 181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 182 | if [ -d "${wdir}" ]; then 183 | wdir=`cd "$wdir/.."; pwd` 184 | fi 185 | # end of workaround 186 | done 187 | echo "${basedir}" 188 | } 189 | 190 | # concatenates all lines of a file 191 | concat_lines() { 192 | if [ -f "$1" ]; then 193 | echo "$(tr -s '\n' ' ' < "$1")" 194 | fi 195 | } 196 | 197 | BASE_DIR=`find_maven_basedir "$(pwd)"` 198 | if [ -z "$BASE_DIR" ]; then 199 | exit 1; 200 | fi 201 | 202 | ########################################################################################## 203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 204 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 205 | ########################################################################################## 206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 207 | if [ "$MVNW_VERBOSE" = true ]; then 208 | echo "Found .mvn/wrapper/maven-wrapper.jar" 209 | fi 210 | else 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 213 | fi 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 216 | else 217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 218 | fi 219 | while IFS="=" read key value; do 220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 221 | esac 222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 223 | if [ "$MVNW_VERBOSE" = true ]; then 224 | echo "Downloading from: $jarUrl" 225 | fi 226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 227 | if $cygwin; then 228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 229 | fi 230 | 231 | if command -v wget > /dev/null; then 232 | if [ "$MVNW_VERBOSE" = true ]; then 233 | echo "Found wget ... using wget" 234 | fi 235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 236 | wget "$jarUrl" -O "$wrapperJarPath" 237 | else 238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 239 | fi 240 | elif command -v curl > /dev/null; then 241 | if [ "$MVNW_VERBOSE" = true ]; then 242 | echo "Found curl ... using curl" 243 | fi 244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 245 | curl -o "$wrapperJarPath" "$jarUrl" -f 246 | else 247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 248 | fi 249 | 250 | else 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo "Falling back to using Java to download" 253 | fi 254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 255 | # For Cygwin, switch paths to Windows format before running javac 256 | if $cygwin; then 257 | javaClass=`cygpath --path --windows "$javaClass"` 258 | fi 259 | if [ -e "$javaClass" ]; then 260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 261 | if [ "$MVNW_VERBOSE" = true ]; then 262 | echo " - Compiling MavenWrapperDownloader.java ..." 263 | fi 264 | # Compiling the Java class 265 | ("$JAVA_HOME/bin/javac" "$javaClass") 266 | fi 267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 268 | # Running the downloader 269 | if [ "$MVNW_VERBOSE" = true ]; then 270 | echo " - Running MavenWrapperDownloader.java ..." 271 | fi 272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 273 | fi 274 | fi 275 | fi 276 | fi 277 | ########################################################################################## 278 | # End of extension 279 | ########################################################################################## 280 | 281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 282 | if [ "$MVNW_VERBOSE" = true ]; then 283 | echo $MAVEN_PROJECTBASEDIR 284 | fi 285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 286 | 287 | # For Cygwin, switch paths to Windows format before running java 288 | if $cygwin; then 289 | [ -n "$M2_HOME" ] && 290 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 291 | [ -n "$JAVA_HOME" ] && 292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 293 | [ -n "$CLASSPATH" ] && 294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 295 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 297 | fi 298 | 299 | # Provide a "standardized" way to retrieve the CLI args that will 300 | # work with both Windows and non-Windows executions. 301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 302 | export MAVEN_CMD_LINE_ARGS 303 | 304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 305 | 306 | exec "$JAVACMD" \ 307 | $MAVEN_OPTS \ 308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 311 | -------------------------------------------------------------------------------- /sample-code/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 4.0.0 9 | com.datastax 10 | killrvideo-parent 11 | killrvideo 12 | pom 13 | 6 14 | 15 | 16 | 17 | 18 | 2016 19 | https://killrvideo.github.io/ 20 | 21 | KillrVideo is a reference application for developers looking to learn how to 22 | build applications with Apache Cassandra and DataStax Enterprise. 23 | 24 | 25 | 26 | 27 | 28 | 29 | killrvideo-dse 30 | 31 | killrvideo-api-grpc 32 | killrvideo-api-rest 33 | killrvideo-api-graphql 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 1.6.8 43 | 44 | 45 | 5.0.8.RELEASE 46 | 2.0.3.RELEASE 47 | 48 | 49 | 3.0.0 50 | 6.0.11.Final 51 | 2.0.1.Final 52 | 53 | 54 | 1.10.0 55 | 3.5.1-1 56 | 3.5.1 57 | 1.10.0 58 | 2.1.5 59 | 60 | 61 | 3.3.0.2 62 | 3.11.2 63 | 4.12 64 | 1.2.0 65 | 5.2.0 66 | 1.2.5 67 | 1.9.5 68 | 1.7.0 69 | 70 | 71 | 3.7 72 | 1.11 73 | 4.1 74 | 1.2.3 75 | 1.2 76 | 0.14.0 77 | 19.0 78 | 2.21.0 79 | 1.7.1 80 | 4.1.23.Final 81 | 82 | 83 | 3.0.0 84 | 3.5 85 | 1.8 86 | 1.8 87 | 3.7.0 88 | 2.21.0 89 | 1.5.0.Final 90 | 0.5.0 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | com.datastax.dse 103 | dse-java-driver-core 104 | ${dse.version} 105 | 106 | 107 | com.datastax.dse 108 | dse-java-driver-mapping 109 | ${dse.version} 110 | 111 | 112 | com.datastax.dse 113 | dse-java-driver-extras 114 | ${dse.version} 115 | 116 | 117 | com.datastax.dse 118 | dse-java-driver-graph 119 | ${dse.version} 120 | 121 | 122 | 123 | 124 | javax.validation 125 | validation-api 126 | ${validation.api.version} 127 | 128 | 129 | org.hibernate 130 | hibernate-validator 131 | ${validator.version} 132 | 133 | 134 | org.hibernate.validator 135 | hibernate-validator 136 | ${validator.version} 137 | 138 | 139 | org.glassfish 140 | javax.el 141 | ${javax.el.api.version} 142 | 143 | 144 | javax.el 145 | javax.el-api 146 | ${javax.el.api.version} 147 | 148 | 149 | 150 | 151 | io.grpc 152 | grpc-all 153 | ${grpc.version} 154 | 155 | 156 | com.google.protobuf 157 | protobuf-java 158 | ${protobuf.java.version} 159 | 160 | 161 | org.apache.commons 162 | commons-lang3 163 | ${commons.lang3.version} 164 | 165 | 166 | commons-codec 167 | commons-codec 168 | ${commons-codec.version} 169 | 170 | 171 | org.apache.commons 172 | commons-collections4 173 | ${commons-collections.version} 174 | 175 | 178 | 179 | 180 | 181 | com.xqbase 182 | etcd4j 183 | ${etcd4j.version} 184 | 185 | 186 | com.google.guava 187 | guava 188 | 189 | 190 | 191 | 192 | 193 | 194 | org.springframework 195 | spring-core 196 | ${spring.version} 197 | 198 | 199 | org.springframework 200 | spring-aop 201 | ${spring.version} 202 | 203 | 204 | org.springframework 205 | spring-beans 206 | ${spring.version} 207 | 208 | 209 | org.springframework 210 | spring-context 211 | ${spring.version} 212 | 213 | 214 | org.springframework 215 | spring-expression 216 | ${spring.version} 217 | 218 | 219 | 220 | 221 | org.springframework.boot 222 | spring-boot-dependencies 223 | ${spring-boot.version} 224 | pom 225 | import 226 | 227 | 228 | 229 | 230 | ch.qos.logback 231 | logback-classic 232 | ${logback.version} 233 | 234 | 235 | ch.qos.logback 236 | logback-core 237 | ${logback.version} 238 | 239 | 240 | 241 | 242 | junit 243 | junit 244 | ${junit4.version} 245 | 246 | 247 | org.springframework 248 | spring-test 249 | ${spring.version} 250 | 251 | 252 | 253 | 254 | org.cassandraunit 255 | cassandra-unit 256 | ${cassandra-unit.version} 257 | 258 | 259 | org.apache.cassandra 260 | cassandra-all 261 | ${cassandra-all.version} 262 | 263 | 264 | org.cassandraunit 265 | cassandra-unit-spring 266 | ${cassandra-unit.version} 267 | 268 | 269 | 270 | org.junit.platform 271 | junit-platform-launcher 272 | ${junit-platform.version} 273 | 274 | 275 | org.junit.platform 276 | junit-platform-runner 277 | ${junit-platform.version} 278 | 279 | 280 | org.junit.platform 281 | junit-platform-console-standalone 282 | ${junit-platform.version} 283 | 284 | 285 | org.junit.jupiter 286 | junit-jupiter-engine 287 | ${junit-jupiter.version} 288 | 289 | 290 | org.junit.jupiter 291 | junit-jupiter-params 292 | ${junit-jupiter.version} 293 | 294 | 295 | 296 | info.cukes 297 | cucumber-java 298 | ${cucumber.version} 299 | 300 | 301 | info.cukes 302 | cucumber-spring 303 | ${cucumber.version} 304 | test 305 | 306 | 307 | info.cukes 308 | cucumber-junit 309 | ${cucumber.version} 310 | 311 | 312 | org.assertj 313 | assertj-core 314 | ${assertj-core.version} 315 | 316 | 317 | 318 | 319 | com.evanlennick 320 | retry4j 321 | ${retry4j.version} 322 | 323 | 324 | com.google.guava 325 | guava 326 | ${guava.version} 327 | 328 | 329 | org.ff4j 330 | ff4j-core 331 | ${ff4j.version} 332 | 333 | 334 | org.ff4j 335 | ff4j-web 336 | ${ff4j.version} 337 | 338 | 339 | io.netty 340 | netty-all 341 | ${netty.version} 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | kr.motd.maven 354 | os-maven-plugin 355 | ${maven-os-plugin.version} 356 | 357 | 358 | 359 | 360 | 361 | 362 | org.apache.maven.plugins 363 | maven-compiler-plugin 364 | ${maven-compiler-plugin.version} 365 | 366 | ${maven-compiler-plugin.source} 367 | ${maven-compiler-plugin.target} 368 | true 369 | 370 | 371 | 372 | 373 | org.codehaus.mojo 374 | cassandra-maven-plugin 375 | ${cassandra-maven-plugin.version} 376 | 377 | 378 | 379 | start 380 | flush 381 | cleanup 382 | 383 | compile 384 | 385 | 386 | 387 | 388 | 389 | org.apache.maven.plugins 390 | maven-surefire-plugin 391 | ${maven-surefire-plugin.version} 392 | 393 | 394 | org.junit.platform 395 | junit-platform-surefire-provider 396 | ${junit-platform.version} 397 | 398 | 399 | org.junit.jupiter 400 | junit-jupiter-engine 401 | ${junit-jupiter.version} 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | Apache 2 417 | http://www.apache.org/licenses/LICENSE-2.0.html 418 | repo 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | DataStax 427 | https://datastax.com/ 428 | 429 | 430 | 431 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 🎓🔥 Expose Rest, Graphql, Grpc Data Apis for Your Databases 🔥🎓 2 | 3 | [![License Apache2](https://img.shields.io/hexpm/l/plug.svg)](http://www.apache.org/licenses/LICENSE-2.0) 4 | [![Discord](https://img.shields.io/discord/685554030159593522)](https://discord.com/widget?id=685554030159593522&theme=dark) 5 | 6 | This instructions will lead you to step by step operations for the talks at DeveloperWeek Austin 7 | 8 | ## Table of content 9 | 10 | 1. [Show me some Api Code](https://github.com/datastaxdevs/conference-2021-developer-week-austin/tree/main/sample-code) 11 | 2. [Show me Docker File](https://github.com/datastaxdevs/conference-2021-developer-week-austin/blob/main/sample-docker/docker-compose.yml) 12 | 3. [Show me the SLIDES](https://github.com/datastaxdevs/conference-2021-developer-week-austin/blob/main/slides.pdf) 13 | 3. [Create Astra Instance](#1-create-astra-instance) 14 | 4. [Working with Cassandra](#2-working-with-cassandra) 15 | 5. [Working with REST API](#3-working-with-rest-api) 16 | 6. [Working with DOCUMENT API](#4-working-with-document-api) 17 | 7. [Working with GRAPHQL API](#5-working-with-graphql-api) 18 | 19 | ## 1. Create Astra Instance 20 | 21 | **`ASTRA`** is the simplest way to run Cassandra with zero operations at all - just push the button and get your cluster. No credit card required, $25.00 USD credit every month, roughly 5M writes, 30M reads, 40GB storage monthly - sufficient to run small production workloads. 22 | 23 | ✅ Register (if needed) and Sign In to Astra [https://astra.datastax.com](https://dtsx.io/workshop): You can use your `Github`, `Google` accounts or register with an `email`. 24 | 25 | _Make sure to chose a password with minimum 8 characters, containing upper and lowercase letters, at least one number and special character_ 26 | 27 | ✅ Create a "pay as you go" plan 28 | 29 | Follow this [guide](https://docs.datastax.com/en/astra/docs/creating-your-astra-database.html), to set up a pay as you go database with a free $25 monthly credit. 30 | 31 | - **Select the pay as you go option**: Includes $25 monthly credit - no credit card needed to set up. 32 | 33 | You will find below which values to enter for each field. 34 | 35 | - **For the database name** - `free_db.` While Astra allows you to fill in these fields with values of your own choosing, please follow our recommendations to ensure the application runs properly. 36 | 37 | - **For the keyspace name** - `ks1`. It's really important that you use the name "ks1" for the code to work. 38 | 39 | _You can technically use whatever you want and update the code to reflect the keyspace name. This is really to get you on a happy path for the first run._ 40 | 41 | - **For provider and region**: Choose GCP as a provider. Region is where your database will reside physically (choose one close to you or your users...you may not have a lot of "free" choices). 42 | 43 | - **Create the database**. Review all the fields to make sure they are as shown, and click the `Create Database` button. 44 | 45 | - **Save your secure token details**: It's a good idea to save the auto-generated token details off at this point. You can click the "Copy" icon or download them locally as a JSON file. 46 | 47 | You will see your new database `pending` in the Dashboard. 48 | 49 | ![my-pic](https://github.com/datastaxdevs/shared-assets/blob/master/astra/dashboard-pending-1000-update.png?raw=true) 50 | 51 | The status will change to `Active` when the database is ready, this will only take 2-3 minutes. You will also receive an email when it is ready. 52 | 53 | ## 2. Working with Cassandra 54 | 55 | **✅ Check that our keyspace exist** 56 | 57 | Click your database name, locate the `CQL Console` TAB and enter this first command: 58 | 59 | ```sql 60 | describe keyspaces; 61 | ``` 62 | 63 | **✅ Create Entities** 64 | 65 | ```sql 66 | use ks1; 67 | 68 | CREATE TYPE IF NOT EXISTS video_format ( 69 | width int, 70 | height int 71 | ); 72 | 73 | CREATE TABLE IF NOT EXISTS videos ( 74 | videoid uuid, 75 | title text, 76 | upload timestamp, 77 | email text, 78 | url text, 79 | tags set , 80 | frames list, 81 | formats map >, 82 | PRIMARY KEY (videoid) 83 | ); 84 | 85 | describe ks1; 86 | ``` 87 | 88 | **✅ Use the data model** : 89 | 90 | - Insert value using plain CQL 91 | 92 | ```sql 93 | INSERT INTO videos(videoid, email, title, upload, url, tags, frames, formats) 94 | VALUES(uuid(), 'clu@sample.com', 'sample video', 95 | toTimeStamp(now()), 'http://google.fr', 96 | { 'cassandra','accelerate','2020'}, 97 | [ 1, 2, 3, 4], 98 | { 'mp4':{width:1,height:1},'ogg':{width:1,height:1}}); 99 | 100 | INSERT INTO videos(videoid, email, title, upload, url) 101 | VALUES(uuid(), 'clu@sample.com', 'video2', toTimeStamp(now()), 'http://google.fr'); 102 | ``` 103 | 104 | - Insert Value using JSON 105 | 106 | ```sql 107 | INSERT INTO videos JSON '{ 108 | "videoid":"e466f561-4ea4-4eb7-8dcc-126e0fbfd573", 109 | "email":"clunven@sample.com", 110 | "title":"A Second videos", 111 | "upload":"2020-02-26 15:09:22 +00:00", 112 | "url": "http://google.fr", 113 | "frames": [1,2,3,4], 114 | "tags": [ "cassandra","accelerate", "2020"], 115 | "formats": { 116 | "mp4": {"width":1,"height":1}, 117 | "ogg": {"width":1,"height":1} 118 | } 119 | }'; 120 | ``` 121 | 122 | - Read values 123 | 124 | ```sql 125 | select * from videos; 126 | ``` 127 | 128 | - Read by id 129 | ```sql 130 | select * from videos where videoid=e466f561-4ea4-4eb7-8dcc-126e0fbfd573; 131 | ``` 132 | 133 | [🏠 Back to Table of Contents](#table-of-content) 134 | 135 | ## 3. Working with REST API 136 | 137 | To use the API we will need a token please create a token following the instructions here: 138 | 139 | #### ✅ 3a. Create a token 140 | 141 | Follow the documentation to [create a token for your app](https://docs.datastax.com/en/astra/docs/manage-application-tokens.html). 142 | 143 | Role: `Database Administrator` 144 | 145 | Copy the token value (eg `AstraCS:KDfdKeNREyWQvDpDrBqwBsUB:ec80667c....`) in your clipboard and save the CSV this value would not be provided afterward. 146 | 147 | _If you have your auto-generated token details from before, you should be able to skip this step._ 148 | 149 | **👁️ Expected output** 150 | 151 | ![image](pics/astra-token.png?raw=true) 152 | 153 | Now launch the swagger UI 154 | 155 | - Click on the "Connect" tab at the top, and then on the "REST API" entry in the left nav. 156 | 157 | ![image](pics/stargate-connect-restapi.png?raw=true) 158 | 159 | Scroll down to the "Launching Swagger UI" section, and click the link. 160 | 161 | _Everyone's Swagger UI link will be unique to their instance of Astra DB._ 162 | 163 | ![image](pics/launch-swagger.png?raw=true) 164 | 165 | You can also follow the link to the [REST API Quick Start](https://stargate.io/docs/stargate/1.0/quickstart/quick_start-rest.html) and run Stargate locally. Here we will use the [SwaggerUI](http://localhost:8082/swagger-ui/#/data). 166 | 167 | #### ✅ 3b. List keyspaces 168 | 169 | - Scroll to the "schemas" section of the Swagger spec. 170 | 171 | - For all exercises, remember that we're using the *v2* API version. 172 | 173 | - [`GET: /v2/schemas/keyspaces`](http://localhost:8082/swagger-ui/#/schemas/getAllKeyspaces) 174 | 175 | ![image](https://raw.githubusercontent.com/datastaxdevs/conference-2021-apachecon-stargate/main/pics/swagger-list-keyspace.png?raw=true) 176 | 177 | - Click `Try it out` 178 | - Provide your token in the field `X-Cassandra-Token` 179 | - Click on `Execute` 180 | 181 | #### ✅ 3c. List Tables 182 | 183 | - [GET /v2/schemas/keyspaces/{keyspaceName}/tables](http://localhost:8082/swagger-ui/#/schemas/getAllTables) 184 | 185 | ![image](https://raw.githubusercontent.com/datastaxdevs/conference-2021-apachecon-stargate/main/pics/swagger-list-tables.png?raw=true) 186 | 187 | - Click `Try it out` 188 | - Provide your token in the field `X-Cassandra-Token` 189 | - keyspace: `ks1` 190 | - Click on `Execute` 191 | 192 | #### ✅ 3d. List Types 193 | 194 | - [GET /v2/schemas/keyspaces/{keyspaceName}/types](http://localhost:8082/swagger-ui/#/schemas/findAll) 195 | 196 | ![image](https://raw.githubusercontent.com/datastaxdevs/conference-2021-apachecon-stargate/main/pics/swagger-list-types.png?raw=true) 197 | 198 | - Click `Try it out` 199 | - X-Cassandra-Token: `` 200 | - keyspace: `ks1` 201 | - Click on `Execute` 202 | 203 | #### ✅ 3e Create a Table 204 | 205 | - [POST /v2/schemas/keyspaces/{keyspaceName}/tables](http://localhost:8082/swagger-ui/#/schemas/createTable) 206 | 207 | ![image](https://raw.githubusercontent.com/datastaxdevs/conference-2021-apachecon-stargate/main/pics/swagger-create-table.png?raw=true) 208 | 209 | - Click `Try it out` 210 | - X-Cassandra-Token: `` 211 | - keyspace: `ks1` 212 | - Data 213 | ```json 214 | { 215 | "name": "users", 216 | "columnDefinitions": 217 | [ 218 | { 219 | "name": "firstname", 220 | "typeDefinition": "text" 221 | }, 222 | { 223 | "name": "lastname", 224 | "typeDefinition": "text" 225 | }, 226 | { 227 | "name": "email", 228 | "typeDefinition": "text" 229 | }, 230 | { 231 | "name": "color", 232 | "typeDefinition": "text" 233 | } 234 | ], 235 | "primaryKey": 236 | { 237 | "partitionKey": ["firstname"], 238 | "clusteringKey": ["lastname"] 239 | }, 240 | "tableOptions": 241 | { 242 | "defaultTimeToLive": 0, 243 | "clusteringExpression": 244 | [{ "column": "lastname", "order": "ASC" }] 245 | } 246 | } 247 | ``` 248 | 249 | **👁️ Expected output** 250 | 251 | ```json 252 | { 253 | "name": "users" 254 | } 255 | ``` 256 | 257 | #### ✅ 3f. Insert Rows 258 | 259 | *Notice for the DML you scroll to the `data` section. Make sure you are using url with `V2`, `V1` would also work but this is NOT the same payload.* 260 | 261 | - [POST /v2/keyspaces/{keyspaceName}/{tableName}](http://localhost:8082/swagger-ui/#/data/createRow) 262 | 263 | ![image](https://raw.githubusercontent.com/datastaxdevs/conference-2021-apachecon-stargate/main/pics/swagger-addrows.png?raw=true) 264 | 265 | - X-Cassandra-Token: `` 266 | - keyspaceName: `ks1` 267 | - tableName: `users` 268 | - Data 269 | ```json 270 | { 271 | "firstname": "Cedrick", 272 | "lastname": "Lunven", 273 | "email": "c.lunven@gmail.com", 274 | "color": "blue" 275 | } 276 | ``` 277 | 278 | You can note that the output code is `201` and return your primary key `{ "firstname": "Cedrick","lastname": "Lunven" } 279 | 280 | - You can add a second record changing only the payload 281 | ```json 282 | { 283 | "firstname": "David", 284 | "lastname": "Gilardi", 285 | "email": "d.gilardi@gmail.com", 286 | "color": "blue" 287 | } 288 | ``` 289 | 290 | - Add a third 291 | ```json 292 | { 293 | "firstname": "Kirsten", 294 | "lastname": "Hunter", 295 | "email": "k.hunter@gmail.com", 296 | "color": "pink" 297 | } 298 | ``` 299 | 300 | #### ✅ 3g. Read multiple rows 301 | 302 | - [GET /v2/keyspaces/{keyspaceName}/{tableName}/rows](http://localhost:8082/swagger-ui/#/data/getAllRows_1) 303 | ![image](https://raw.githubusercontent.com/datastaxdevs/conference-2021-apachecon-stargate/main/pics/swagger-listrows.png?raw=true) 304 | 305 | - X-Cassandra-Token: `` 306 | - keyspaceName: `ks1` 307 | - tableName: `users` 308 | - Click Execute 309 | 310 | - Notice how now you can only limited return fields 311 | 312 | - fields: `firstname, lastname` 313 | 314 | #### ✅ 3h. Read a single partition 315 | 316 | - [GET /v2/keyspaces/{keyspaceName}/{tableName}/{primaryKey}](http://localhost:8082/swagger-ui/#/data/getRows_1) 317 | 318 | ![image](https://raw.githubusercontent.com/datastaxdevs/conference-2021-apachecon-stargate/main/pics/swagger-readrows.png?raw=true) 319 | 320 | - X-Cassandra-Token: `` 321 | - keyspaceName: `ks1` 322 | - tableName: `users` 323 | - primaryKey; 'Cedrick` 324 | - Click Execute 325 | 326 | ```diff 327 | - Important: The Swagger user interface is limited as of now and you cannot test a composite key (here adding Lunven). This is a bug in the UI not the API. 328 | ``` 329 | 330 | #### ✅ 3i. Delete a row 331 | 332 | - [DELETE /v2/keyspaces/{keyspaceName}/{tableName}/{primaryKey}](http://localhost:8082/swagger-ui/#/data/deleteRows) 333 | 334 | ![image](https://raw.githubusercontent.com/datastaxdevs/conference-2021-apachecon-stargate/main/pics/swagger-deleterows.png?raw=true) 335 | 336 | - X-Cassandra-Token: `` 337 | - keyspaceName: `ks1` 338 | - tableName: `users` 339 | - primaryKey; 'Cedrick` 340 | - Click Execute 341 | 342 | ## 4. Working with DOCUMENT API 343 | 344 | This walkthrough has been realized using the [Quick Start](https://stargate.io/docs/stargate/1.0/quickstart/quick_start-document.html) 345 | 346 | Scroll to the top to locate the "documents" section in the Swagger UI. 347 | 348 | ![image](pics/swagger-docs.png?raw=true) 349 | 350 | **✅ 4a. List Namespaces** : 351 | 352 | ![image](https://raw.githubusercontent.com/datastaxdevs/conference-2021-apachecon-stargate/main/pics/swagger-doc-listnamespaces.png?raw=true) 353 | 354 | - Fill with Header `X-Cassandra-Token` with `` 355 | 356 | **👁️ Expected output** 357 | ```json 358 | { 359 | "data": [ 360 | { "name": "system_distributed" }, 361 | { "name": "system" }, 362 | { "name": "data_endpoint_auth"}, 363 | { "name": "system_schema"}, 364 | { "name": "ks1" }, 365 | { "name": "stargate_system"}, 366 | { "name": "system_auth" }, 367 | { "name": "system_traces"} 368 | ] 369 | } 370 | ``` 371 | 372 | **✅ 4b. Create a document** : 373 | 374 | *Note: operations requiring providing `namespace` and `collections` on the swagger UI seems not functional. We are switching to CURL the API is working, this is a documentation bug that has been notified to the development team.* 375 | 376 | ![image](https://raw.githubusercontent.com/datastaxdevs/conference-2021-apachecon-stargate/main/pics/swagger-doc-create.png?raw=true) 377 | 378 | - X-Cassandra-Token: `` 379 | - namespaceName: `ks1` 380 | - collectionname: `col1` 381 | - Click `Execute` 382 | 383 | ```json 384 | { 385 | "videoid":"e466f561-4ea4-4eb7-8dcc-126e0fbfd573", 386 | "email":"clunven@sample.com", 387 | "title":"A Second videos", 388 | "upload":"2020-02-26 15:09:22 +00:00", 389 | "url": "http://google.fr", 390 | "frames": [1,2,3,4], 391 | "tags": [ "cassandra","accelerate", "2020"], 392 | "formats": { 393 | "mp4": {"width":1,"height":1}, 394 | "ogg": {"width":1,"height":1} 395 | } 396 | } 397 | ``` 398 | 399 | **👁️ Expected output**: 400 | ```json 401 | { 402 | "documentId":"" 403 | } 404 | ``` 405 | 406 | **✅ 4c. Retrieve documents** : 407 | 408 | ![image](https://raw.githubusercontent.com/datastaxdevs/conference-2021-apachecon-stargate/main/pics/swagger-doc-search.png?raw=true) 409 | 410 | - X-Cassandra-Token: `` 411 | - namespaceName: `ks1` 412 | - collectionname: `col1` 413 | - Click `Execute` 414 | 415 | 416 | **✅ 4d. Retrieve 1 document** : 417 | 418 | ![image](https://raw.githubusercontent.com/datastaxdevs/conference-2021-apachecon-stargate/main/pics/swagger-doc-get.png?raw=true) 419 | 420 | - X-Cassandra-Token: `` 421 | - namespaceName: `ks1` 422 | - collectionname: `col1` 423 | - documentId: `` 424 | - Click `Execute` 425 | 426 | 427 | **✅ 4e. Search for document by properties** : 428 | 429 | ![image](https://raw.githubusercontent.com/datastaxdevs/conference-2021-apachecon-stargate/main/pics/swagger-doc-search.png?raw=true) 430 | 431 | - X-Cassandra-Token: `` 432 | - namespaceName: `ks1` 433 | - collectionname: `col1` 434 | - documentId: `` 435 | - WhereClause 436 | 437 | ```json 438 | {"email": {"$eq": "clunven@sample.com"}} 439 | ``` 440 | 441 | [🏠 Back to Table of Contents](#table-of-content) 442 | 443 | ## 5. Working with GRAPHQL API 444 | 445 | This walkthrough has been realized using the [GraphQL Quick Start](https://stargate.io/docs/stargate/1.0/quickstart/quick_start-graphql.html) 446 | 447 | **✅ Creating a keyspace** : 448 | 449 | Before you can start using the GraphQL API, you must first create a Cassandra keyspace and at least one table in your database. If you are connecting to a Cassandra database with existing schema, you can skip this step. 450 | 451 | ![image](pics/create-keyspace-1.png?raw=true) 452 | 453 | Then provide the keyspace name `library`: 454 | 455 | ![image](pics/create-keyspace-2.png?raw=true) 456 | 457 | **✅ Open GraphQL Playground** : 458 | 459 | Open the playground 460 | ![image](pics/launch-graphql.png?raw=true) 461 | 462 | _Everyone's GraphQL Playground link will be unique to their instance of Astra DB._ 463 | 464 | **👁️ Expected output** 465 | ![image](pics/playground-home.png?raw=true) 466 | 467 | **✅ Creating a Table** : 468 | 469 | - Use this query 470 | ``` 471 | mutation { 472 | books: createTable( 473 | keyspaceName:"library", 474 | tableName:"books", 475 | partitionKeys: [ # The keys required to access your data 476 | { name: "title", type: {basic: TEXT} } 477 | ] 478 | values: [ # The values associated with the keys 479 | { name: "author", type: {basic: TEXT} } 480 | ] 481 | ) 482 | authors: createTable( 483 | keyspaceName:"library", 484 | tableName:"authors", 485 | partitionKeys: [ 486 | { name: "name", type: {basic: TEXT} } 487 | ] 488 | clusteringKeys: [ # Secondary key used to access values within the partition 489 | { name: "title", type: {basic: TEXT}, order: "ASC" } 490 | ] 491 | ) 492 | } 493 | ``` 494 | 495 | **👁️ Expected output** 496 | 497 | ![image](pics/graphql-createtables.png?raw=true) 498 | 499 | **✅ Populating Table** : 500 | 501 | Any of the created APIs can be used to interact with the GraphQL data, to write or read data. 502 | 503 | First, let’s navigate to your new keyspace `library` inside the playground. Change tab to `graphql` and pick url `/graphql/library`. 504 | 505 | - Use this query 506 | ``` 507 | mutation insert2Books { 508 | moby: insertbooks(value: {title:"Moby Dick", author:"Herman Melville"}) { 509 | value { 510 | title 511 | } 512 | } 513 | catch22: insertbooks(value: {title:"Catch-22", author:"Joseph Heller"}) { 514 | value { 515 | title 516 | } 517 | } 518 | } 519 | ``` 520 | 521 | - Don't forget to update the header again 522 | ``` 523 | { 524 | "x-cassandra-token":"7c37bda5-7360-4d39-96bc-9765db5773bc" 525 | } 526 | ``` 527 | **👁️ Expected output** 528 | 529 | ![image](pics/graphql-insertdata.png?raw=true) 530 | 531 | 532 | **✅ Read data** : 533 | 534 | Stay on the same screen and sinmply update the query with 535 | ``` 536 | query oneBook { 537 | books (value: {title:"Moby Dick"}) { 538 | values { 539 | title 540 | author 541 | } 542 | } 543 | } 544 | ``` 545 | 546 | **👁️ Expected output** 547 | 548 | ![image](pics/graphql-readdata.png?raw=true) 549 | 550 | [🏠 Back to Table of Contents](#table-of-content) 551 | 552 | 553 | ## THE END 554 | 555 | Congratulations, you made it to the END! 556 | --------------------------------------------------------------------------------