diff --git a/spring-cloud-gcp-autoconfigure/pom.xml b/spring-cloud-gcp-autoconfigure/pom.xml index ea4bbe4ef2..0c38edfe8e 100644 --- a/spring-cloud-gcp-autoconfigure/pom.xml +++ b/spring-cloud-gcp-autoconfigure/pom.xml @@ -369,7 +369,7 @@ maven-failsafe-plugin - spring-cloud-gcp-ci-firestore + firestoredb diff --git a/spring-cloud-gcp-data-firestore/src/test/java/com/google/cloud/spring/data/firestore/it/FirestoreIntegrationTestsConfiguration.java b/spring-cloud-gcp-data-firestore/src/test/java/com/google/cloud/spring/data/firestore/it/FirestoreIntegrationTestsConfiguration.java index f1751e1429..1e3a1a3e86 100644 --- a/spring-cloud-gcp-data-firestore/src/test/java/com/google/cloud/spring/data/firestore/it/FirestoreIntegrationTestsConfiguration.java +++ b/spring-cloud-gcp-data-firestore/src/test/java/com/google/cloud/spring/data/firestore/it/FirestoreIntegrationTestsConfiguration.java @@ -16,7 +16,10 @@ package com.google.cloud.spring.data.firestore.it; +import com.google.api.client.util.escape.PercentEscaper; +import com.google.api.gax.rpc.internal.Headers; import com.google.auth.oauth2.GoogleCredentials; +import com.google.cloud.spring.core.DefaultGcpProjectIdProvider; import com.google.cloud.spring.data.firestore.FirestoreTemplate; import com.google.cloud.spring.data.firestore.entities.UserRepository; import com.google.cloud.spring.data.firestore.mapping.FirestoreClassMapper; @@ -26,11 +29,15 @@ import com.google.cloud.spring.data.firestore.transaction.ReactiveFirestoreTransactionManager; import com.google.firestore.v1.FirestoreGrpc; import io.grpc.CallCredentials; +import io.grpc.ClientInterceptor; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; +import io.grpc.Metadata; import io.grpc.auth.MoreCallCredentials; +import io.grpc.stub.MetadataUtils; import java.io.IOException; import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @@ -44,17 +51,34 @@ @EnableReactiveFirestoreRepositories(basePackageClasses = UserRepository.class) @EnableTransactionManagement public class FirestoreIntegrationTestsConfiguration { - @Value("projects/${test.integration.firestore.project-id}/databases/(default)/documents") String defaultParent; + String projectId; + + String databaseId; + + @Autowired + public FirestoreIntegrationTestsConfiguration( + @Value("${test.integration.firestore.database-id:(default)}") String databaseId) { + this.projectId = new DefaultGcpProjectIdProvider().getProjectId(); + this.databaseId = databaseId; + this.defaultParent = + String.format("projects/%s/databases/%s/documents", this.projectId, databaseId); + } + + private static final PercentEscaper PERCENT_ESCAPER = new PercentEscaper("._-~"); + @Bean - FirestoreGrpc.FirestoreStub firestoreStub() throws IOException { + FirestoreGrpc.FirestoreStub firestoreStub(ClientInterceptor firestoreRoutingHeadersInterceptor) + throws IOException { GoogleCredentials credentials = GoogleCredentials.getApplicationDefault(); CallCredentials callCredentials = MoreCallCredentials.from(credentials); // Create a channel ManagedChannel channel = - ManagedChannelBuilder.forTarget("dns:///firestore.googleapis.com:443").build(); + ManagedChannelBuilder.forTarget("dns:///firestore.googleapis.com:443") + .intercept(firestoreRoutingHeadersInterceptor) + .build(); return FirestoreGrpc.newStub(channel).withCallCredentials(callCredentials); } @@ -72,6 +96,24 @@ public FirestoreTemplate firestoreTemplate( firestoreStub, this.defaultParent, classMapper, firestoreMappingContext); } + @Bean + @ConditionalOnMissingBean(name = "firestoreRoutingHeadersInterceptor") + public ClientInterceptor firestoreRoutingHeadersInterceptor() { + // add routing header for custom database id + Metadata routingHeader = new Metadata(); + if (projectId != null && databaseId != null) { + Metadata.Key key = + Metadata.Key.of(Headers.DYNAMIC_ROUTING_HEADER_KEY, Metadata.ASCII_STRING_MARSHALLER); + routingHeader.put( + key, + "project_id=" + + PERCENT_ESCAPER.escape(projectId) + + "&database_id=" + + PERCENT_ESCAPER.escape(databaseId)); + } + return MetadataUtils.newAttachHeadersInterceptor(routingHeader); + } + @Bean @ConditionalOnMissingBean public ReactiveFirestoreTransactionManager firestoreTransactionManager( diff --git a/spring-cloud-gcp-data-firestore/src/test/resources/application-test.properties b/spring-cloud-gcp-data-firestore/src/test/resources/application-test.properties index ca4cfd0603..fa007f1025 100644 --- a/spring-cloud-gcp-data-firestore/src/test/resources/application-test.properties +++ b/spring-cloud-gcp-data-firestore/src/test/resources/application-test.properties @@ -1 +1 @@ -test.integration.firestore.project-id=spring-cloud-gcp-ci-firestore +test.integration.firestore.database-id=firestoredb diff --git a/spring-cloud-gcp-samples/spring-cloud-gcp-data-firestore-sample/src/test/resources/application-test.properties b/spring-cloud-gcp-samples/spring-cloud-gcp-data-firestore-sample/src/test/resources/application-test.properties index 21dd41714f..edc6d2bef0 100644 --- a/spring-cloud-gcp-samples/spring-cloud-gcp-data-firestore-sample/src/test/resources/application-test.properties +++ b/spring-cloud-gcp-samples/spring-cloud-gcp-data-firestore-sample/src/test/resources/application-test.properties @@ -1,2 +1 @@ -spring.cloud.gcp.firestore.project-id=spring-cloud-gcp-ci spring.cloud.gcp.firestore.database-id=firestoredb \ No newline at end of file diff --git a/spring-cloud-gcp-samples/spring-cloud-gcp-starter-firestore-sample/src/test/resources/application-test.properties b/spring-cloud-gcp-samples/spring-cloud-gcp-starter-firestore-sample/src/test/resources/application-test.properties index 32c8585e05..918db35743 100644 --- a/spring-cloud-gcp-samples/spring-cloud-gcp-starter-firestore-sample/src/test/resources/application-test.properties +++ b/spring-cloud-gcp-samples/spring-cloud-gcp-starter-firestore-sample/src/test/resources/application-test.properties @@ -1 +1 @@ -spring.cloud.gcp.firestore.project-id=spring-cloud-gcp-ci-firestore +spring.cloud.gcp.firestore.database-id=firestoredb