This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 82f7c6871c29 CAMEL-24347: camel-google-firestore - listCollections
honours the configured documentId
82f7c6871c29 is described below
commit 82f7c6871c29c2f2a03b03fa57020fd957f89a39
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue Aug 11 14:22:37 2026 +0200
CAMEL-24347: camel-google-firestore - listCollections honours the
configured documentId
listCollections was the only document operation reading the document id
straight
from the header: with documentId set on the endpoint it silently listed the
root
collections instead of the sub-collections of that document. It now falls
back to
the configured id like the other operations, while still treating a missing
id as
"list the root collections".
The query and list operations also wrote their _id/_path entries into the
map the
Firestore SDK returned; they now build their own copy.
Closes #25430
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Signed-off-by: Andrea Cosentino <[email protected]>
---
.../camel-google/camel-google-firestore/pom.xml | 5 ++
.../google/firestore/GoogleFirestoreProducer.java | 36 +++++++---
.../GoogleFirestoreProducerDocumentIdTest.java | 77 ++++++++++++++++++++++
3 files changed, 109 insertions(+), 9 deletions(-)
diff --git a/components/camel-google/camel-google-firestore/pom.xml
b/components/camel-google/camel-google-firestore/pom.xml
index a8e2fa53873b..3c506040fe03 100644
--- a/components/camel-google/camel-google-firestore/pom.xml
+++ b/components/camel-google/camel-google-firestore/pom.xml
@@ -71,5 +71,10 @@
<artifactId>camel-test-junit6</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git
a/components/camel-google/camel-google-firestore/src/main/java/org/apache/camel/component/google/firestore/GoogleFirestoreProducer.java
b/components/camel-google/camel-google-firestore/src/main/java/org/apache/camel/component/google/firestore/GoogleFirestoreProducer.java
index 3032de741d78..022fd115c55c 100644
---
a/components/camel-google/camel-google-firestore/src/main/java/org/apache/camel/component/google/firestore/GoogleFirestoreProducer.java
+++
b/components/camel-google/camel-google-firestore/src/main/java/org/apache/camel/component/google/firestore/GoogleFirestoreProducer.java
@@ -243,10 +243,7 @@ public class GoogleFirestoreProducer extends
DefaultProducer {
List<Map<String, Object>> results = new ArrayList<>();
for (QueryDocumentSnapshot document : querySnapshot.getDocuments()) {
- Map<String, Object> docData = document.getData();
- docData.put("_id", document.getId());
- docData.put("_path", document.getReference().getPath());
- results.add(docData);
+ results.add(withDocumentMetadata(document));
}
LOG.debug("Query returned {} documents from collection: {}",
results.size(), collectionName);
@@ -255,6 +252,17 @@ public class GoogleFirestoreProducer extends
DefaultProducer {
message.setBody(results);
}
+ /**
+ * The document data plus its id and path. The snapshot data is copied
first: the returned map is handed to the
+ * route, and the id/path entries must not be written into the object the
SDK gave us.
+ */
+ private static Map<String, Object>
withDocumentMetadata(QueryDocumentSnapshot document) {
+ Map<String, Object> docData = new HashMap<>(document.getData());
+ docData.put("_id", document.getId());
+ docData.put("_path", document.getReference().getPath());
+ return docData;
+ }
+
private Query applyWhereClause(Query query, String field, String operator,
Object value) {
return switch (operator.toLowerCase()) {
case "==", "eq", "equals" -> query.whereEqualTo(field, value);
@@ -291,10 +299,7 @@ public class GoogleFirestoreProducer extends
DefaultProducer {
List<Map<String, Object>> results = new ArrayList<>();
for (QueryDocumentSnapshot document : querySnapshot.getDocuments()) {
- Map<String, Object> docData = document.getData();
- docData.put("_id", document.getId());
- docData.put("_path", document.getReference().getPath());
- results.add(docData);
+ results.add(withDocumentMetadata(document));
}
LOG.debug("Listed {} documents from collection: {}", results.size(),
collectionName);
@@ -304,7 +309,7 @@ public class GoogleFirestoreProducer extends
DefaultProducer {
}
private void listCollections(Firestore firestore, Exchange exchange)
throws Exception {
- String documentId =
exchange.getIn().getHeader(GoogleFirestoreConstants.DOCUMENT_ID, String.class);
+ String documentId = determineListedDocumentId(exchange);
List<String> collectionIds = new ArrayList<>();
Iterable<CollectionReference> collections;
@@ -384,6 +389,19 @@ public class GoogleFirestoreProducer extends
DefaultProducer {
return collectionName;
}
+ /**
+ * The document id for listCollections, where it is optional: without one
the root collections are listed. Unlike
+ * {@link #determineDocumentId(Exchange)} this does not fail when none is
set, but it does fall back to the
+ * configured id the same way.
+ */
+ String determineListedDocumentId(Exchange exchange) {
+ String documentId =
exchange.getIn().getHeader(GoogleFirestoreConstants.DOCUMENT_ID, String.class);
+ if (ObjectHelper.isEmpty(documentId)) {
+ documentId = getConfiguration().getDocumentId();
+ }
+ return documentId;
+ }
+
private String determineDocumentId(Exchange exchange) {
String documentId =
exchange.getIn().getHeader(GoogleFirestoreConstants.DOCUMENT_ID, String.class);
if (ObjectHelper.isEmpty(documentId)) {
diff --git
a/components/camel-google/camel-google-firestore/src/test/java/org/apache/camel/component/google/firestore/GoogleFirestoreProducerDocumentIdTest.java
b/components/camel-google/camel-google-firestore/src/test/java/org/apache/camel/component/google/firestore/GoogleFirestoreProducerDocumentIdTest.java
new file mode 100644
index 000000000000..0a09c0679931
--- /dev/null
+++
b/components/camel-google/camel-google-firestore/src/test/java/org/apache/camel/component/google/firestore/GoogleFirestoreProducerDocumentIdTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.google.firestore;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.support.DefaultExchange;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Verifies that the operations resolve the document id the same way: the
header first, then the endpoint option.
+ */
+class GoogleFirestoreProducerDocumentIdTest {
+
+ private DefaultCamelContext context;
+
+ @AfterEach
+ void tearDown() {
+ if (context != null) {
+ context.stop();
+ }
+ }
+
+ private GoogleFirestoreProducer producer(String query) throws Exception {
+ if (context != null) {
+ context.stop();
+ }
+ context = new DefaultCamelContext();
+ // the endpoint is deliberately not started, so no firestore client is
built
+ GoogleFirestoreComponent component =
context.getComponent("google-firestore", GoogleFirestoreComponent.class);
+ GoogleFirestoreEndpoint endpoint
+ = (GoogleFirestoreEndpoint)
component.createEndpoint("google-firestore://users" + query);
+ return new GoogleFirestoreProducer(endpoint);
+ }
+
+ @Test
+ void theConfiguredDocumentIdIsUsedWhenNoHeaderIsSet() throws Exception {
+ GoogleFirestoreProducer producer = producer("?documentId=configured");
+
+ assertThat(producer.determineListedDocumentId(new
DefaultExchange(context))).isEqualTo("configured");
+ }
+
+ @Test
+ void theHeaderWinsOverTheConfiguredDocumentId() throws Exception {
+ GoogleFirestoreProducer producer = producer("?documentId=configured");
+
+ Exchange exchange = new DefaultExchange(context);
+ exchange.getIn().setHeader(GoogleFirestoreConstants.DOCUMENT_ID,
"from-header");
+
+
assertThat(producer.determineListedDocumentId(exchange)).isEqualTo("from-header");
+ }
+
+ @Test
+ void withoutAnyDocumentIdTheRootCollectionsAreListed() throws Exception {
+ // listCollections is the one operation where the document id is
optional: no id means the root
+ GoogleFirestoreProducer producer = producer("");
+
+ assertThat(producer.determineListedDocumentId(new
DefaultExchange(context))).isNull();
+ }
+}