diff --git a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/README.adoc b/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/README.adoc deleted file mode 100644 index 588688f8..00000000 --- a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/README.adoc +++ /dev/null @@ -1,24 +0,0 @@ -# Cloud Spanner Spring Data R2DBC sample - -This sample creates a table called `BOOK` on application startup, and deletes it prior to application shutdown. - -## Running the Sample - -image:http://gstatic.com/cloudssh/images/open-btn.svg[link=https://ssh.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https%3A%2F%2Fgithub.com%2FGoogleCloudPlatform%2Fcloud-spanner-r2dbc&cloudshell_open_in_editor=cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/README.adoc] - - -1. Run the sample from the command line, providing `spanner.instance`, `spanner.database` and `gcp.project` properties: - -``` -mvn spring-boot:run -Dspring-boot.run.jvmArguments="-Dspanner.instance=[SPANNER-INSTANCE] -Dspanner.database=[SPANNER-DATABASE] -Dgcp.project=GCP-PROJECT" -``` - -2. Visit http://localhost:8080/index.html - -3. Try the different actions available - - - Listing all books. - - Adding a new book with only title. - - Adding a new book with extra details (a `Map` field in `Book` entity) stored as a spanner JSON column. - - Adding a new book with a review (a custom class field in `Book` entity) stored as a spanner JSON column - - Searching for a book by its ID. diff --git a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/pom.xml b/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/pom.xml deleted file mode 100644 index fd6ef193..00000000 --- a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/pom.xml +++ /dev/null @@ -1,131 +0,0 @@ - - - - - org.springframework.boot - spring-boot-starter-parent - 2.7.17 - - - - 4.0.0 - - cloud-spanner-spring-data-r2dbc-sample - com.google.cloud - 1.2.3-SNAPSHOT - - - true - - - - - - com.google.cloud - cloud-spanner-r2dbc - ${project.version} - - - - - org.springframework.data - spring-data-r2dbc - 1.5.17 - - - io.r2dbc - r2dbc-spi - 0.9.0.RELEASE - - - - - - - - com.google.cloud - cloud-spanner-spring-data-r2dbc - ${project.version} - - - - org.springframework.boot - spring-boot-starter-webflux - - - - - org.springframework.boot - spring-boot-starter-test - test - - - io.projectreactor - reactor-test - test - - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - **/*IntegrationTest* - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - - **/*IntegrationTest* - - - - - - integration-tests - - integration-test - verify - - - - - - - - maven-deploy-plugin - - true - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.13 - - true - - - - - - - diff --git a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/Book.java b/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/Book.java deleted file mode 100644 index 28eb2979..00000000 --- a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/Book.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import java.util.List; -import org.springframework.data.annotation.Id; -import org.springframework.data.domain.Persistable; -import org.springframework.data.relational.core.mapping.Column; -import org.springframework.data.relational.core.mapping.Table; -import java.util.Map; -import java.util.UUID; - -/** - * Book entity. - */ -@Table -public class Book implements Persistable { - - @Id - @Column("ID") - private String id; - - @Column("TITLE") - private String title; - - @Column("EXTRADETAILS") - private Map extraDetails; - - @Column("REVIEWS") - private Review review; - - @Column("CATEGORIES") - private List categories; - - public Book(String title, Map extraDetails, Review review) { - this.id = UUID.randomUUID().toString(); - this.title = title; - this.extraDetails = extraDetails; - this.review = review; - } - - public String getId() { - return id; - } - - @Override - public boolean isNew() { - return true; - } - - public String getTitle() { - return this.title; - } - - - public Map getExtraDetails() { - return extraDetails; - } - - public Review getReview() { - return review; - } - - public List getCategories() { - return categories; - } - - public void setCategories(List categories) { - this.categories = categories; - } - - @Override - public String toString() { - return "Book{" + - "id='" + id + '\'' + - ", title='" + title + '\'' + - ", extraDetails=" + (extraDetails == null ? "" : extraDetails.toString()) + - ", categories=" + (categories == null ? "" : categories) + - '}'; - } -} diff --git a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/BookRepository.java b/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/BookRepository.java deleted file mode 100644 index b0204660..00000000 --- a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/BookRepository.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import org.springframework.data.repository.reactive.ReactiveCrudRepository; - -/** - * Spring Data repository for books. - * Query derivation is not supported yet. - */ -interface BookRepository extends ReactiveCrudRepository { - -} diff --git a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/CustomConfiguration.java b/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/CustomConfiguration.java deleted file mode 100644 index f3b20a62..00000000 --- a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/CustomConfiguration.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import com.google.cloud.spanner.r2dbc.v2.JsonWrapper; -import com.google.gson.Gson; -import com.google.gson.JsonParseException; -import io.r2dbc.spi.ConnectionFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.convert.ReadingConverter; -import org.springframework.data.convert.WritingConverter; -import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration; -import org.springframework.data.r2dbc.convert.R2dbcCustomConversions; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.List; - -@Configuration -public class CustomConfiguration extends AbstractR2dbcConfiguration { - - @Autowired - ApplicationContext applicationContext; - - @Override - public ConnectionFactory connectionFactory() { - return null; - } - - @Bean - @Override - public R2dbcCustomConversions r2dbcCustomConversions() { - List> converters = new ArrayList<>(); - converters.add(this.applicationContext.getBean(JsonToReviewsConverter.class)); - converters.add(this.applicationContext.getBean(ReviewsToJsonConverter.class)); - return new R2dbcCustomConversions(getStoreConversions(), converters); - } - - @Component - @ReadingConverter - public class JsonToReviewsConverter implements Converter { - - private final Gson gson; - - @Autowired - public JsonToReviewsConverter(Gson gson) { - this.gson = gson; - } - - @Override - public Review convert(JsonWrapper json) { - try { - return this.gson.fromJson(json.toString(), Review.class); - } catch (JsonParseException e) { - return new Review(); - } - } - } - - @Component - @WritingConverter - public class ReviewsToJsonConverter implements Converter { - - private final Gson gson; - - @Autowired - public ReviewsToJsonConverter(Gson gson) { - this.gson = gson; - } - - @Override - public JsonWrapper convert(Review source) { - return JsonWrapper.of(this.gson.toJson(source)); - } - } - -} diff --git a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/Review.java b/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/Review.java deleted file mode 100644 index 82841953..00000000 --- a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/Review.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import com.google.common.base.Objects; - -public class Review { - String reviewerId; - String reviewerContent; - - public String getReviewerId() { - return reviewerId; - } - - public void setReviewerId(String reviewerId) { - this.reviewerId = reviewerId; - } - - public String getReviewerContent() { - return reviewerContent; - } - - public void setReviewerContent(String reviewerContent) { - this.reviewerContent = reviewerContent; - } - - public Review() { - } - - public Review(String reviewerId, String reviewerContent) { - this.reviewerId = reviewerId; - this.reviewerContent = reviewerContent; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Review review = (Review) o; - return Objects.equal(reviewerId, review.reviewerId) && Objects.equal(reviewerContent, review.reviewerContent); - } - - @Override - public int hashCode() { - return Objects.hashCode(reviewerId, reviewerContent); - } -} diff --git a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/SpringDataR2dbcApp.java b/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/SpringDataR2dbcApp.java deleted file mode 100644 index 56864b9d..00000000 --- a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/SpringDataR2dbcApp.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import static com.google.cloud.spanner.r2dbc.SpannerConnectionFactoryProvider.INSTANCE; -import static io.r2dbc.spi.ConnectionFactoryOptions.DATABASE; -import static org.springframework.web.reactive.function.server.RequestPredicates.GET; -import static org.springframework.web.reactive.function.server.RouterFunctions.route; - -import java.net.URI; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.annotation.Bean; -import org.springframework.context.event.ContextClosedEvent; -import org.springframework.context.event.EventListener; -import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; -import org.springframework.r2dbc.core.DatabaseClient; -import org.springframework.util.Assert; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.ServerResponse; -import reactor.core.publisher.Hooks; - -/** - * Driver application showing Cloud Spanner R2DBC use with Spring Data. - */ -@SpringBootApplication -@EnableR2dbcRepositories -public class SpringDataR2dbcApp { - - private static final Logger LOGGER = LoggerFactory.getLogger(SpringDataR2dbcApp.class); - - private static final String SPANNER_INSTANCE = System.getProperty("spanner.instance"); - - private static final String SPANNER_DATABASE = System.getProperty("spanner.database"); - - private static final String GCP_PROJECT = System.getProperty("gcp.project"); - - @Autowired - private DatabaseClient r2dbcClient; - - public static void main(String[] args) { - Hooks.onOperatorDebug(); - Assert.notNull(INSTANCE, "Please provide spanner.instance property"); - Assert.notNull(DATABASE, "Please provide spanner.database property"); - Assert.notNull(GCP_PROJECT, "Please provide gcp.project property"); - - SpringApplication.run(SpringDataR2dbcApp.class, args); - } - - @EventListener(ApplicationReadyEvent.class) - public void setUpData() { - LOGGER.info("Setting up test table BOOK..."); - try { - r2dbcClient - .sql( - "CREATE TABLE BOOK (" - + " ID STRING(36) NOT NULL," - + " TITLE STRING(MAX) NOT NULL," - + " EXTRADETAILS JSON," - + " REVIEWS JSON," - + " CATEGORIES ARRAY" - + ") PRIMARY KEY (ID)") - .fetch() - .rowsUpdated() - .block(); - - } catch (Exception e) { - LOGGER.info("Failed to set up test table BOOK", e); - return; - } - LOGGER.info("Finished setting up test table BOOK"); - LOGGER.info("App Started..visit http://localhost:8080/index.html"); - } - - @EventListener({ContextClosedEvent.class}) - public void tearDownData() { - LOGGER.info("Deleting test table BOOK..."); - try { - r2dbcClient.sql("DROP TABLE BOOK") - .fetch().rowsUpdated().block(); - } catch (Exception e) { - LOGGER.info("Failed to delete test table BOOK", e); - return; - } - - LOGGER.info("Finished deleting test table BOOK."); - } - - @Bean - public RouterFunction indexRouter() { - // Serve static index.html at root. - return route( - GET("/"), - req -> ServerResponse.permanentRedirect(URI.create("/index.html")).build()); - } -} diff --git a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/WebController.java b/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/WebController.java deleted file mode 100644 index 16c7e24e..00000000 --- a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/java/com/example/WebController.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2019-2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.UUID; -import com.google.common.base.Splitter; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -/** - * Provides HTTP endpoints for manipulating the BOOK table. - * - * {@code /list} Returns all books in the table (GET). - * {@code /add} Adds a new book with a given title and a generated UUID as {@code id} (POST). - * {@code /search/\{id\}} Finds a single book by its ID. - * - */ -@RestController -public class WebController { - - @Autowired - private R2dbcEntityTemplate r2dbcEntityTemplate; - - @Autowired - private BookRepository r2dbcRepository; - - @GetMapping("/list") - public Flux listBooks() { - return r2dbcEntityTemplate - .select(Book.class) - .all(); - } - - @PostMapping("/add") - public Mono addBook(@RequestBody Book book) { - return r2dbcEntityTemplate - .insert(Book.class) - .using(book) - .log() - .then(); - } - - /** - * For test cleanup. - */ - @GetMapping("/delete-all") - public Mono deleteAll() { - return r2dbcRepository.findAll().flatMap(x -> r2dbcRepository.delete(x)).log().then(); - } - - @GetMapping("/search/{id}") - public Mono searchBooks(@PathVariable String id) { - return r2dbcRepository.findById(id); - } - -} diff --git a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/resources/application.properties b/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/resources/application.properties deleted file mode 100644 index e952e083..00000000 --- a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/resources/application.properties +++ /dev/null @@ -1,4 +0,0 @@ -spring.r2dbc.url=\ -r2dbc:cloudspanner://spanner.googleapis.com:443/projects/${gcp.project}/instances/${spanner.instance}/databases/${spanner.database} - -logging.level.org.springframework.r2dbc=DEBUG \ No newline at end of file diff --git a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/resources/static/index.html b/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/resources/static/index.html deleted file mode 100644 index d8aa50ef..00000000 --- a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/main/resources/static/index.html +++ /dev/null @@ -1,155 +0,0 @@ - - - Spring Data R2DBC for Cloud Spanner demo - - - - - - - - - - - - List Books - - - - - Required Field - - New Book Title: - - - Extra details (Map entity field turning into JSON Spanner column) - - Rating: Is series: - - - Review (custom Review object entity field turning into JSON Spanner column) - - Reviewer: Review: - - - Comma-separated categories (List entity field turning into ARRAY Spanner column) - - Categories: - - - Add Book - - - - - Search for: Find - Books - - - -Command Output - - - - - - - - diff --git a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/test/java/com/example/SpringDataR2dbcAppIntegrationTest.java b/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/test/java/com/example/SpringDataR2dbcAppIntegrationTest.java deleted file mode 100644 index 4c1d136f..00000000 --- a/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample/src/test/java/com/example/SpringDataR2dbcAppIntegrationTest.java +++ /dev/null @@ -1,141 +0,0 @@ -package com.example; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.cloud.ServiceOptions; -import java.util.concurrent.atomic.AtomicReference; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; -import org.springframework.test.web.reactive.server.WebTestClient; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -class SpringDataR2dbcAppIntegrationTest { - - @DynamicPropertySource - static void registerProperties(DynamicPropertyRegistry registry) { - registry.add("gcp.project", () -> System.getProperty("gcp.project", ServiceOptions.getDefaultProjectId())); - registry.add("spanner.database", () -> System.getProperty("spanner.database","testdb")); - registry.add("spanner.instance", () -> System.getProperty("spanner.instance", "reactivetest")); - } - - @Autowired - private WebTestClient webTestClient; - - @Autowired - private BookRepository bookRepository; - - @Autowired - private ObjectMapper objectMapper; - - @AfterEach - void deleteRecords() { - this.webTestClient.get().uri("delete-all").exchange().expectStatus().is2xxSuccessful(); - } - - @Test - void tryBasicRepoMethods() { - Book newBook = new Book("Call of the wild", null, null); - bookRepository.save(newBook).block(); - Book newBook2 = new Book("War and Peace", null, null); - bookRepository.save(newBook2).block(); - - StepVerifier.create(bookRepository.findById(newBook.getId())) - .expectNextCount(1L) - .verifyComplete(); - - StepVerifier.create(bookRepository.findAll()).expectNextCount(2L).verifyComplete(); - - StepVerifier.create(bookRepository.count()).expectNext(2L).verifyComplete(); - } - - @Test - void testBasicWebEndpoints() throws JsonProcessingException { - - // initially empty table - this.webTestClient.get().uri("/list").exchange() - .expectBody(Book[].class).isEqualTo(new Book[0]); - - Book newBook = new Book("Call of the wild", null, null); - this.webTestClient.post() - .uri("/add") - .contentType(MediaType.APPLICATION_JSON) - .body(Mono.just(objectMapper.writeValueAsString(newBook)), String.class) - .exchange() - .expectStatus() - .is2xxSuccessful(); - - AtomicReference id = new AtomicReference<>(); - this.webTestClient.get().uri("/list").exchange() - .expectBody(Book[].class).value(books -> { - assertThat(books).hasSize(1); - assertThat(books[0].getTitle()).isEqualTo("Call of the wild"); - id.set(books[0].getId()); - }); - - assertThat(id).doesNotHaveValue(""); - - this.webTestClient.get().uri("/search/" +id.get()).exchange() - .expectBody(Book.class).value(book -> { - assertThat(book.getTitle()).isEqualTo("Call of the wild"); - }); - } - - @Test - void testJsonWebEndpoints() { - this.webTestClient - .post() - .uri("/add") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .body( - Mono.just( - "{\"title\":\"Call of the wild II\",\"extraDetails\":" - + "{\"rating\":\"8\",\"series\":\"yes\"},\"review\":null}"), - String.class) - .exchange() - .expectStatus() - .is2xxSuccessful(); - - this.webTestClient - .post() - .uri("/add") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .body( - Mono.just( - "{\"title\":\"Call of the wild III\",\"extraDetails\":null," - + "\"review\":{\"reviewerId\":\"John\",\"reviewerContent\":\"Good read.\"}}"), - String.class) - .exchange() - .expectStatus() - .is2xxSuccessful(); - - this.webTestClient - .get() - .uri("/list") - .exchange() - .expectBody(Book[].class) - .value( - books -> { - assertThat(books).hasSize(2); - for (Book book : books) { - if (book.getTitle().equals("Call of the wild II")) { - assertThat(book.getExtraDetails()).containsEntry("rating", "8"); - assertThat(book.getExtraDetails()).containsEntry("series", "yes"); - } - if (book.getTitle().equals("Call of the wild III")) { - assertThat(book.getReview()).isEqualTo(new Review("John", "Good read.")); - } - } - }); - } -} diff --git a/cloud-spanner-r2dbc-samples/pom.xml b/cloud-spanner-r2dbc-samples/pom.xml index ea95aa7e..880307ac 100644 --- a/cloud-spanner-r2dbc-samples/pom.xml +++ b/cloud-spanner-r2dbc-samples/pom.xml @@ -20,7 +20,6 @@ cloud-spanner-r2dbc-sample - cloud-spanner-spring-data-r2dbc-sample diff --git a/cloud-spanner-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryResult.java b/cloud-spanner-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryResult.java index 4e896e2e..3775e89c 100644 --- a/cloud-spanner-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryResult.java +++ b/cloud-spanner-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryResult.java @@ -31,18 +31,17 @@ class SpannerClientLibraryResult implements Result { private final Flux resultRows; - private final int numRowsUpdated; + private final long numRowsUpdated; private RowMetadata rowMetadata; - public SpannerClientLibraryResult( - Flux resultRows, int numRowsUpdated) { + public SpannerClientLibraryResult(Flux resultRows, long numRowsUpdated) { this.resultRows = Assert.requireNonNull(resultRows, "A non-null flux of rows is required."); this.numRowsUpdated = numRowsUpdated; } @Override - public Publisher getRowsUpdated() { + public Publisher getRowsUpdated() { return Mono.just(this.numRowsUpdated); } diff --git a/cloud-spanner-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryRowMetadata.java b/cloud-spanner-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryRowMetadata.java index 88973c46..da8d6c93 100644 --- a/cloud-spanner-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryRowMetadata.java +++ b/cloud-spanner-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryRowMetadata.java @@ -81,7 +81,6 @@ public List extends ColumnMetadata> getColumnMetadatas() { return this.columnMetadatas; } - @Override public Collection getColumnNames() { return this.columnNames; } diff --git a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/ClientLibraryBasedIntegrationTest.java b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/ClientLibraryBasedIntegrationTest.java index 266ff3b1..2d5adb6e 100644 --- a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/ClientLibraryBasedIntegrationTest.java +++ b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/ClientLibraryBasedIntegrationTest.java @@ -148,7 +148,7 @@ void testMetadata() { .bind("price", new BigDecimal("123.99")) .execute()) .flatMapMany(rs -> rs.getRowsUpdated()) - ).expectNext(1).verifyComplete(); + ).expectNext(1L).verifyComplete(); StepVerifier.create( Mono.from(conn.createStatement( @@ -200,7 +200,7 @@ void testDmlInsert() { ) .execute()) .flatMapMany(rs -> rs.getRowsUpdated()) - ).expectNext(1).verifyComplete(); + ).expectNext(1L).verifyComplete(); StepVerifier.create( Mono.from(conn.createStatement( @@ -251,7 +251,7 @@ void testJsonFieldInsert() { .bind("extra", JsonWrapper.of("{\"b\":9,\"a\":true}")) .execute()) .flatMapMany(rs -> rs.getRowsUpdated())) - .expectNext(1) + .expectNext(1L) .verifyComplete(); StepVerifier.create( @@ -303,7 +303,7 @@ void testTransactionSingleStatementCommitted() { ).flatMap(r -> r.getRowsUpdated()), c.commitTransaction(), c.close())) - ).expectNext(1).verifyComplete(); + ).expectNext(1L).verifyComplete(); StepVerifier.create( Mono.from(connectionFactory.create()) @@ -344,7 +344,7 @@ void testTransactionMultipleStatementsCommitted() { )) - ).expectNext(1, 1, 2).verifyComplete(); + ).expectNext(1L, 1L, 2L).verifyComplete(); StepVerifier.create( Mono.from(connectionFactory.create()) @@ -378,7 +378,7 @@ void testTransactionFollowedByStandaloneStatementCommitted() { ).flatMap(r -> r.getRowsUpdated()) )) - ).expectNext(1, 1).verifyComplete(); + ).expectNext(1L, 1L).verifyComplete(); verifyIds(uuid1, uuid2); } @@ -396,7 +396,7 @@ void testTransactionRolledBack() { .execute() ).flatMap(r -> r.getRowsUpdated()), c.rollbackTransaction())) - ).expectNext(1).verifyComplete(); + ).expectNext(1L).verifyComplete(); StepVerifier.create( Mono.from(connectionFactory.create()) @@ -429,7 +429,7 @@ void selectQueryReturnsUpdatedDataDuringAndAfterTransactionCommit() { c.commitTransaction() )) - ).expectNext(1, uuid1).verifyComplete(); + ).expectNext(1L, uuid1).verifyComplete(); verifyIds(uuid1); } @@ -453,7 +453,7 @@ void selectQueryReturnsUpdatedDataDuringTransactionButNotAfterTransactionRollbac c.rollbackTransaction() )) - ).expectNext(1, uuid1).verifyComplete(); + ).expectNext(1L, uuid1).verifyComplete(); // no data verifyIds(); @@ -481,7 +481,7 @@ void selectMultipleBoundParameterSetsNoTransaction() { ).flatMap(r -> r.getRowsUpdated()) )) - ).expectNext(1, 1, 1).verifyComplete(); + ).expectNext(1L, 1L, 1L).verifyComplete(); StepVerifier.create( Mono.from(connectionFactory.create()).flatMapMany( @@ -527,7 +527,7 @@ void selectMultipleBoundParameterSetsInTransaction() { ).flatMap(r -> r.getRowsUpdated()) )) - ).expectNext(1, 1, 1).verifyComplete(); + ).expectNext(1L, 1L, 1L).verifyComplete(); StepVerifier.create( Mono.from(connectionFactory.create()).flatMapMany( @@ -570,7 +570,7 @@ void insertMultipleBoundParameterSetsNoTransaction() { .execute() ) .flatMap(rs -> rs.getRowsUpdated())) - ).expectNext(1, 1, 1).as("Row insert count matches").verifyComplete(); + ).expectNext(1L, 1L, 1L).as("Row insert count matches").verifyComplete(); StepVerifier.create( Mono.from(connectionFactory.create()) @@ -605,7 +605,7 @@ void insertMultipleBoundParameterSetsInTransaction() { conn.commitTransaction() ) ) - ).expectNext(1, 1, 1).as("Row insert count matches").verifyComplete(); + ).expectNext(1L, 1L, 1L).as("Row insert count matches").verifyComplete(); StepVerifier.create( Mono.from(connectionFactory.create()) @@ -643,7 +643,7 @@ void testStaleRead() throws InterruptedException { Flux.from(readStatement.execute()).flatMap(result -> result.map((row, rm) -> row.get(0))), conn.commitTransaction(), Flux.from(readStatement.execute()).flatMap(result -> result.map((row, rm) -> row.get(0))) - )).expectNext(1) + )).expectNext(1L) .as("row inserted") .expectNext(1L) .as("strong read returns the inserted row without a transaction") @@ -674,7 +674,7 @@ void testStrongReadFromSubclassedConnection() throws InterruptedException { ) )).expectNext(0L) .as("empty table; nothing has been inserted yet") - .expectNext(1) + .expectNext(1L) .as("row inserted") .expectNext(1L) .as("strong read returns the inserted row without a transaction") @@ -787,7 +787,7 @@ void testBatchDml() { .add(String.format("UPDATE %s SET CATEGORY=17 WHERE CATEGORY=29", BOOKS_TABLE)) .execute() ).flatMap(r -> r.getRowsUpdated()) - ).expectNext(1, 1, 2) + ).expectNext(1L, 1L, 2L) .verifyComplete(); verifyIds(uuid1, uuid2); diff --git a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/ClientLibraryDdlIntegrationTest.java b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/ClientLibraryDdlIntegrationTest.java index bf0e46f5..9f6f20e4 100644 --- a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/ClientLibraryDdlIntegrationTest.java +++ b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/ClientLibraryDdlIntegrationTest.java @@ -79,7 +79,7 @@ void ddlCreateAndDrop() { .execute()) .log("Table" + tableName + " created") .flatMap(res -> Mono.from(res.getRowsUpdated())) - ).expectNext(0).as("DDL execution returns zero affected rows") + ).expectNext(0L).as("DDL execution returns zero affected rows") .verifyComplete(); StepVerifier.create( diff --git a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/SpannerDmlReactiveStreamVerification.java b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/SpannerDmlReactiveStreamVerification.java index 7c9c6cd1..a1eccf24 100644 --- a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/SpannerDmlReactiveStreamVerification.java +++ b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/SpannerDmlReactiveStreamVerification.java @@ -37,7 +37,7 @@ import reactor.core.publisher.Mono; class SpannerDmlReactiveStreamVerification extends - PublisherVerification { + PublisherVerification { private static ConnectionFactory connectionFactory; @@ -73,7 +73,7 @@ public SpannerDmlReactiveStreamVerification() { } @Override - public Publisher createPublisher(long l) { + public Publisher createPublisher(long l) { return Mono.from(connectionFactory.create()) .flatMapMany(conn -> Flux.from(conn.createStatement( @@ -85,7 +85,7 @@ public Publisher createPublisher(long l) { } @Override - public Publisher createFailedPublisher() { + public Publisher createFailedPublisher() { return Mono.from(connectionFactory.create()) .flatMapMany(conn -> conn.createStatement( String.format("UPDATE %s SET bad syntax ", BOOKS_TABLE)).execute()) diff --git a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/SpannerQueryUtil.java b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/SpannerQueryUtil.java index 6df711d9..64190f97 100644 --- a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/SpannerQueryUtil.java +++ b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/it/SpannerQueryUtil.java @@ -43,10 +43,10 @@ static List executeReadQuery( /** * Executes a DML query and returns the rows updated. */ - static int executeDmlQuery(Connection connection, String sql) { + static long executeDmlQuery(Connection connection, String sql) { Mono.from(connection.beginTransaction()).block(); - int rowsUpdated = Mono.from(connection.createStatement(sql).execute()) + long rowsUpdated = Mono.from(connection.createStatement(sql).execute()) .flatMap(result -> Mono.from(result.getRowsUpdated())) .block(); Mono.from(connection.commitTransaction()).block(); diff --git a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/AbstractSpannerClientLibraryStatementTest.java b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/AbstractSpannerClientLibraryStatementTest.java index c7aa1d32..9813e6e8 100644 --- a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/AbstractSpannerClientLibraryStatementTest.java +++ b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/AbstractSpannerClientLibraryStatementTest.java @@ -61,7 +61,7 @@ void noParametersSendsSingleStatement() { FakeStatement statement = new FakeStatement(this.mockAdapter, query); StepVerifier.create(Flux.from(statement.execute()).flatMap(r -> r.getRowsUpdated())) - .expectNext(19) + .expectNext(19L) .verifyComplete(); ArgumentCaptor capturedStatement = ArgumentCaptor.forClass(Statement.class); @@ -82,7 +82,7 @@ void singleSetOfParametersWithNoAddSendsOneStatement() { statement.bind("three", "333"); StepVerifier.create(Flux.from(statement.execute()).flatMap(r -> r.getRowsUpdated())) - .expectNext(19) + .expectNext(19L) .verifyComplete(); ArgumentCaptor capturedStatement = ArgumentCaptor.forClass(Statement.class); @@ -108,7 +108,7 @@ void singleSetOfParametersWithAddTriggersBatchWithOneStatement() { statement.add(); StepVerifier.create(Flux.from(statement.execute()).flatMap(r -> r.getRowsUpdated())) - .expectNext(7, 11, 13) + .expectNext(7L, 11L, 13L) .verifyComplete(); ArgumentCaptor> params = ArgumentCaptor.forClass(List.class); @@ -139,7 +139,7 @@ void twoParameterSetsWithNoTrailingAddSendsTwoStatements() { statement.bind("three", "B333"); StepVerifier.create(Flux.from(statement.execute()).flatMap(r -> r.getRowsUpdated())) - .expectNext(7, 11, 13) + .expectNext(7L, 11L, 13L) .verifyComplete(); ArgumentCaptor> params = ArgumentCaptor.forClass(List.class); @@ -175,7 +175,7 @@ void twoParameterSetsWithTrailingAddSendsTwoStatements() { statement.add(); StepVerifier.create(Flux.from(statement.execute()).flatMap(r -> r.getRowsUpdated())) - .expectNext(7, 11, 13) + .expectNext(7L, 11L, 13L) .verifyComplete(); ArgumentCaptor> params = ArgumentCaptor.forClass(List.class); diff --git a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/DatabaseClientReactiveAdapterTest.java b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/DatabaseClientReactiveAdapterTest.java index 2e231818..71917014 100644 --- a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/DatabaseClientReactiveAdapterTest.java +++ b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/DatabaseClientReactiveAdapterTest.java @@ -283,7 +283,7 @@ void runBatchDml() { this.adapter.runBatchDml( ImmutableList.of(Statement.of("UPDATE something"), Statement.of("UPDATE something else"))) .flatMap(result -> result.getRowsUpdated()) - ).expectNext(42, 81) + ).expectNext(42L, 81L) .verifyComplete(); } @@ -299,7 +299,7 @@ void runBatchDml_rowCountAboveMaxIntIsTruncated() { this.adapter.runBatchDml( ImmutableList.of(Statement.of("UPDATE something"))) .flatMap(result -> result.getRowsUpdated()) - ).expectNext(Integer.MAX_VALUE) + ).expectNext(Long.valueOf(Integer.MAX_VALUE)) .verifyComplete(); } } diff --git a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerBatchTest.java b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerBatchTest.java index dec84f7c..14a37f49 100644 --- a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerBatchTest.java +++ b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerBatchTest.java @@ -78,7 +78,7 @@ void batchPassesCorrectQueriesToAdapter() { batch.add("UPDATE tbl SET col1=val1") .add("UPDATE tbl SET col2=val2").execute() ).flatMap(r -> r.getRowsUpdated()) - ).expectNext(35, 47) + ).expectNext(35L, 47L) .verifyComplete(); ArgumentCaptor> argCaptor = ArgumentCaptor.forClass(List.class); diff --git a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryConnectionTest.java b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryConnectionTest.java index 467c187c..dbeed991 100644 --- a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryConnectionTest.java +++ b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryConnectionTest.java @@ -71,7 +71,7 @@ void batchUsesCorrectAdapter() { Flux.from( batch.add("UPDATE tbl SET col1=val1").execute() ).flatMap(r -> r.getRowsUpdated()) - ).expectNext(35) + ).expectNext(35L) .verifyComplete(); ArgumentCaptor> argCaptor = ArgumentCaptor.forClass(List.class); diff --git a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryDdlStatementTest.java b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryDdlStatementTest.java index 37b31abc..4cf75284 100644 --- a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryDdlStatementTest.java +++ b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryDdlStatementTest.java @@ -59,7 +59,7 @@ void executeDdlAffectsZeroRows() { StepVerifier.create( statement.execute().flatMap((Result r) -> Mono.from(r.getRowsUpdated())) - ).expectNext(0) + ).expectNext(0L) .verifyComplete(); } diff --git a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryDmlStatementTest.java b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryDmlStatementTest.java index a6708627..c129b2dc 100644 --- a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryDmlStatementTest.java +++ b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryDmlStatementTest.java @@ -48,7 +48,7 @@ void executeSingleNoRowsUpdated() { StepVerifier.create( Flux.from(dmlStatement.execute()).flatMap(result -> result.getRowsUpdated()) ) - .expectNext(0) + .expectNext(0L) .verifyComplete(); } @@ -68,7 +68,7 @@ void executeMultiple() { StepVerifier.create( Flux.from(dmlStatement.execute()).flatMap(result -> result.getRowsUpdated())) - .expectNext(2, 5) + .expectNext(2L, 5L) .verifyComplete(); } } diff --git a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryResultTest.java b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryResultTest.java index 539fe3dd..7a40c8dd 100644 --- a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryResultTest.java +++ b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryResultTest.java @@ -41,7 +41,7 @@ void nullRowsNotAllowed() { void getRowsUpdatedReturnsCorrectNumber() { SpannerClientLibraryResult result = new SpannerClientLibraryResult(Flux.empty(), 42); StepVerifier.create(result.getRowsUpdated()) - .expectNext(42) + .expectNext(42L) .verifyComplete(); } diff --git a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryStatementTest.java b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryStatementTest.java index 17938148..56f382bf 100644 --- a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryStatementTest.java +++ b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerClientLibraryStatementTest.java @@ -56,7 +56,7 @@ void executeSingleNoRowsUpdated() { StepVerifier.create( Flux.from(statement.execute()).flatMap(result -> result.getRowsUpdated())) - .expectNext(0) + .expectNext(0L) .verifyComplete(); StepVerifier.create( diff --git a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerResultTest.java b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerResultTest.java index 9849759f..0a9c19bb 100644 --- a/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerResultTest.java +++ b/cloud-spanner-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/v2/SpannerResultTest.java @@ -49,8 +49,8 @@ public void setup() { @Test void getRowsUpdatedTest() { StepVerifier.create( - ((Mono) new SpannerClientLibraryResult(this.resultSet, 2).getRowsUpdated())) - .expectNext(2) + ((Mono) new SpannerClientLibraryResult(this.resultSet, 2L).getRowsUpdated())) + .expectNext(2L) .verifyComplete(); } diff --git a/cloud-spanner-spring-data-r2dbc/README.md b/cloud-spanner-spring-data-r2dbc/README.md index 87264392..54e6f993 100644 --- a/cloud-spanner-spring-data-r2dbc/README.md +++ b/cloud-spanner-spring-data-r2dbc/README.md @@ -6,42 +6,5 @@ The goal of the Spring Data project is to create easy and consistent ways of usi ## Setup -To begin using Spring Data R2DBC with Cloud Spanner, add the following dependencies to your project: - -```xml - - - - org.springframework.boot.experimental - spring-boot-starter-data-r2dbc - ${r2dbc-version} - - - - - com.google.cloud - cloud-spanner-spring-data-r2dbc - ${cloud-spanner-r2dbc-version} - - -``` - -## Overview - -Spring Data R2DBC allows you to use the convenient features of Spring Data in a reactive application. -These features include: - -* Spring configuration support using Java based `@Configuration` classes. -* Annotation based mapping metadata. -* Automatic implementation of Repository interfaces. -* Support for Reactive Transactions. -* Schema and data initialization utilities. - -See the [Spring Data R2DBC documentation](https://docs.spring.io/spring-data/r2dbc/docs/1.0.x/reference/html/#reference) for more information on how to use Spring Data R2DBC. - -## Sample Application - -We provide a [sample application](https://github.com/GoogleCloudPlatform/cloud-spanner-r2dbc/tree/main/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample) which demonstrates using the Spring Data R2DBC framework with Cloud Spanner in [Spring WebFlux](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html). +This module has moved to [spring-cloud-gcp](https://github.com/GoogleCloudPlatform/spring-cloud-gcp). +Please follow [this document](https://github.com/GoogleCloudPlatform/spring-cloud-gcp/blob/main/docs/src/main/asciidoc/spanner-spring-data-r2dbc.adoc) for setting it up. diff --git a/cloud-spanner-spring-data-r2dbc/pom.xml b/cloud-spanner-spring-data-r2dbc/pom.xml deleted file mode 100644 index 6f41c36c..00000000 --- a/cloud-spanner-spring-data-r2dbc/pom.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - cloud-spanner-r2dbc-parent - com.google.cloud - 1.2.3-SNAPSHOT - - 4.0.0 - - Cloud Spanner R2DBC Spring Data Dialect - cloud-spanner-spring-data-r2dbc - - - 1.5.17 - 5.3.30 - - - - - com.google.cloud - cloud-spanner-r2dbc - ${project.version} - - - - org.springframework.data - spring-data-r2dbc - ${spring-data-r2dbc.version} - - - - org.springframework - spring-test - ${spring-test.version} - test - - - - - - diff --git a/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/JsonToMapConverter.java b/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/JsonToMapConverter.java deleted file mode 100644 index 0e384b33..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/JsonToMapConverter.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2021-2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata; - -import com.google.cloud.spanner.r2dbc.v2.JsonWrapper; -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; -import java.util.Map; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.convert.ReadingConverter; - -/** JsonWrapper to Map converter. */ -@ReadingConverter -public class JsonToMapConverter implements Converter> { - - private final Gson gson; - - @Autowired - public JsonToMapConverter(Gson gson) { - this.gson = gson; - } - - @Override - public Map convert(JsonWrapper json) throws JsonSyntaxException { - return this.gson.fromJson(json.toString(), Map.class); - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/MapToJsonConverter.java b/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/MapToJsonConverter.java deleted file mode 100644 index d580410d..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/MapToJsonConverter.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2021-2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata; - -import com.google.cloud.spanner.r2dbc.v2.JsonWrapper; -import com.google.gson.Gson; -import java.util.Map; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.convert.WritingConverter; - -/** - * Map to JsonWrapper Converter. - */ -@WritingConverter -public class MapToJsonConverter implements Converter, JsonWrapper> { - - private final Gson gson; - - @Autowired - public MapToJsonConverter(Gson gson) { - this.gson = gson; - } - - @Override - public JsonWrapper convert(Map source) { - return JsonWrapper.of(this.gson.toJson(source)); - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/SpannerArrayColumns.java b/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/SpannerArrayColumns.java deleted file mode 100644 index c0bcf507..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/SpannerArrayColumns.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2022-2022 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata; - -import org.springframework.data.relational.core.dialect.ArrayColumns; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; - -class SpannerArrayColumns implements ArrayColumns { - - @Override - public boolean isSupported() { - return true; - } - - @Override - public Class> getArrayType(Class> userType) { - Assert.notNull(userType, "Array component type must not be null"); - - return ClassUtils.resolvePrimitiveIfNecessary(userType); - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/SpannerBindMarkerFactoryProvider.java b/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/SpannerBindMarkerFactoryProvider.java deleted file mode 100644 index cd979f25..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/SpannerBindMarkerFactoryProvider.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2021-2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata; - -import com.google.cloud.spanner.r2dbc.SpannerConnectionFactoryMetadata; -import io.r2dbc.spi.ConnectionFactory; -import org.springframework.r2dbc.core.binding.BindMarkersFactory; -import org.springframework.r2dbc.core.binding.BindMarkersFactoryResolver.BindMarkerFactoryProvider; - -/** - * Provides the named bind marker strategy for Cloud Spanner. - */ -public class SpannerBindMarkerFactoryProvider implements BindMarkerFactoryProvider { - - @Override - public BindMarkersFactory getBindMarkers(ConnectionFactory connectionFactory) { - if (SpannerConnectionFactoryMetadata.INSTANCE.equals(connectionFactory.getMetadata())) { - return SpannerR2dbcDialect.NAMED; - } - return null; - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/SpannerR2dbcDialect.java b/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/SpannerR2dbcDialect.java deleted file mode 100644 index 0d546213..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/SpannerR2dbcDialect.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2019-2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata; - -import com.google.cloud.ByteArray; -import com.google.cloud.Date; -import com.google.cloud.Timestamp; -import com.google.cloud.spanner.r2dbc.v2.JsonWrapper; -import com.google.gson.Gson; -import java.util.Arrays; -import java.util.Collection; -import org.springframework.data.r2dbc.dialect.R2dbcDialect; -import org.springframework.data.relational.core.dialect.AbstractDialect; -import org.springframework.data.relational.core.dialect.ArrayColumns; -import org.springframework.data.relational.core.dialect.LimitClause; -import org.springframework.data.relational.core.dialect.LockClause; -import org.springframework.data.relational.core.sql.LockOptions; -import org.springframework.r2dbc.core.binding.BindMarkersFactory; - -/** - * The {@link R2dbcDialect} implementation which enables usage of Spring Data R2DBC with Cloud - * Spanner. - */ -public class SpannerR2dbcDialect extends AbstractDialect implements R2dbcDialect { - static final BindMarkersFactory NAMED = - BindMarkersFactory.named("@", "val", 32); - - public static final String SQL_LIMIT = "LIMIT "; - - private Gson gson = new Gson(); - - private static final LimitClause LIMIT_CLAUSE = new LimitClause() { - @Override - public String getLimit(long limit) { - return SQL_LIMIT + limit; - } - - @Override - public String getOffset(long offset) { - return SQL_LIMIT + Long.MAX_VALUE + " OFFSET " + offset; - } - - @Override - public String getLimitOffset(long limit, long offset) { - return SQL_LIMIT + limit + " OFFSET " + offset; - } - - @Override - public Position getClausePosition() { - return Position.AFTER_ORDER_BY; - } - }; - - /** - * Pessimistic locking is not supported. - * Spanner has a LOCK_SCANNED_RANGES hint, but it appears before SELECT, a position not currently - * supported in LockClause.Position - */ - private static final LockClause LOCK_CLAUSE = new LockClause() { - @Override - public String getLock(LockOptions lockOptions) { - return ""; - } - - @Override - public Position getClausePosition() { - // It does not matter where to append an empty string. - return Position.AFTER_FROM_TABLE; - } - }; - - @Override - public BindMarkersFactory getBindMarkersFactory() { - return NAMED; - } - - @Override - public LimitClause limit() { - return LIMIT_CLAUSE; - } - - @Override - public LockClause lock() { - return LOCK_CLAUSE; - } - - @Override - public Collection extends Class>> getSimpleTypes() { - return Arrays.asList( - JsonWrapper.class, - Timestamp.class, - ByteArray.class, - Date.class); - } - - @Override - public Collection getConverters() { - return Arrays.asList( - new JsonToMapConverter<>(this.gson), - new MapToJsonConverter<>(this.gson)); - } - - @Override - public ArrayColumns getArraySupport() { - return new SpannerArrayColumns(); - } - -} \ No newline at end of file diff --git a/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/SpannerR2dbcDialectProvider.java b/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/SpannerR2dbcDialectProvider.java deleted file mode 100644 index aac2f811..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/main/java/com/google/cloud/spanner/r2dbc/springdata/SpannerR2dbcDialectProvider.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2019-2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata; - -import com.google.cloud.spanner.r2dbc.SpannerConnectionFactoryMetadata; -import io.r2dbc.spi.ConnectionFactory; -import java.util.Optional; -import org.springframework.data.r2dbc.dialect.DialectResolver.R2dbcDialectProvider; -import org.springframework.data.r2dbc.dialect.R2dbcDialect; - -/** - * Provides the {@link SpannerR2dbcDialect} for Spring Data. - */ -public class SpannerR2dbcDialectProvider implements R2dbcDialectProvider { - - @Override - public Optional getDialect(ConnectionFactory connectionFactory) { - if (connectionFactory.getMetadata().getName().equals(SpannerConnectionFactoryMetadata.NAME)) { - return Optional.of(new SpannerR2dbcDialect()); - } - return Optional.empty(); - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/main/resources/META-INF/spring.factories b/cloud-spanner-spring-data-r2dbc/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 295ae067..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,3 +0,0 @@ -org.springframework.data.r2dbc.dialect.DialectResolver$R2dbcDialectProvider=\ -com.google.cloud.spanner.r2dbc.springdata.SpannerR2dbcDialectProvider -org.springframework.r2dbc.core.binding.BindMarkersFactoryResolver$BindMarkerFactoryProvider=com.google.cloud.spanner.r2dbc.springdata.SpannerBindMarkerFactoryProvider diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/MapJsonConverterTest.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/MapJsonConverterTest.java deleted file mode 100644 index 44b72e4b..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/MapJsonConverterTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2020-2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import com.google.cloud.spanner.r2dbc.v2.JsonWrapper; -import com.google.common.collect.ImmutableMap; -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; -import java.util.Map; -import org.junit.jupiter.api.Test; - -/** Test for converters. */ -class MapJsonConverterTest { - private Gson gson = new Gson(); - - @Test - void jsonToMapConverterTest() { - JsonToMapConverter jsonToMapConverter = new JsonToMapConverter(this.gson); - Map resultMap = - jsonToMapConverter.convert( - JsonWrapper.of("{\"a\":\"a string\",\"b\":9, \"c\" : 12.537, \"d\" : true}")); - assertThat(resultMap) - .isInstanceOf(Map.class) - .hasSize(4) - .containsEntry("a", "a string") - .containsEntry("b", 9.0) - .containsEntry("c", 12.537) - .containsEntry("d", true); - - // Convert should fail: duplicate keys not allowed - JsonWrapper invalidJsonToConvert = JsonWrapper.of("{\"a\":\"a string\"," - + " \"a\":\"another string\"}"); - assertThatThrownBy( - () -> jsonToMapConverter.convert(invalidJsonToConvert)) - .isInstanceOf(JsonSyntaxException.class); - } - - @Test - void mapToJsonConverterTest() { - MapToJsonConverter mapToJsonConverter = new MapToJsonConverter(this.gson); - Map mapToConvert = - ImmutableMap.of("a", "a string", "b", 9, "c", 12.537, "d", true); - assertThat(mapToJsonConverter.convert(mapToConvert)) - .isEqualTo(JsonWrapper.of("{\"a\":\"a string\",\"b\":9,\"c\":12.537,\"d\":true}")); - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/SpannerBindMarkerFactoryProviderTest.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/SpannerBindMarkerFactoryProviderTest.java deleted file mode 100644 index 5dfa05b7..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/SpannerBindMarkerFactoryProviderTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2021-2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.google.cloud.spanner.Spanner; -import com.google.cloud.spanner.SpannerOptions; -import com.google.cloud.spanner.r2dbc.SpannerConnectionConfiguration; -import com.google.cloud.spanner.r2dbc.v2.SpannerClientLibraryConnectionFactory; -import io.r2dbc.spi.ConnectionFactory; -import io.r2dbc.spi.ConnectionFactoryMetadata; -import org.junit.jupiter.api.Test; -import org.springframework.r2dbc.core.binding.BindMarkersFactory; - -class SpannerBindMarkerFactoryProviderTest { - - @Test - void spannerBindMarkersFoundForV2ConnectionFactory() { - SpannerBindMarkerFactoryProvider provider = new SpannerBindMarkerFactoryProvider(); - SpannerConnectionConfiguration mockConfig = mock(SpannerConnectionConfiguration.class); - SpannerOptions mockSpannerOptions = mock(SpannerOptions.class); - Spanner mockService = mock(Spanner.class); - - when(mockConfig.buildSpannerOptions()).thenReturn(mockSpannerOptions); - when(mockSpannerOptions.getService()).thenReturn(mockService); - - SpannerClientLibraryConnectionFactory cf = - new SpannerClientLibraryConnectionFactory(mockConfig); - - BindMarkersFactory factory = provider.getBindMarkers(cf); - assertThat(factory).isSameAs(SpannerR2dbcDialect.NAMED); - } - - @Test - void spannerBindMarkersNotFoundForUnknownFactory() { - SpannerBindMarkerFactoryProvider provider = new SpannerBindMarkerFactoryProvider(); - ConnectionFactory cf = mock(ConnectionFactory.class); - ConnectionFactoryMetadata mockMetadata = mock(ConnectionFactoryMetadata.class); - when(cf.getMetadata()).thenReturn(mockMetadata); - when(mockMetadata.getName()).thenReturn("SOME_DATABASE"); - - BindMarkersFactory factory = provider.getBindMarkers(cf); - assertThat(factory).isNull(); - } - -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/SpannerR2dbcDialectTest.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/SpannerR2dbcDialectTest.java deleted file mode 100644 index e4a3de44..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/SpannerR2dbcDialectTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2020-2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.google.cloud.ByteArray; -import com.google.cloud.Date; -import com.google.cloud.Timestamp; -import com.google.cloud.spanner.r2dbc.v2.JsonWrapper; -import java.util.Collection; -import java.util.stream.Stream; -import org.junit.jupiter.api.Test; -import org.springframework.data.mapping.model.SimpleTypeHolder; -import org.springframework.data.relational.core.dialect.LimitClause; -import org.springframework.data.relational.core.dialect.LockClause; -import org.springframework.data.relational.core.sql.LockMode; -import org.springframework.data.relational.core.sql.LockOptions; -import org.springframework.data.relational.core.sql.SQL; -import org.springframework.data.relational.core.sql.Select; -import org.springframework.data.relational.core.sql.Table; -import org.springframework.r2dbc.core.binding.BindMarkers; - -class SpannerR2dbcDialectTest { - - @Test - void testLimitClause() { - LimitClause clause = new SpannerR2dbcDialect().limit(); - assertThat(clause.getOffset(100)).isEqualTo("LIMIT 9223372036854775807 OFFSET 100"); - assertThat(clause.getLimit(42)).isEqualTo("LIMIT 42"); - assertThat(clause.getLimitOffset(42, 100)).isEqualTo("LIMIT 42 OFFSET 100"); - assertThat(clause.getClausePosition()).isSameAs(LimitClause.Position.AFTER_ORDER_BY); - } - - @Test - void testBindMarkersFactory() { - SpannerR2dbcDialect dialect = new SpannerR2dbcDialect(); - BindMarkers bindMarkers = dialect.getBindMarkersFactory().create(); - assertThat(bindMarkers).isNotNull(); - assertThat(bindMarkers.next().getPlaceholder()).isEqualTo("@val0"); - assertThat(bindMarkers.next().getPlaceholder()).isEqualTo("@val1"); - } - - @Test - void lockStringAlwaysEmpty() { - SpannerR2dbcDialect dialect = new SpannerR2dbcDialect(); - Table table = SQL.table("aTable"); - Select sql = Select.builder().select(table.column("aColumn")) - .from(table) - .build(); - LockOptions lockOptions = new LockOptions(LockMode.PESSIMISTIC_READ, sql.getFrom()); - - LockClause lock = dialect.lock(); - - assertNotNull(lock); - assertThat(lock.getLock(lockOptions)).isEmpty(); - assertThat(lock.getClausePosition()).isSameAs(LockClause.Position.AFTER_FROM_TABLE); - } - - @Test - void testSimpleType() { - SpannerR2dbcDialect dialect = new SpannerR2dbcDialect(); - SimpleTypeHolder simpleTypeHolder = dialect.getSimpleTypeHolder(); - assertThat(Stream.of(JsonWrapper.class, Timestamp.class, Date.class, ByteArray.class) - .allMatch(simpleTypeHolder::isSimpleType)).isTrue(); - } - - @Test - void testConverter() { - SpannerR2dbcDialect dialect = new SpannerR2dbcDialect(); - Collection converters = dialect.getConverters(); - assertTrue( - converters.stream() - .anyMatch(converter -> converter.getClass().equals(JsonToMapConverter.class))); - assertTrue( - converters.stream() - .anyMatch(converter -> converter.getClass().equals(MapToJsonConverter.class))); - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerR2dbcDialectDateTimeBindingIntegrationTest.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerR2dbcDialectDateTimeBindingIntegrationTest.java deleted file mode 100644 index a784df34..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerR2dbcDialectDateTimeBindingIntegrationTest.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright 2022-2022 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata.it; - -import static com.google.cloud.spanner.r2dbc.SpannerConnectionFactoryProvider.INSTANCE; -import static com.google.cloud.spanner.r2dbc.SpannerConnectionFactoryProvider.PROJECT; -import static io.r2dbc.spi.ConnectionFactoryOptions.DATABASE; -import static io.r2dbc.spi.ConnectionFactoryOptions.DRIVER; - -import com.google.cloud.Date; -import com.google.cloud.ServiceOptions; -import com.google.cloud.Timestamp; -import com.google.cloud.spanner.r2dbc.springdata.SpannerR2dbcDialect; -import com.google.cloud.spanner.r2dbc.springdata.it.entities.Card; -import io.r2dbc.spi.ConnectionFactories; -import io.r2dbc.spi.ConnectionFactory; -import io.r2dbc.spi.ConnectionFactoryOptions; -import io.r2dbc.spi.Option; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.OffsetDateTime; -import java.util.Arrays; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.extension.ExtendWith; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.convert.ReadingConverter; -import org.springframework.data.convert.WritingConverter; -import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration; -import org.springframework.data.r2dbc.convert.R2dbcCustomConversions; -import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -/** - * Integration tests verifying the {@link Timestamp},{@link Date} support of {@link - * SpannerR2dbcDialect}. - */ -@ExtendWith(SpringExtension.class) -@ContextConfiguration( - classes = SpannerR2dbcDialectDateTimeBindingIntegrationTest.TestConfiguration.class) -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class SpannerR2dbcDialectDateTimeBindingIntegrationTest { - - private static final Logger log = - LoggerFactory.getLogger(SpannerR2dbcDialectDateTimeBindingIntegrationTest.class); - - private static final String PROJECT_NAME = - System.getProperty("gcp.project", ServiceOptions.getDefaultProjectId()); - private static final String DRIVER_NAME = "spanner"; - - private static final String TEST_INSTANCE = - System.getProperty("spanner.instance", "reactivetest"); - - private static final String TEST_DATABASE = System.getProperty("spanner.database", "testdb"); - - private static final ConnectionFactory connectionFactory = - ConnectionFactories.get( - ConnectionFactoryOptions.builder() - .option(Option.valueOf("project"), ServiceOptions.getDefaultProjectId()) - .option(PROJECT, PROJECT_NAME) - .option(DRIVER, DRIVER_NAME) - .option(INSTANCE, TEST_INSTANCE) - .option(DATABASE, TEST_DATABASE) - .build()); - - @Autowired private R2dbcEntityTemplate r2dbcEntityTemplate; - - /** Initializes the integration test environment for the Spanner R2DBC dialect. */ - @BeforeAll - static void initializeTestEnvironment() { - Mono.from(connectionFactory.create()) - .flatMap(c -> Mono.from(c.createStatement("drop table card").execute()) - .doOnSuccess(x -> log.info("Table drop completed.")) - .doOnError( - x -> { - if (!x.getMessage().contains("Table not found")) { - log.info("Table drop failed. {}", x.getMessage()); - } - } - ) - .onErrorResume(x -> Mono.empty()) - .thenReturn(c) - ) - .flatMap(c -> Mono.from(c.createStatement( - "create table card (" - + " id int64 not null," - + " expiry_year int64 not null," - + " expiry_month int64 not null," - + " issue_date date not null," - + " requested_at timestamp not null" - + ") primary key (id)") - .execute()) - .doOnSuccess(x -> log.info("Table creation completed.")) - ).block(); - } - - @AfterAll - static void cleanupTableAfterTest() { - Mono.from(connectionFactory.create()) - .flatMap(c -> Mono.from(c.createStatement("drop table card").execute()) - .doOnSuccess(x -> log.info("Table drop completed.")) - .doOnError(x -> log.info("Table drop failed.")) - ).block(); - } - - @Test - void shouldReadWriteDateAndTimestampTypes() { - Card card = new Card(1L, 2022, 12, - LocalDate.parse("2021-12-31"), - LocalDateTime.parse("2021-12-15T21:30:10")); - - this.r2dbcEntityTemplate - .insert(Card.class) - .using(card) - .then() - .as(StepVerifier::create) - .verifyComplete(); - - this.r2dbcEntityTemplate - .select(Card.class) - .first() - .as(StepVerifier::create) - .expectNextMatches( - c -> - c.getId() == 1L - && c.getExpiryYear() == 2022 - && c.getExpiryMonth() == 12 - && c.getIssueDate().equals(LocalDate.parse("2021-12-31")) - && c.getRequestedAt().equals(LocalDateTime.parse("2021-12-15T21:30:10"))) - .verifyComplete(); - } - - /** Register custom converters. */ - @Configuration - static class TestConfiguration extends AbstractR2dbcConfiguration { - - @Autowired ApplicationContext applicationContext; - - @Override - public ConnectionFactory connectionFactory() { - return connectionFactory; - } - - @Bean - @Override - public R2dbcCustomConversions r2dbcCustomConversions() { - return new R2dbcCustomConversions( - getStoreConversions(), - Arrays.asList( - new DateToLocalDateConverter(), - new LocalDateToDateConverter(), - new LocalDateTimeToTimestampConverter(), - new TimestampToLocalDateTimeConverter())); - } - } - - /** {@link Date} to {@link LocalDate} reading converter. */ - @ReadingConverter - static class DateToLocalDateConverter implements Converter { - - @Override - public LocalDate convert(Date row) { - return LocalDate.parse(row.toString()); - } - } - - /** {@link LocalDate} to {@link Date} writing converter. */ - @WritingConverter - static class LocalDateToDateConverter implements Converter { - - @Override - public Date convert(LocalDate source) { - return Date.parseDate(source.toString()); - } - } - - /** {@link Timestamp} to {@link LocalDateTime} reading converter. */ - @ReadingConverter - static class TimestampToLocalDateTimeConverter implements Converter { - - @Override - public LocalDateTime convert(Timestamp row) { - return OffsetDateTime.parse(row.toString()).toLocalDateTime(); - } - } - - /** {@link LocalDateTime} to {@link Timestamp} writing converter. */ - @WritingConverter - static class LocalDateTimeToTimestampConverter implements Converter { - - @Override - public Timestamp convert(LocalDateTime source) { - return Timestamp.parseTimestamp(source.toString()); - } - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerR2dbcDialectIntegrationTest.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerR2dbcDialectIntegrationTest.java deleted file mode 100644 index 57dc4d9d..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerR2dbcDialectIntegrationTest.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2019-2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata.it; - -import static com.google.cloud.spanner.r2dbc.SpannerConnectionFactoryProvider.INSTANCE; -import static io.r2dbc.spi.ConnectionFactoryOptions.DATABASE; -import static io.r2dbc.spi.ConnectionFactoryOptions.DRIVER; - -import com.google.cloud.ServiceOptions; -import com.google.cloud.spanner.r2dbc.springdata.it.entities.President; -import io.r2dbc.spi.Connection; -import io.r2dbc.spi.ConnectionFactories; -import io.r2dbc.spi.ConnectionFactory; -import io.r2dbc.spi.ConnectionFactoryOptions; -import io.r2dbc.spi.Option; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; -import org.springframework.data.relational.core.query.Query; -import org.springframework.r2dbc.core.DatabaseClient; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -/** - * Integration tests for the Spring Data R2DBC dialect. - * - * By default, the test is configured to run tests in the `reactivetest` instance on the - * `testdb` database. This can be configured by overriding the `spanner.instance` and - * `spanner.database` system properties. - */ -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class SpannerR2dbcDialectIntegrationTest { - - private static final Logger logger = - LoggerFactory.getLogger(SpannerR2dbcDialectIntegrationTest.class); - - private static final String DRIVER_NAME = "spanner"; - - private static final String TEST_INSTANCE = - System.getProperty("spanner.instance", "reactivetest"); - - private static final String TEST_DATABASE = - System.getProperty("spanner.database", "testdb"); - - private static final ConnectionFactory connectionFactory = - ConnectionFactories.get(ConnectionFactoryOptions.builder() - .option(Option.valueOf("project"), ServiceOptions.getDefaultProjectId()) - .option(DRIVER, DRIVER_NAME) - .option(INSTANCE, TEST_INSTANCE) - .option(DATABASE, TEST_DATABASE) - .build()); - - private DatabaseClient databaseClient; - - private R2dbcEntityTemplate r2dbcEntityTemplate; - - /** - * Initializes the integration test environment for the Spanner R2DBC dialect. - */ - @BeforeAll - public void initializeTestEnvironment() { - Connection connection = Mono.from(connectionFactory.create()).block(); - - this.r2dbcEntityTemplate = new R2dbcEntityTemplate(connectionFactory); - this.databaseClient = this.r2dbcEntityTemplate.getDatabaseClient(); - - if (SpannerTestUtils.tableExists(connection, "PRESIDENT")) { - this.databaseClient.sql("DROP TABLE PRESIDENT") - .fetch() - .rowsUpdated() - .block(); - } - - this.databaseClient.sql( - "CREATE TABLE PRESIDENT (" - + " NAME STRING(256) NOT NULL," - + " START_YEAR INT64 NOT NULL" - + ") PRIMARY KEY (NAME)") - .fetch() - .rowsUpdated() - .block(); - } - - @AfterEach - public void cleanupTableAfterTest() { - this.databaseClient - .sql("DELETE FROM PRESIDENT where NAME is not null") - .fetch() - .rowsUpdated() - .block(); - } - - @Test - void testReadWrite() { - insertPresident(new President("Bill Clinton", 1992)); - - this.r2dbcEntityTemplate.select(President.class) - .first() - .as(StepVerifier::create) - .expectNextMatches( - president -> president.getName().equals("Bill Clinton") - && president.getStartYear() == 1992) - .verifyComplete(); - } - - @Test - void testLimitOffsetSupport() { - insertPresident(new President("Bill Clinton", 1992)); - insertPresident(new President("Joe Smith", 1996)); - insertPresident(new President("Bob", 2000)); - insertPresident(new President("Hello", 2004)); - insertPresident(new President("George Washington", 2008)); - - this.r2dbcEntityTemplate.select(President.class) - .matching( - Query.empty() - .sort(Sort.by(Direction.ASC, "name")) - .with(PageRequest.of(0, 2)) - ) - // Get the page at index 1; 2 elements per page. - //.page() - .all() - .as(StepVerifier::create) - .expectNextMatches(president -> president.getName().equals("Bill Clinton")) - .expectNextMatches(president -> president.getName().equals("Bob")) - .verifyComplete(); - } - - @Test - void testRowMap() { - insertPresident(new President("Bill Clinton", 1992)); - insertPresident(new President("Joe Smith", 1996)); - insertPresident(new President("Bob", 2000)); - insertPresident(new President("Hello", 2004)); - insertPresident(new President("George Washington", 2008)); - - this.r2dbcEntityTemplate.select(President.class) - .matching( - Query.empty() - .sort(Sort.by(Direction.ASC, "name"))) - .all() - .map(president -> president.getName()) - .as(StepVerifier::create) - .expectNext( - "Bill Clinton", "Bob", "George Washington", "Hello", "Joe Smith") - .verifyComplete(); - } - - private void insertPresident(President president) { - this.r2dbcEntityTemplate - .insert(President.class) - .using(president) - .then() - .as(StepVerifier::create) - .verifyComplete(); - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerR2dbcDialectJsonIntegrationTest.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerR2dbcDialectJsonIntegrationTest.java deleted file mode 100644 index dfd8b26f..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerR2dbcDialectJsonIntegrationTest.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright 2021-2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata.it; - -import static com.google.cloud.spanner.r2dbc.SpannerConnectionFactoryProvider.INSTANCE; -import static com.google.cloud.spanner.r2dbc.SpannerConnectionFactoryProvider.PROJECT; -import static io.r2dbc.spi.ConnectionFactoryOptions.DATABASE; -import static io.r2dbc.spi.ConnectionFactoryOptions.DRIVER; - -import com.google.cloud.ServiceOptions; -import com.google.cloud.spanner.r2dbc.springdata.it.entities.Address; -import com.google.cloud.spanner.r2dbc.springdata.it.entities.Person; -import com.google.cloud.spanner.r2dbc.v2.JsonWrapper; -import com.google.common.math.DoubleMath; -import com.google.gson.Gson; -import com.google.gson.JsonParseException; -import io.r2dbc.spi.Connection; -import io.r2dbc.spi.ConnectionFactories; -import io.r2dbc.spi.ConnectionFactory; -import io.r2dbc.spi.ConnectionFactoryOptions; -import io.r2dbc.spi.Option; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.convert.ReadingConverter; -import org.springframework.data.convert.WritingConverter; -import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration; -import org.springframework.data.r2dbc.convert.R2dbcCustomConversions; -import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; -import org.springframework.r2dbc.core.DatabaseClient; -import org.springframework.stereotype.Component; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -/** - * Integration tests for the Spring Data R2DBC dialect Json support. - * - * By default, the test is configured to run tests in the `reactivetest` instance on the `testdb` - * database. This can be configured by overriding the `spanner.instance` and `spanner.database` - * system properties. - */ -@ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = SpannerR2dbcDialectJsonIntegrationTest.TestConfiguration.class) -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class SpannerR2dbcDialectJsonIntegrationTest { - - private static final String PROJECT_NAME = - System.getProperty("gcp.project", ServiceOptions.getDefaultProjectId()); - private static final String DRIVER_NAME = "spanner"; - - private static final String TEST_INSTANCE = - System.getProperty("spanner.instance", "reactivetest"); - - private static final String TEST_DATABASE = System.getProperty("spanner.database", "testdb"); - - private static final ConnectionFactory connectionFactory = - ConnectionFactories.get( - ConnectionFactoryOptions.builder() - .option(Option.valueOf("project"), ServiceOptions.getDefaultProjectId()) - .option(PROJECT, PROJECT_NAME) - .option(DRIVER, DRIVER_NAME) - .option(INSTANCE, TEST_INSTANCE) - .option(DATABASE, TEST_DATABASE) - .build()); - - private DatabaseClient databaseClient; - - @Autowired private R2dbcEntityTemplate r2dbcEntityTemplate; - - /** Initializes the integration test environment for the Spanner R2DBC dialect. */ - @BeforeAll - public void initializeTestEnvironment() { - Connection connection = Mono.from(connectionFactory.create()).block(); - - this.databaseClient = this.r2dbcEntityTemplate.getDatabaseClient(); - - if (SpannerTestUtils.tableExists(connection, "PERSON")) { - this.databaseClient.sql("DROP TABLE PERSON").fetch().rowsUpdated().block(); - } - - this.databaseClient - .sql( - "CREATE TABLE PERSON (" - + " NAME STRING(256) NOT NULL," - + " BIRTH_YEAR INT64 NOT NULL," - + " EXTRAS JSON," - + " ADDRESS JSON" - + ") PRIMARY KEY (NAME)") - .fetch() - .rowsUpdated() - .block(); - } - - @AfterEach - public void cleanupTableAfterTest() { - this.databaseClient - .sql("DELETE FROM PERSON where NAME is not null") - .fetch() - .rowsUpdated() - .block(); - } - - private void insertPerson(Person person) { - this.r2dbcEntityTemplate - .insert(Person.class) - .using(person) - .then() - .as(StepVerifier::create) - .verifyComplete(); - } - - @Test - void testReadWriteWithJsonFieldString() { - Map extras = new HashMap<>(); - extras.put("bio", "former U.S. president"); - extras.put("spouse", "Hillary Clinton"); - Person billClinton = new Person("Bill Clinton", 1946, extras, null); - insertPerson(billClinton); - - this.r2dbcEntityTemplate - .select(Person.class) - .first() - .as(StepVerifier::create) - .expectNextMatches( - person -> - person.getName().equals("Bill Clinton") - && person.getBirthYear() == 1946 - && person.getExtras().getOrDefault("spouse", "none").equals("Hillary Clinton") - && person - .getExtras() - .getOrDefault("bio", "none") - .equals("former U.S. president")) - .verifyComplete(); - } - - @Test - void testReadWriteWithJsonFieldBoolean() { - Map extras = new HashMap<>(); - extras.put("male", true); - extras.put("US citizen", true); - Person billClinton = new Person("Bill Clinton", 1946, extras, null); - insertPerson(billClinton); - - this.r2dbcEntityTemplate - .select(Person.class) - .first() - .as(StepVerifier::create) - .expectNextMatches( - person -> - person.getName().equals("Bill Clinton") - && person.getBirthYear() == 1946 - && person.getExtras().getOrDefault("male", false).equals(true) - && person.getExtras().getOrDefault("US citizen", false).equals(true)) - .verifyComplete(); - } - - @Test - void testReadWriteWithJsonFieldDouble() { - Map extras = new HashMap<>(); - extras.put("weight", 144.5); - extras.put("height", 5.916); - Person billClinton = new Person("John Doe", 1946, extras, null); - insertPerson(billClinton); - - this.r2dbcEntityTemplate - .select(Person.class) - .first() - .as(StepVerifier::create) - .expectNextMatches( - person -> - person.getName().equals("John Doe") - && person.getBirthYear() == 1946 - && DoubleMath.fuzzyEquals( - (double) person.getExtras().get("weight"), 144.5, 1e-5) - && DoubleMath.fuzzyEquals( - (double) person.getExtras().get("height"), 5.916, 1e-5)) - .verifyComplete(); - } - - @Test - void testReadWriteWithJsonFieldCustomClass() { - Address address = new Address("home address", "work address", 10012, 10011); - Person billClinton = new Person("Bill Clinton", 1946, null, address); - insertPerson(billClinton); - - this.r2dbcEntityTemplate - .select(Person.class) - .first() - .as(StepVerifier::create) - .expectNextMatches( - person -> - person.getName().equals("Bill Clinton") - && person.getBirthYear() == 1946 - && person.getAddress().equals(address)) - .verifyComplete(); - } - - /** Register custom converters between Map and JsonWrapper. */ - @Configuration - static class TestConfiguration extends AbstractR2dbcConfiguration { - - @Autowired ApplicationContext applicationContext; - - @Override - public ConnectionFactory connectionFactory() { - return connectionFactory; - } - - @Bean - public Gson gson() { - return new Gson(); - } - - @Bean - @Override - public R2dbcCustomConversions r2dbcCustomConversions() { - List> converters = new ArrayList<>(); - converters.add(this.applicationContext.getBean(JsonToReviewsConverter.class)); - converters.add(this.applicationContext.getBean(ReviewsToJsonConverter.class)); - return new R2dbcCustomConversions(getStoreConversions(), converters); - } - - @Component - @ReadingConverter - public class JsonToReviewsConverter implements Converter { - - private final Gson gson; - - @Autowired - public JsonToReviewsConverter(Gson gson) { - this.gson = gson; - } - - @Override - public Address convert(JsonWrapper json) { - try { - return this.gson.fromJson(json.toString(), Address.class); - } catch (JsonParseException e) { - return new Address(); - } - } - } - - @Component - @WritingConverter - public class ReviewsToJsonConverter implements Converter { - - private final Gson gson; - - @Autowired - public ReviewsToJsonConverter(Gson gson) { - this.gson = gson; - } - - @Override - public JsonWrapper convert(Address source) { - try { - return JsonWrapper.of(this.gson.toJson(source)); - } catch (JsonParseException e) { - return JsonWrapper.of(""); - } - } - } - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerTestUtils.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerTestUtils.java deleted file mode 100644 index be82b907..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerTestUtils.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2019-2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata.it; - -import io.r2dbc.spi.Connection; -import reactor.core.publisher.Mono; - -/** - * Helper functions for Spanner integration testing. - */ -public class SpannerTestUtils { - - /** - * Returns true if the Spanner table exists; false if not. - */ - public static boolean tableExists(Connection connection, String tableName) { - return Mono.from(connection.createStatement( - "SELECT table_name FROM information_schema.tables WHERE table_name = @name") - .bind("name", tableName) - .execute()) - .flatMapMany(result -> result.map((r, m) -> r)) - .hasElements() - .block(); - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Address.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Address.java deleted file mode 100644 index b3d6d96b..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Address.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2021-2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata.it.entities; - -import com.google.common.base.Objects; - -/** - * Example custom class entity field. - */ -public class Address { - String homeAddress; - String workAddress; - Integer homeZipCode; - Integer workZipCode; - - public Address() {} - - /** - * Constructor. - * - * @param homeAddress home address - * @param workAddress work address - * @param homeZipCode home zip code - * @param workZipCode work zip code - */ - public Address(String homeAddress, String workAddress, Integer homeZipCode, Integer workZipCode) { - this.homeAddress = homeAddress; - this.workAddress = workAddress; - this.homeZipCode = homeZipCode; - this.workZipCode = workZipCode; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Address address = (Address) o; - return Objects.equal(this.homeAddress, address.homeAddress) - && Objects.equal(this.workAddress, address.workAddress) - && Objects.equal(this.homeZipCode, address.homeZipCode) - && Objects.equal(this.workZipCode, address.workZipCode); - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Card.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Card.java deleted file mode 100644 index baa3cbda..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Card.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2022-2022 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata.it.entities; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import org.springframework.data.relational.core.mapping.Column; - -/** Example entity. */ -public class Card { - - @Column("id") - private long id; - - @Column("expiry_year") - private int expiryYear; - - @Column("expiry_month") - private int expiryMonth; - - @Column("issue_date") - private LocalDate issueDate; - - @Column("requested_at") - private LocalDateTime requestedAt; - - /** Constructor. */ - public Card(long id, int expiryYear, int expiryMonth, LocalDate issueDate, - LocalDateTime requestedAt) { - this.id = id; - this.expiryYear = expiryYear; - this.expiryMonth = expiryMonth; - this.issueDate = issueDate; - this.requestedAt = requestedAt; - } - - public long getId() { - return this.id; - } - - public void setId(long id) { - this.id = id; - } - - public int getExpiryYear() { - return this.expiryYear; - } - - public void setExpiryYear(int expiryYear) { - this.expiryYear = expiryYear; - } - - public int getExpiryMonth() { - return this.expiryMonth; - } - - public void setExpiryMonth(int expiryMonth) { - this.expiryMonth = expiryMonth; - } - - public LocalDate getIssueDate() { - return this.issueDate; - } - - public void setIssueDate(LocalDate issueDate) { - this.issueDate = issueDate; - } - - public LocalDateTime getRequestedAt() { - return this.requestedAt; - } - - public void setRequestedAt(LocalDateTime requestedAt) { - this.requestedAt = requestedAt; - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Person.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Person.java deleted file mode 100644 index 1d11c02f..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Person.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2021-2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata.it.entities; - -import java.util.Map; -import org.springframework.data.relational.core.mapping.Column; - -/** - * Example entity. - */ -public class Person { - - @Column("NAME") - private String name; - - @Column("BIRTH_YEAR") - private long birthYear; - - @Column("EXTRAS") - private Map extras; - - @Column("ADDRESS") - private Address address; - - /** - * Constructor. - * - * @param name name - * @param birthYear birth year. - * @param extras extra info stored in Map. - */ - public Person(String name, long birthYear, Map extras, Address address) { - this.name = name; - this.birthYear = birthYear; - this.extras = extras; - this.address = address; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public long getBirthYear() { - return this.birthYear; - } - - public void setBirthYear(long birthYear) { - this.birthYear = birthYear; - } - - public Map getExtras() { - return this.extras; - } - - public void setExtras(Map extras) { - this.extras = extras; - } - - public Address getAddress() { - return this.address; - } - - public void setAddress(Address address) { - this.address = address; - } - - @Override - public String toString() { - return "President{" - + "name='" - + this.name - + '\'' - + ", birthYear=" - + this.birthYear - + ", extras=" - + (this.getExtras() == null ? " " : this.getExtras().toString()) - + '}'; - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/President.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/President.java deleted file mode 100644 index 2affe37f..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/President.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2019-2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata.it.entities; - -import org.springframework.data.relational.core.mapping.Column; - -/** - * Example entity. - */ -public class President { - - @Column("NAME") - private String name; - - @Column("START_YEAR") - private long startYear; - - public President(String name, long startYear) { - this.name = name; - this.startYear = startYear; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public long getStartYear() { - return this.startYear; - } - - public void setStartYear(long startYear) { - this.startYear = startYear; - } - - @Override - public String toString() { - return "President{" - + "name='" - + this.name + '\'' - + ", startYear=" - + this.startYear + '}'; - } -} diff --git a/pom.xml b/pom.xml index fb49e0cc..fdf6dcfe 100644 --- a/pom.xml +++ b/pom.xml @@ -86,7 +86,7 @@ 26.25.0 - 0.9.0.RELEASE + 1.0.0.RELEASE 2022.0.12 4.9.0 @@ -335,7 +335,6 @@ cloud-spanner-r2dbc - cloud-spanner-spring-data-r2dbc cloud-spanner-r2dbc-samples @@ -386,7 +385,6 @@ cloud-spanner-r2dbc - cloud-spanner-spring-data-r2dbc
Query derivation is not supported yet.
By default, the test is configured to run tests in the `reactivetest` instance on the - * `testdb` database. This can be configured by overriding the `spanner.instance` and - * `spanner.database` system properties. - */ -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class SpannerR2dbcDialectIntegrationTest { - - private static final Logger logger = - LoggerFactory.getLogger(SpannerR2dbcDialectIntegrationTest.class); - - private static final String DRIVER_NAME = "spanner"; - - private static final String TEST_INSTANCE = - System.getProperty("spanner.instance", "reactivetest"); - - private static final String TEST_DATABASE = - System.getProperty("spanner.database", "testdb"); - - private static final ConnectionFactory connectionFactory = - ConnectionFactories.get(ConnectionFactoryOptions.builder() - .option(Option.valueOf("project"), ServiceOptions.getDefaultProjectId()) - .option(DRIVER, DRIVER_NAME) - .option(INSTANCE, TEST_INSTANCE) - .option(DATABASE, TEST_DATABASE) - .build()); - - private DatabaseClient databaseClient; - - private R2dbcEntityTemplate r2dbcEntityTemplate; - - /** - * Initializes the integration test environment for the Spanner R2DBC dialect. - */ - @BeforeAll - public void initializeTestEnvironment() { - Connection connection = Mono.from(connectionFactory.create()).block(); - - this.r2dbcEntityTemplate = new R2dbcEntityTemplate(connectionFactory); - this.databaseClient = this.r2dbcEntityTemplate.getDatabaseClient(); - - if (SpannerTestUtils.tableExists(connection, "PRESIDENT")) { - this.databaseClient.sql("DROP TABLE PRESIDENT") - .fetch() - .rowsUpdated() - .block(); - } - - this.databaseClient.sql( - "CREATE TABLE PRESIDENT (" - + " NAME STRING(256) NOT NULL," - + " START_YEAR INT64 NOT NULL" - + ") PRIMARY KEY (NAME)") - .fetch() - .rowsUpdated() - .block(); - } - - @AfterEach - public void cleanupTableAfterTest() { - this.databaseClient - .sql("DELETE FROM PRESIDENT where NAME is not null") - .fetch() - .rowsUpdated() - .block(); - } - - @Test - void testReadWrite() { - insertPresident(new President("Bill Clinton", 1992)); - - this.r2dbcEntityTemplate.select(President.class) - .first() - .as(StepVerifier::create) - .expectNextMatches( - president -> president.getName().equals("Bill Clinton") - && president.getStartYear() == 1992) - .verifyComplete(); - } - - @Test - void testLimitOffsetSupport() { - insertPresident(new President("Bill Clinton", 1992)); - insertPresident(new President("Joe Smith", 1996)); - insertPresident(new President("Bob", 2000)); - insertPresident(new President("Hello", 2004)); - insertPresident(new President("George Washington", 2008)); - - this.r2dbcEntityTemplate.select(President.class) - .matching( - Query.empty() - .sort(Sort.by(Direction.ASC, "name")) - .with(PageRequest.of(0, 2)) - ) - // Get the page at index 1; 2 elements per page. - //.page() - .all() - .as(StepVerifier::create) - .expectNextMatches(president -> president.getName().equals("Bill Clinton")) - .expectNextMatches(president -> president.getName().equals("Bob")) - .verifyComplete(); - } - - @Test - void testRowMap() { - insertPresident(new President("Bill Clinton", 1992)); - insertPresident(new President("Joe Smith", 1996)); - insertPresident(new President("Bob", 2000)); - insertPresident(new President("Hello", 2004)); - insertPresident(new President("George Washington", 2008)); - - this.r2dbcEntityTemplate.select(President.class) - .matching( - Query.empty() - .sort(Sort.by(Direction.ASC, "name"))) - .all() - .map(president -> president.getName()) - .as(StepVerifier::create) - .expectNext( - "Bill Clinton", "Bob", "George Washington", "Hello", "Joe Smith") - .verifyComplete(); - } - - private void insertPresident(President president) { - this.r2dbcEntityTemplate - .insert(President.class) - .using(president) - .then() - .as(StepVerifier::create) - .verifyComplete(); - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerR2dbcDialectJsonIntegrationTest.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerR2dbcDialectJsonIntegrationTest.java deleted file mode 100644 index dfd8b26f..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerR2dbcDialectJsonIntegrationTest.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright 2021-2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata.it; - -import static com.google.cloud.spanner.r2dbc.SpannerConnectionFactoryProvider.INSTANCE; -import static com.google.cloud.spanner.r2dbc.SpannerConnectionFactoryProvider.PROJECT; -import static io.r2dbc.spi.ConnectionFactoryOptions.DATABASE; -import static io.r2dbc.spi.ConnectionFactoryOptions.DRIVER; - -import com.google.cloud.ServiceOptions; -import com.google.cloud.spanner.r2dbc.springdata.it.entities.Address; -import com.google.cloud.spanner.r2dbc.springdata.it.entities.Person; -import com.google.cloud.spanner.r2dbc.v2.JsonWrapper; -import com.google.common.math.DoubleMath; -import com.google.gson.Gson; -import com.google.gson.JsonParseException; -import io.r2dbc.spi.Connection; -import io.r2dbc.spi.ConnectionFactories; -import io.r2dbc.spi.ConnectionFactory; -import io.r2dbc.spi.ConnectionFactoryOptions; -import io.r2dbc.spi.Option; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.convert.ReadingConverter; -import org.springframework.data.convert.WritingConverter; -import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration; -import org.springframework.data.r2dbc.convert.R2dbcCustomConversions; -import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; -import org.springframework.r2dbc.core.DatabaseClient; -import org.springframework.stereotype.Component; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -/** - * Integration tests for the Spring Data R2DBC dialect Json support. - * - *
By default, the test is configured to run tests in the `reactivetest` instance on the `testdb` - * database. This can be configured by overriding the `spanner.instance` and `spanner.database` - * system properties. - */ -@ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = SpannerR2dbcDialectJsonIntegrationTest.TestConfiguration.class) -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class SpannerR2dbcDialectJsonIntegrationTest { - - private static final String PROJECT_NAME = - System.getProperty("gcp.project", ServiceOptions.getDefaultProjectId()); - private static final String DRIVER_NAME = "spanner"; - - private static final String TEST_INSTANCE = - System.getProperty("spanner.instance", "reactivetest"); - - private static final String TEST_DATABASE = System.getProperty("spanner.database", "testdb"); - - private static final ConnectionFactory connectionFactory = - ConnectionFactories.get( - ConnectionFactoryOptions.builder() - .option(Option.valueOf("project"), ServiceOptions.getDefaultProjectId()) - .option(PROJECT, PROJECT_NAME) - .option(DRIVER, DRIVER_NAME) - .option(INSTANCE, TEST_INSTANCE) - .option(DATABASE, TEST_DATABASE) - .build()); - - private DatabaseClient databaseClient; - - @Autowired private R2dbcEntityTemplate r2dbcEntityTemplate; - - /** Initializes the integration test environment for the Spanner R2DBC dialect. */ - @BeforeAll - public void initializeTestEnvironment() { - Connection connection = Mono.from(connectionFactory.create()).block(); - - this.databaseClient = this.r2dbcEntityTemplate.getDatabaseClient(); - - if (SpannerTestUtils.tableExists(connection, "PERSON")) { - this.databaseClient.sql("DROP TABLE PERSON").fetch().rowsUpdated().block(); - } - - this.databaseClient - .sql( - "CREATE TABLE PERSON (" - + " NAME STRING(256) NOT NULL," - + " BIRTH_YEAR INT64 NOT NULL," - + " EXTRAS JSON," - + " ADDRESS JSON" - + ") PRIMARY KEY (NAME)") - .fetch() - .rowsUpdated() - .block(); - } - - @AfterEach - public void cleanupTableAfterTest() { - this.databaseClient - .sql("DELETE FROM PERSON where NAME is not null") - .fetch() - .rowsUpdated() - .block(); - } - - private void insertPerson(Person person) { - this.r2dbcEntityTemplate - .insert(Person.class) - .using(person) - .then() - .as(StepVerifier::create) - .verifyComplete(); - } - - @Test - void testReadWriteWithJsonFieldString() { - Map extras = new HashMap<>(); - extras.put("bio", "former U.S. president"); - extras.put("spouse", "Hillary Clinton"); - Person billClinton = new Person("Bill Clinton", 1946, extras, null); - insertPerson(billClinton); - - this.r2dbcEntityTemplate - .select(Person.class) - .first() - .as(StepVerifier::create) - .expectNextMatches( - person -> - person.getName().equals("Bill Clinton") - && person.getBirthYear() == 1946 - && person.getExtras().getOrDefault("spouse", "none").equals("Hillary Clinton") - && person - .getExtras() - .getOrDefault("bio", "none") - .equals("former U.S. president")) - .verifyComplete(); - } - - @Test - void testReadWriteWithJsonFieldBoolean() { - Map extras = new HashMap<>(); - extras.put("male", true); - extras.put("US citizen", true); - Person billClinton = new Person("Bill Clinton", 1946, extras, null); - insertPerson(billClinton); - - this.r2dbcEntityTemplate - .select(Person.class) - .first() - .as(StepVerifier::create) - .expectNextMatches( - person -> - person.getName().equals("Bill Clinton") - && person.getBirthYear() == 1946 - && person.getExtras().getOrDefault("male", false).equals(true) - && person.getExtras().getOrDefault("US citizen", false).equals(true)) - .verifyComplete(); - } - - @Test - void testReadWriteWithJsonFieldDouble() { - Map extras = new HashMap<>(); - extras.put("weight", 144.5); - extras.put("height", 5.916); - Person billClinton = new Person("John Doe", 1946, extras, null); - insertPerson(billClinton); - - this.r2dbcEntityTemplate - .select(Person.class) - .first() - .as(StepVerifier::create) - .expectNextMatches( - person -> - person.getName().equals("John Doe") - && person.getBirthYear() == 1946 - && DoubleMath.fuzzyEquals( - (double) person.getExtras().get("weight"), 144.5, 1e-5) - && DoubleMath.fuzzyEquals( - (double) person.getExtras().get("height"), 5.916, 1e-5)) - .verifyComplete(); - } - - @Test - void testReadWriteWithJsonFieldCustomClass() { - Address address = new Address("home address", "work address", 10012, 10011); - Person billClinton = new Person("Bill Clinton", 1946, null, address); - insertPerson(billClinton); - - this.r2dbcEntityTemplate - .select(Person.class) - .first() - .as(StepVerifier::create) - .expectNextMatches( - person -> - person.getName().equals("Bill Clinton") - && person.getBirthYear() == 1946 - && person.getAddress().equals(address)) - .verifyComplete(); - } - - /** Register custom converters between Map and JsonWrapper. */ - @Configuration - static class TestConfiguration extends AbstractR2dbcConfiguration { - - @Autowired ApplicationContext applicationContext; - - @Override - public ConnectionFactory connectionFactory() { - return connectionFactory; - } - - @Bean - public Gson gson() { - return new Gson(); - } - - @Bean - @Override - public R2dbcCustomConversions r2dbcCustomConversions() { - List> converters = new ArrayList<>(); - converters.add(this.applicationContext.getBean(JsonToReviewsConverter.class)); - converters.add(this.applicationContext.getBean(ReviewsToJsonConverter.class)); - return new R2dbcCustomConversions(getStoreConversions(), converters); - } - - @Component - @ReadingConverter - public class JsonToReviewsConverter implements Converter { - - private final Gson gson; - - @Autowired - public JsonToReviewsConverter(Gson gson) { - this.gson = gson; - } - - @Override - public Address convert(JsonWrapper json) { - try { - return this.gson.fromJson(json.toString(), Address.class); - } catch (JsonParseException e) { - return new Address(); - } - } - } - - @Component - @WritingConverter - public class ReviewsToJsonConverter implements Converter { - - private final Gson gson; - - @Autowired - public ReviewsToJsonConverter(Gson gson) { - this.gson = gson; - } - - @Override - public JsonWrapper convert(Address source) { - try { - return JsonWrapper.of(this.gson.toJson(source)); - } catch (JsonParseException e) { - return JsonWrapper.of(""); - } - } - } - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerTestUtils.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerTestUtils.java deleted file mode 100644 index be82b907..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/SpannerTestUtils.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2019-2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata.it; - -import io.r2dbc.spi.Connection; -import reactor.core.publisher.Mono; - -/** - * Helper functions for Spanner integration testing. - */ -public class SpannerTestUtils { - - /** - * Returns true if the Spanner table exists; false if not. - */ - public static boolean tableExists(Connection connection, String tableName) { - return Mono.from(connection.createStatement( - "SELECT table_name FROM information_schema.tables WHERE table_name = @name") - .bind("name", tableName) - .execute()) - .flatMapMany(result -> result.map((r, m) -> r)) - .hasElements() - .block(); - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Address.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Address.java deleted file mode 100644 index b3d6d96b..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Address.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2021-2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata.it.entities; - -import com.google.common.base.Objects; - -/** - * Example custom class entity field. - */ -public class Address { - String homeAddress; - String workAddress; - Integer homeZipCode; - Integer workZipCode; - - public Address() {} - - /** - * Constructor. - * - * @param homeAddress home address - * @param workAddress work address - * @param homeZipCode home zip code - * @param workZipCode work zip code - */ - public Address(String homeAddress, String workAddress, Integer homeZipCode, Integer workZipCode) { - this.homeAddress = homeAddress; - this.workAddress = workAddress; - this.homeZipCode = homeZipCode; - this.workZipCode = workZipCode; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Address address = (Address) o; - return Objects.equal(this.homeAddress, address.homeAddress) - && Objects.equal(this.workAddress, address.workAddress) - && Objects.equal(this.homeZipCode, address.homeZipCode) - && Objects.equal(this.workZipCode, address.workZipCode); - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Card.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Card.java deleted file mode 100644 index baa3cbda..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Card.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2022-2022 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata.it.entities; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import org.springframework.data.relational.core.mapping.Column; - -/** Example entity. */ -public class Card { - - @Column("id") - private long id; - - @Column("expiry_year") - private int expiryYear; - - @Column("expiry_month") - private int expiryMonth; - - @Column("issue_date") - private LocalDate issueDate; - - @Column("requested_at") - private LocalDateTime requestedAt; - - /** Constructor. */ - public Card(long id, int expiryYear, int expiryMonth, LocalDate issueDate, - LocalDateTime requestedAt) { - this.id = id; - this.expiryYear = expiryYear; - this.expiryMonth = expiryMonth; - this.issueDate = issueDate; - this.requestedAt = requestedAt; - } - - public long getId() { - return this.id; - } - - public void setId(long id) { - this.id = id; - } - - public int getExpiryYear() { - return this.expiryYear; - } - - public void setExpiryYear(int expiryYear) { - this.expiryYear = expiryYear; - } - - public int getExpiryMonth() { - return this.expiryMonth; - } - - public void setExpiryMonth(int expiryMonth) { - this.expiryMonth = expiryMonth; - } - - public LocalDate getIssueDate() { - return this.issueDate; - } - - public void setIssueDate(LocalDate issueDate) { - this.issueDate = issueDate; - } - - public LocalDateTime getRequestedAt() { - return this.requestedAt; - } - - public void setRequestedAt(LocalDateTime requestedAt) { - this.requestedAt = requestedAt; - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Person.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Person.java deleted file mode 100644 index 1d11c02f..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/Person.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2021-2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata.it.entities; - -import java.util.Map; -import org.springframework.data.relational.core.mapping.Column; - -/** - * Example entity. - */ -public class Person { - - @Column("NAME") - private String name; - - @Column("BIRTH_YEAR") - private long birthYear; - - @Column("EXTRAS") - private Map extras; - - @Column("ADDRESS") - private Address address; - - /** - * Constructor. - * - * @param name name - * @param birthYear birth year. - * @param extras extra info stored in Map. - */ - public Person(String name, long birthYear, Map extras, Address address) { - this.name = name; - this.birthYear = birthYear; - this.extras = extras; - this.address = address; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public long getBirthYear() { - return this.birthYear; - } - - public void setBirthYear(long birthYear) { - this.birthYear = birthYear; - } - - public Map getExtras() { - return this.extras; - } - - public void setExtras(Map extras) { - this.extras = extras; - } - - public Address getAddress() { - return this.address; - } - - public void setAddress(Address address) { - this.address = address; - } - - @Override - public String toString() { - return "President{" - + "name='" - + this.name - + '\'' - + ", birthYear=" - + this.birthYear - + ", extras=" - + (this.getExtras() == null ? " " : this.getExtras().toString()) - + '}'; - } -} diff --git a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/President.java b/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/President.java deleted file mode 100644 index 2affe37f..00000000 --- a/cloud-spanner-spring-data-r2dbc/src/test/java/com/google/cloud/spanner/r2dbc/springdata/it/entities/President.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2019-2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.spanner.r2dbc.springdata.it.entities; - -import org.springframework.data.relational.core.mapping.Column; - -/** - * Example entity. - */ -public class President { - - @Column("NAME") - private String name; - - @Column("START_YEAR") - private long startYear; - - public President(String name, long startYear) { - this.name = name; - this.startYear = startYear; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public long getStartYear() { - return this.startYear; - } - - public void setStartYear(long startYear) { - this.startYear = startYear; - } - - @Override - public String toString() { - return "President{" - + "name='" - + this.name + '\'' - + ", startYear=" - + this.startYear + '}'; - } -} diff --git a/pom.xml b/pom.xml index fb49e0cc..fdf6dcfe 100644 --- a/pom.xml +++ b/pom.xml @@ -86,7 +86,7 @@ 26.25.0 - 0.9.0.RELEASE + 1.0.0.RELEASE 2022.0.12 4.9.0 @@ -335,7 +335,6 @@ cloud-spanner-r2dbc - cloud-spanner-spring-data-r2dbc cloud-spanner-r2dbc-samples @@ -386,7 +385,6 @@ cloud-spanner-r2dbc - cloud-spanner-spring-data-r2dbc