From 30f7b7a776bd75415a6ff214f110ad087757073d Mon Sep 17 00:00:00 2001 From: Mike Eltsufin Date: Tue, 12 Dec 2023 10:17:59 -0500 Subject: [PATCH] fix(3.x): GcpFirestoreEmulatorAutoConfiguration constructs invalid document parent (#2429) Backport of (#2348). Fixes: #2290. Fixes: #2330. --- ...GcpFirestoreEmulatorAutoConfiguration.java | 5 +- ...atorAutoConfigurationIntegrationTests.java | 87 ------------ ...restoreEmulatorAutoConfigurationTests.java | 125 +++++++++++++----- 3 files changed, 98 insertions(+), 119 deletions(-) delete mode 100644 spring-cloud-gcp-autoconfigure/src/test/java/com/google/cloud/spring/autoconfigure/firestore/GcpFirestoreEmulatorAutoConfigurationIntegrationTests.java diff --git a/spring-cloud-gcp-autoconfigure/src/main/java/com/google/cloud/spring/autoconfigure/firestore/GcpFirestoreEmulatorAutoConfiguration.java b/spring-cloud-gcp-autoconfigure/src/main/java/com/google/cloud/spring/autoconfigure/firestore/GcpFirestoreEmulatorAutoConfiguration.java index fe742db36e..2773e5b225 100644 --- a/spring-cloud-gcp-autoconfigure/src/main/java/com/google/cloud/spring/autoconfigure/firestore/GcpFirestoreEmulatorAutoConfiguration.java +++ b/spring-cloud-gcp-autoconfigure/src/main/java/com/google/cloud/spring/autoconfigure/firestore/GcpFirestoreEmulatorAutoConfiguration.java @@ -79,7 +79,8 @@ public FirestoreOptions firestoreOptions() { private Credentials emulatorCredentials() { final Map> headerMap = new HashMap<>(); headerMap.put("Authorization", Collections.singletonList("Bearer owner")); - headerMap.put("google-cloud-resource-prefix", Collections.singletonList(rootPath)); + headerMap.put("google-cloud-resource-prefix", Collections.singletonList( + rootPath.substring(0, rootPath.lastIndexOf("/documents")))); return new Credentials() { @Override @@ -120,7 +121,7 @@ public FirestoreTemplate firestoreTemplate( FirestoreClassMapper classMapper, FirestoreMappingContext firestoreMappingContext) { return new FirestoreTemplate( - firestoreStub, rootPath + "/documents", classMapper, firestoreMappingContext); + firestoreStub, rootPath, classMapper, firestoreMappingContext); } @Bean diff --git a/spring-cloud-gcp-autoconfigure/src/test/java/com/google/cloud/spring/autoconfigure/firestore/GcpFirestoreEmulatorAutoConfigurationIntegrationTests.java b/spring-cloud-gcp-autoconfigure/src/test/java/com/google/cloud/spring/autoconfigure/firestore/GcpFirestoreEmulatorAutoConfigurationIntegrationTests.java deleted file mode 100644 index 2c4b12c3d6..0000000000 --- a/spring-cloud-gcp-autoconfigure/src/test/java/com/google/cloud/spring/autoconfigure/firestore/GcpFirestoreEmulatorAutoConfigurationIntegrationTests.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2017-2020 the original author or authors. - * - * 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.spring.autoconfigure.firestore; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; -import com.google.cloud.firestore.FirestoreOptions; -import com.google.cloud.spring.autoconfigure.core.GcpContextAutoConfiguration; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledIfSystemProperty; -import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.test.context.runner.ApplicationContextRunner; - -/** Tests for Firestore Emulator autoconfiguration. */ -@EnabledIfSystemProperty(named = "it.firestore", matches = "true") -class GcpFirestoreEmulatorAutoConfigurationIntegrationTests { - - ApplicationContextRunner contextRunner = - new ApplicationContextRunner() - .withConfiguration( - AutoConfigurations.of( - GcpFirestoreEmulatorAutoConfiguration.class, - GcpContextAutoConfiguration.class, - GcpFirestoreAutoConfiguration.class, - FirestoreTransactionManagerAutoConfiguration.class)); - - @Test - void testAutoConfigurationEnabled() { - contextRunner - .withPropertyValues( - "spring.cloud.gcp.firestore.project-id=", - "spring.cloud.gcp.firestore.emulator.enabled=true", - "spring.cloud.gcp.firestore.host-port=localhost:9000") - .run( - context -> { - FirestoreOptions firestoreOptions = context.getBean(FirestoreOptions.class); - String endpoint = - ((InstantiatingGrpcChannelProvider) - firestoreOptions.getTransportChannelProvider()) - .getEndpoint(); - assertThat(endpoint).isEqualTo("localhost:9000"); - - assertThat(firestoreOptions.getProjectId()).isEqualTo("unused"); - }); - } - - @Test - void testAutoConfigurationDisabled() { - contextRunner.run( - context -> { - FirestoreOptions firestoreOptions = context.getBean(FirestoreOptions.class); - String endpoint = - ((InstantiatingGrpcChannelProvider) firestoreOptions.getTransportChannelProvider()) - .getEndpoint(); - assertThat(endpoint).isEqualTo("firestore.googleapis.com:443"); - }); - } - - @Test - void testThatIfProjectIdIsGivenItWillBeUsed() { - contextRunner - .withPropertyValues( - "spring.cloud.gcp.firestore.project-id=demo", - "spring.cloud.gcp.firestore.emulator.enabled=true", - "spring.cloud.gcp.firestore.host-port=localhost:9000") - .run( - context -> { - FirestoreOptions firestoreOptions = context.getBean(FirestoreOptions.class); - assertThat(firestoreOptions.getProjectId()).isEqualTo("demo"); - }); - } -} diff --git a/spring-cloud-gcp-autoconfigure/src/test/java/com/google/cloud/spring/autoconfigure/firestore/GcpFirestoreEmulatorAutoConfigurationTests.java b/spring-cloud-gcp-autoconfigure/src/test/java/com/google/cloud/spring/autoconfigure/firestore/GcpFirestoreEmulatorAutoConfigurationTests.java index 09beeca3cc..5133ebe7db 100644 --- a/spring-cloud-gcp-autoconfigure/src/test/java/com/google/cloud/spring/autoconfigure/firestore/GcpFirestoreEmulatorAutoConfigurationTests.java +++ b/spring-cloud-gcp-autoconfigure/src/test/java/com/google/cloud/spring/autoconfigure/firestore/GcpFirestoreEmulatorAutoConfigurationTests.java @@ -17,55 +17,95 @@ package com.google.cloud.spring.autoconfigure.firestore; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; import com.google.api.gax.core.CredentialsProvider; -import com.google.api.gax.core.NoCredentialsProvider; import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; import com.google.auth.Credentials; import com.google.cloud.NoCredentials; import com.google.cloud.firestore.Firestore; import com.google.cloud.firestore.FirestoreOptions; +import com.google.cloud.spring.autoconfigure.core.GcpContextAutoConfiguration; import com.google.cloud.spring.core.GcpProjectIdProvider; +import com.google.cloud.spring.data.firestore.FirestoreTemplate; +import java.util.List; +import org.assertj.core.util.Lists; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.test.util.ReflectionTestUtils; -/** Tests for the Firestore emulator config. */ +/** + * Tests for Firestore Emulator autoconfiguration. + */ class GcpFirestoreEmulatorAutoConfigurationTests { - GcpProjectIdProvider projectId = () -> "projectId"; - - private final ApplicationContextRunner contextRunner = + ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration( AutoConfigurations.of( - GcpFirestoreEmulatorAutoConfiguration.class, GcpFirestoreAutoConfiguration.class)) - .withBean("projectId", GcpProjectIdProvider.class, () -> projectId); + GcpFirestoreEmulatorAutoConfiguration.class, + GcpContextAutoConfiguration.class, + GcpFirestoreAutoConfiguration.class, + FirestoreTransactionManagerAutoConfiguration.class)); @Test - void testEmulatorEnabledConfig() { + void testAutoConfigurationEnabled() { + contextRunner + .withPropertyValues( + "spring.cloud.gcp.firestore.project-id=", + "spring.cloud.gcp.firestore.emulator.enabled=true", + "spring.cloud.gcp.firestore.host-port=localhost:9000") + .run( + context -> { + FirestoreOptions firestoreOptions = context.getBean(FirestoreOptions.class); + String endpoint = + ((InstantiatingGrpcChannelProvider) + firestoreOptions.getTransportChannelProvider()) + .getEndpoint(); + assertThat(endpoint).isEqualTo("localhost:9000"); + + assertThat(firestoreOptions.getProjectId()).isEqualTo("unused"); + }); + } + + @Test + void testAutoConfigurationDisabled() { + contextRunner + .withBean(CredentialsProvider.class, () -> NoCredentials::getInstance) + .withBean(GcpProjectIdProvider.class, () -> () -> "my-project") + .run( + context -> { + FirestoreOptions firestoreOptions = context.getBean(FirestoreOptions.class); + String endpoint = + ((InstantiatingGrpcChannelProvider) firestoreOptions.getTransportChannelProvider()) + .getEndpoint(); + assertThat(endpoint).isEqualTo("firestore.googleapis.com:443"); + }); + } - CredentialsProvider mockedCredentialsProvider = () -> mock(NoCredentials.class); + @Test + void testThatIfProjectIdIsGivenItWillBeUsed() { + contextRunner + .withPropertyValues( + "spring.cloud.gcp.firestore.project-id=demo", + "spring.cloud.gcp.firestore.emulator.enabled=true", + "spring.cloud.gcp.firestore.host-port=localhost:9000") + .run( + context -> { + FirestoreOptions firestoreOptions = context.getBean(FirestoreOptions.class); + assertThat(firestoreOptions.getProjectId()).isEqualTo("demo"); + }); + } + @Test + void testEmulatorEnabledConfig() { this.contextRunner .withPropertyValues( "spring.cloud.gcp.firestore.projectId=test-project", "spring.cloud.gcp.firestore.emulator.enabled=true", "spring.cloud.gcp.firestore.host-port=localhost:8080") - .withBean( - "mockedCredentialsProvider", CredentialsProvider.class, () -> mockedCredentialsProvider) .run( context -> { - CredentialsProvider defaultCredentialsProvider = - context.getBean(CredentialsProvider.class); - assertThat(defaultCredentialsProvider).isSameAs(mockedCredentialsProvider); - - GcpFirestoreAutoConfiguration firestoreAutoConfiguration = - context.getBean(GcpFirestoreAutoConfiguration.class); - assertThat(firestoreAutoConfiguration.getCredentialsProvider()) - .isInstanceOf(NoCredentialsProvider.class); - FirestoreOptions datastoreOptions = context.getBean(Firestore.class).getOptions(); assertThat(datastoreOptions.getProjectId()).isEqualTo("test-project"); @@ -77,23 +117,16 @@ void testEmulatorEnabledConfig() { @Test void testDefaultConfig() { - CredentialsProvider mockedCredentialsProvider = () -> mock(Credentials.class); - this.contextRunner .withPropertyValues("spring.cloud.gcp.firestore.projectId=test-project") - .withBean( - "mockedCredentialsProvider", CredentialsProvider.class, () -> mockedCredentialsProvider) + .withBean(CredentialsProvider.class, () -> NoCredentials::getInstance) + .withBean(GcpProjectIdProvider.class, () -> () -> "my-project") .run( context -> { GcpFirestoreProperties gcpFirestoreProperties = context.getBean(GcpFirestoreProperties.class); assertThat(gcpFirestoreProperties.getEmulator().isEnabled()).isFalse(); - GcpFirestoreAutoConfiguration firestoreAutoConfiguration = - context.getBean(GcpFirestoreAutoConfiguration.class); - assertThat(firestoreAutoConfiguration.getCredentialsProvider()) - .isEqualTo(mockedCredentialsProvider); - FirestoreOptions datastoreOptions = context.getBean(Firestore.class).getOptions(); assertThat(datastoreOptions.getProjectId()).isEqualTo("test-project"); @@ -102,4 +135,36 @@ void testDefaultConfig() { assertThat(channelProvider.getEndpoint()).isEqualTo("firestore.googleapis.com:443"); }); } + + @Test + void testFirestoreTemplateParent() { + contextRunner + .withPropertyValues( + "spring.cloud.gcp.firestore.project-id=demo", + "spring.cloud.gcp.firestore.emulator.enabled=true", + "spring.cloud.gcp.firestore.database-id=testdb") + .run( + context -> { + FirestoreTemplate template = context.getBean(FirestoreTemplate.class); + assertThat(ReflectionTestUtils.getField(template, "parent")).isEqualTo( + "projects/demo/databases/testdb/documents"); + }); + } + + @Test + void testDefaultCredentials() { + contextRunner + .withPropertyValues( + "spring.cloud.gcp.firestore.project-id=demo", + "spring.cloud.gcp.firestore.emulator.enabled=true", + "spring.cloud.gcp.firestore.database-id=testdb") + .run( + context -> { + FirestoreOptions firestoreOptions = context.getBean(FirestoreOptions.class); + Credentials credentials = firestoreOptions.getCredentials(); + List header = credentials.getRequestMetadata() + .get("google-cloud-resource-prefix"); + assertThat(header).isEqualTo(Lists.list("projects/demo/databases/testdb")); + }); + } }