This is an automated email from the ASF dual-hosted git repository. jamesnetherton pushed a commit to branch camel-main in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
commit 4ff854b0c062faefbbb242f28bf0a2fbae6453bc Author: Jiri Ondrusek <[email protected]> AuthorDate: Fri Jul 10 10:02:29 2026 +0200 Fixes #8854. Adapt weaviate extension and tests for weaviate client v6 Migrate from io.weaviate:client (v5) to io.weaviate:client6 package structure, update deployment processor reflection registration, handle v6 response types (direct objects instead of Result<?> wrapper), add gRPC transport support via quarkus-grpc-common with bytecode remapping of shaded netty/grpc references (same pattern as Milvus extension). Co-Authored-By: Claude Opus 4.6 <[email protected]> --- extensions/weaviate/deployment/pom.xml | 4 ++ .../weaviate/deployment/WeaviateProcessor.java | 49 +++++++++++++++---- extensions/weaviate/runtime/pom.xml | 4 ++ .../component/weaviate/it/WeaviateResource.java | 55 +++++++++++++--------- .../component/weaviate/it/WeaviateRoutes.java | 15 +++++- .../component/weaviate/it/WeaviateTest.java | 39 +++++---------- .../weaviate/it/WeaviateTestResource.java | 7 ++- 7 files changed, 112 insertions(+), 61 deletions(-) diff --git a/extensions/weaviate/deployment/pom.xml b/extensions/weaviate/deployment/pom.xml index 2c0a08d297..8430c63cdd 100644 --- a/extensions/weaviate/deployment/pom.xml +++ b/extensions/weaviate/deployment/pom.xml @@ -42,6 +42,10 @@ <groupId>io.quarkus</groupId> <artifactId>quarkus-netty-deployment</artifactId> </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-grpc-common-deployment</artifactId> + </dependency> <dependency> <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-weaviate</artifactId> diff --git a/extensions/weaviate/deployment/src/main/java/org/apache/camel/quarkus/component/weaviate/deployment/WeaviateProcessor.java b/extensions/weaviate/deployment/src/main/java/org/apache/camel/quarkus/component/weaviate/deployment/WeaviateProcessor.java index c316f8ea2f..17c8870ce2 100644 --- a/extensions/weaviate/deployment/src/main/java/org/apache/camel/quarkus/component/weaviate/deployment/WeaviateProcessor.java +++ b/extensions/weaviate/deployment/src/main/java/org/apache/camel/quarkus/component/weaviate/deployment/WeaviateProcessor.java @@ -18,12 +18,17 @@ package org.apache.camel.quarkus.component.weaviate.deployment; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.IndexDependencyBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; import org.jboss.jandex.IndexView; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.ClassRemapper; +import org.objectweb.asm.commons.Remapper; class WeaviateProcessor { @@ -40,28 +45,52 @@ class WeaviateProcessor { String[] dtos = index.getKnownClasses().stream() .map(ci -> ci.name().toString()) - .filter(n -> n.startsWith("io.weaviate.client.v1.") - && n.contains(".model")) + .filter(n -> n.startsWith("io.weaviate.client6.v1.api.")) .sorted() .toArray(String[]::new); reflectiveClass.produce(ReflectiveClassBuildItem.builder(dtos).methods().fields().build()); - - //error handler - reflectiveClass.produce( - ReflectiveClassBuildItem - .builder(new String[] { "io.weaviate.client.base.WeaviateErrorResponse", - "io.weaviate.client.base.WeaviateErrorMessage", "io.weaviate.client.base.WeaviateError" }) - .methods().fields().build()); } @BuildStep IndexDependencyBuildItem registerDependencyForIndex() { - return new IndexDependencyBuildItem("io.weaviate", "client"); + return new IndexDependencyBuildItem("io.weaviate", "client6"); } @BuildStep RuntimeInitializedClassBuildItem runtimeInitializedClasses() { return new RuntimeInitializedClassBuildItem("com.google.protobuf.JavaFeaturesProto"); } + + @BuildStep + void relocateShadedGrpcCalls( + CombinedIndexBuildItem index, + BuildProducer<BytecodeTransformerBuildItem> transformers) { + + index.getIndex().getKnownClasses().stream() + .filter(ci -> ci.name().toString().startsWith("io.weaviate.client6")) + .forEach(ci -> transformers.produce(new BytecodeTransformerBuildItem( + ci.name().toString(), + (name, cv) -> new ShadedRelocationVisitor(cv)))); + } + + private static class ShadedRelocationVisitor extends ClassRemapper { + ShadedRelocationVisitor(ClassVisitor cv) { + super(Opcodes.ASM9, cv, new Remapper(Opcodes.ASM9) { + @Override + public String map(String internalName) { + if (internalName == null) { + return null; + } + if (internalName.startsWith("io/grpc/netty/shaded/io/grpc")) { + return internalName.replace("io/grpc/netty/shaded/io/grpc", "io/grpc"); + } + if (internalName.startsWith("io/grpc/netty/shaded/io/netty")) { + return internalName.replace("io/grpc/netty/shaded/io/netty", "io/netty"); + } + return super.map(internalName); + } + }); + } + } } diff --git a/extensions/weaviate/runtime/pom.xml b/extensions/weaviate/runtime/pom.xml index 16166e0651..64d944bfdd 100644 --- a/extensions/weaviate/runtime/pom.xml +++ b/extensions/weaviate/runtime/pom.xml @@ -52,6 +52,10 @@ <groupId>org.apache.camel</groupId> <artifactId>camel-weaviate</artifactId> </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-grpc-common</artifactId> + </dependency> </dependencies> <build> diff --git a/integration-tests/weaviate/src/main/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateResource.java b/integration-tests/weaviate/src/main/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateResource.java index b31e40ab1a..3cdcea69ef 100644 --- a/integration-tests/weaviate/src/main/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateResource.java +++ b/integration-tests/weaviate/src/main/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateResource.java @@ -19,11 +19,11 @@ package org.apache.camel.quarkus.component.weaviate.it; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; -import io.weaviate.client.base.Result; -import io.weaviate.client.v1.data.model.WeaviateObject; -import io.weaviate.client.v1.graphql.model.GraphQLResponse; +import io.weaviate.client6.v1.api.collections.WeaviateObject; +import io.weaviate.client6.v1.api.collections.query.QueryResponse; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.ws.rs.POST; @@ -40,6 +40,8 @@ import org.jboss.logging.Logger; public class WeaviateResource { public static final String WEAVIATE_CONTAINER_ADDRESS = "cq.weaviate.container.address"; + public static final String WEAVIATE_CONTAINER_GRPC_HOST = "cq.weaviate.container.grpc.host"; + public static final String WEAVIATE_CONTAINER_GRPC_PORT = "cq.weaviate.container.grpc.port"; public static final String WEAVIATE_API_KEY_ENV = "WEAVIATE_API_KEY"; public static final String WEAVIATE_HOST_ENV = "WEAVIATE_HOST"; @@ -48,6 +50,7 @@ public class WeaviateResource { @Inject CamelContext context; + @SuppressWarnings("unchecked") @Path("/request") @POST @Produces(MediaType.APPLICATION_JSON) @@ -57,7 +60,6 @@ public class WeaviateResource { Object body = headers.get("body"); headers.remove("body"); - //convert Double to Float in body list (for create) if (body instanceof List) { body = ((List<?>) body).stream().map(o -> o instanceof Double ? ((Double) o).floatValue() : o) .collect(Collectors.toList()); @@ -69,25 +71,36 @@ public class WeaviateResource { .withHeaders(headers) .request(Exchange.class); - Result<?> result = response.getIn().getBody(Result.class); - LOG.debugf("Response for collections with headers (%s) is: \"%s\".", headers, result); + if (response.getException() != null) { + HashMap<String, Object> errorMap = new HashMap<>(); + errorMap.put("error", response.getException().getMessage()); + return Response.ok(errorMap).build(); + } + + Object result = response.getIn().getBody(); + LOG.debugf("Response for request with headers (%s) is: \"%s\".", headers, result); if (result != null) { - HashMap<String, Object> map = new HashMap(); - map.put("error", result.getError() == null ? "" : result.getError()); - - if (result.getResult() instanceof Boolean) { - map.put("result", result.getResult()); - } else if (result.getResult() instanceof WeaviateObject) { - map.put("result", ((WeaviateObject) result.getResult()).getId()); - map.put("resultProperties", ((WeaviateObject) result.getResult()).getProperties()); - } else if (result.getResult() instanceof List) { - @SuppressWarnings("unchecked") - List<WeaviateObject> objects = (List<WeaviateObject>) result.getResult(); - map.put("result", - objects.stream().collect(Collectors.toMap(WeaviateObject::getId, WeaviateObject::getProperties))); - } else if (result.getResult() instanceof GraphQLResponse) { - map.put("result", result.getResult()); + HashMap<String, Object> map = new HashMap<>(); + + if (result instanceof Boolean) { + map.put("result", result); + } else if (result instanceof WeaviateObject) { + WeaviateObject<Map<String, Object>> wo = (WeaviateObject<Map<String, Object>>) result; + map.put("result", wo.uuid()); + map.put("resultProperties", wo.properties()); + } else if (result instanceof Optional) { + Optional<WeaviateObject<Map<String, Object>>> opt = (Optional<WeaviateObject<Map<String, Object>>>) result; + if (opt.isPresent()) { + WeaviateObject<Map<String, Object>> wo = opt.get(); + map.put("result", Map.of(wo.uuid(), wo.properties())); + } + } else if (result instanceof QueryResponse) { + QueryResponse<Map<String, Object>> qr = (QueryResponse<Map<String, Object>>) result; + List<Map<String, Object>> objects = qr.objects().stream() + .map(WeaviateObject::properties) + .collect(Collectors.toList()); + map.put("result", objects); } return Response.ok(map).build(); diff --git a/integration-tests/weaviate/src/main/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateRoutes.java b/integration-tests/weaviate/src/main/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateRoutes.java index a32a500461..9edecdb1e7 100644 --- a/integration-tests/weaviate/src/main/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateRoutes.java +++ b/integration-tests/weaviate/src/main/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateRoutes.java @@ -28,6 +28,12 @@ public class WeaviateRoutes extends RouteBuilder { @ConfigProperty(name = WeaviateResource.WEAVIATE_CONTAINER_ADDRESS) Optional<String> weaviateContainerAddress; + @ConfigProperty(name = WeaviateResource.WEAVIATE_CONTAINER_GRPC_HOST) + Optional<String> weaviateGrpcHost; + + @ConfigProperty(name = WeaviateResource.WEAVIATE_CONTAINER_GRPC_PORT) + Optional<String> weaviateGrpcPort; + @ConfigProperty(name = WeaviateResource.WEAVIATE_HOST_ENV) Optional<String> host; @@ -42,7 +48,14 @@ public class WeaviateRoutes extends RouteBuilder { private String getUrl() { if (weaviateContainerAddress.isPresent()) { - return "weaviate:test-collection?scheme=http&host=" + weaviateContainerAddress.get(); + String url = "weaviate:test-collection?host=" + weaviateContainerAddress.get(); + if (weaviateGrpcHost.isPresent()) { + url += "&grpcHost=" + weaviateGrpcHost.get(); + } + if (weaviateGrpcPort.isPresent()) { + url += "&grpcPort=" + weaviateGrpcPort.get(); + } + return url; } if (host.isPresent() && apiKey.isPresent()) { diff --git a/integration-tests/weaviate/src/test/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateTest.java b/integration-tests/weaviate/src/test/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateTest.java index 65b66180d1..04e6e14f6c 100644 --- a/integration-tests/weaviate/src/test/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateTest.java +++ b/integration-tests/weaviate/src/test/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateTest.java @@ -78,12 +78,8 @@ class WeaviateTest { if (collectionCreated) { deleteCollection(collectionName); - //verify that collection is removed query(collectionName, Arrays.asList(0.15f, 0.25f, 0.35f), Map.of("title", "", "content", ""), true) - .body("error.statusCode", Matchers.equalTo(422)) - .body("error.messages.message", - Matchers.hasItem( - "no graphql provider present, this is most likely because no schema is present. Import a schema first!")); + .body("error", Matchers.not(IsEmptyString.emptyOrNullString())); } } } @@ -106,25 +102,23 @@ class WeaviateTest { Map.of("title", "Third Article", "content", "The content of the third article.")); query(collectionName, Arrays.asList(0.15f, 0.25f, 0.35f), Map.of("title", "", "content", "")) - .body("result.data.Get." + collectionName, Matchers.hasSize(2)) - .body("result.data.Get." + collectionName + "[0]", Matchers.aMapWithSize(2)) - .body("result.data.Get." + collectionName + "[0].title", Matchers.equalTo("Second Article")) - .body("result.data.Get." + collectionName + "[1].title", Matchers.equalTo("First Article")); + .body("result", Matchers.hasSize(2)) + .body("result[0]", Matchers.aMapWithSize(2)) + .body("result[0].title", Matchers.equalTo("Second Article")) + .body("result[1].title", Matchers.equalTo("First Article")); query(collectionName, Arrays.asList(0.3f, 0.4f, 0.5f), Map.of("title", "", "content", "")) - .body("result.data.Get." + collectionName, Matchers.hasSize(2)) - .body("result.data.Get." + collectionName + "[0]", Matchers.aMapWithSize(2)) - .body("result.data.Get." + collectionName + "[0].title", Matchers.equalTo("Third Article")) - .body("result.data.Get." + collectionName + "[1].title", Matchers.equalTo("Second Article")); + .body("result", Matchers.hasSize(2)) + .body("result[0]", Matchers.aMapWithSize(2)) + .body("result[0].title", Matchers.equalTo("Third Article")) + .body("result[1].title", Matchers.equalTo("Second Article")); } finally { if (collectionCreated) { deleteCollection(collectionName); - //verify that collection is removed query(collectionName, Arrays.asList(0.15f, 0.25f, 0.35f), Map.of("title", "", "content", ""), true) - .body("error.statusCode", Matchers.equalTo(422)); - //message is already covered by operation test + .body("error", Matchers.not(IsEmptyString.emptyOrNullString())); } } } @@ -137,7 +131,6 @@ class WeaviateTest { .post("/weaviate/request") .then() .statusCode(200) - .body("error", IsEmptyString.emptyOrNullString()) .body("result", Matchers.is(true)); } @@ -149,7 +142,6 @@ class WeaviateTest { .post("/weaviate/request") .then() .statusCode(200) - .body("error", IsEmptyString.emptyOrNullString()) .body("result", Matchers.is(true)); } @@ -167,7 +159,6 @@ class WeaviateTest { .post("/weaviate/request") .then() .statusCode(200) - .body("error", IsEmptyString.emptyOrNullString()) .extract().path("result"); Assertions.assertNotNull(createdId); @@ -186,8 +177,7 @@ class WeaviateTest { .body(payload) .post("/weaviate/request") .then() - .statusCode(200) - .body("error", IsEmptyString.emptyOrNullString()); + .statusCode(200); } private void updateById(String collectionName, String id, List<Float> values, @@ -206,7 +196,7 @@ class WeaviateTest { .post("/weaviate/request") .then() .statusCode(200) - .body("error", IsEmptyString.emptyOrNullString()); + .body("result", Matchers.is(true)); } public void deleteById(String collectionName, String id) { @@ -221,7 +211,6 @@ class WeaviateTest { .body(payload) .post("/weaviate/request") .then().statusCode(200) - .body("error", IsEmptyString.emptyOrNullString()) .body("result", Matchers.is(true)); } @@ -246,10 +235,6 @@ class WeaviateTest { .then() .statusCode(200); - if (!expectError) { - response.body("error", IsEmptyString.emptyOrNullString()); - } - return response; } } diff --git a/integration-tests/weaviate/src/test/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateTestResource.java b/integration-tests/weaviate/src/test/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateTestResource.java index bb69d45e5d..cd37189b21 100644 --- a/integration-tests/weaviate/src/test/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateTestResource.java +++ b/integration-tests/weaviate/src/test/java/org/apache/camel/quarkus/component/weaviate/it/WeaviateTestResource.java @@ -29,6 +29,8 @@ import org.testcontainers.weaviate.WeaviateContainer; public class WeaviateTestResource implements QuarkusTestResourceLifecycleManager { + private static final int GRPC_PORT = 50051; + private static final DockerImageName WEAVIATE_IMAGE = DockerImageName .parse(ConfigProvider.getConfig().getValue("weaviate.container.image", String.class)) .asCompatibleSubstituteFor("semitechnologies/weaviate"); @@ -38,7 +40,6 @@ public class WeaviateTestResource implements QuarkusTestResourceLifecycleManager @Override public Map<String, String> start() { - //detect real/mock backend Optional<String> apiKey = ConfigProvider.getConfig().getOptionalValue(WeaviateResource.WEAVIATE_API_KEY_ENV, String.class); Optional<String> hostKey = ConfigProvider.getConfig().getOptionalValue(WeaviateResource.WEAVIATE_HOST_ENV, @@ -53,7 +54,9 @@ public class WeaviateTestResource implements QuarkusTestResourceLifecycleManager container.start(); return Map.of( - WeaviateResource.WEAVIATE_CONTAINER_ADDRESS, container.getHttpHostAddress()); + WeaviateResource.WEAVIATE_CONTAINER_ADDRESS, container.getHttpHostAddress(), + WeaviateResource.WEAVIATE_CONTAINER_GRPC_HOST, container.getHost(), + WeaviateResource.WEAVIATE_CONTAINER_GRPC_PORT, String.valueOf(container.getMappedPort(GRPC_PORT))); } else if (!startMockBackend && !realApiProvided) { throw new IllegalStateException( "Set %s and %s env vars if you set CAMEL_QUARKUS_START_MOCK_BACKEND=false"
