From 7e09841b4abba428ee2c62e8dc85be415fc561dd Mon Sep 17 00:00:00 2001 From: salaboy Date: Tue, 25 Jun 2024 06:20:41 +0100 Subject: [PATCH] Adding metadata endpoint (#1049) * adding metadata endpoint Signed-off-by: salaboy * apply formatting Signed-off-by: salaboy * updating formatting Signed-off-by: salaboy * updating formatting Signed-off-by: salaboy * reverting formatting Signed-off-by: salaboy * making domain classes final Signed-off-by: salaboy * making domain model immutable Signed-off-by: salaboy * equals/hashcode Signed-off-by: salaboy * updating tests and clients Signed-off-by: salaboy * rebasing after http client removed Signed-off-by: salaboy * reverting delete Signed-off-by: salaboy * adding space Signed-off-by: salaboy * update copy and spaces Signed-off-by: salaboy * eof line Signed-off-by: salaboy * another new line Signed-off-by: salaboy * adding coverage tests Signed-off-by: salaboy * removing equals and hashcode Signed-off-by: salaboy --------- Signed-off-by: salaboy --- .../main/java/io/dapr/client/DaprClient.java | 8 +++ .../java/io/dapr/client/DaprClientImpl.java | 55 +++++++++++++-- .../dapr/client/domain/ComponentMetadata.java | 52 ++++++++++++++ .../io/dapr/client/domain/DaprMetadata.java | 62 +++++++++++++++++ .../io/dapr/client/domain/RuleMetadata.java | 30 ++++++++ .../client/domain/SubscriptionMetadata.java | 62 +++++++++++++++++ .../io/dapr/client/DaprClientGrpcTest.java | 68 ++++++++++++++++++- 7 files changed, 332 insertions(+), 5 deletions(-) create mode 100644 sdk/src/main/java/io/dapr/client/domain/ComponentMetadata.java create mode 100644 sdk/src/main/java/io/dapr/client/domain/DaprMetadata.java create mode 100644 sdk/src/main/java/io/dapr/client/domain/RuleMetadata.java create mode 100644 sdk/src/main/java/io/dapr/client/domain/SubscriptionMetadata.java diff --git a/sdk/src/main/java/io/dapr/client/DaprClient.java b/sdk/src/main/java/io/dapr/client/DaprClient.java index fe01b356b..9b713f7c7 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClient.java +++ b/sdk/src/main/java/io/dapr/client/DaprClient.java @@ -14,6 +14,7 @@ limitations under the License. package io.dapr.client; import io.dapr.client.domain.ConfigurationItem; +import io.dapr.client.domain.DaprMetadata; import io.dapr.client.domain.DeleteStateRequest; import io.dapr.client.domain.ExecuteStateTransactionRequest; import io.dapr.client.domain.GetBulkSecretRequest; @@ -671,6 +672,13 @@ public interface DaprClient extends AutoCloseable { */ > T newGrpcStub(String appId, Function stubBuilder); + /** + * Fetches Dapr Metadata from the metadata endpoint. + * + * @return DaprMetadata containing Dapr Metadata from the metadata endpoint. + */ + Mono getMetadata(); + /** * Gracefully shutdown the dapr runtime. * diff --git a/sdk/src/main/java/io/dapr/client/DaprClientImpl.java b/sdk/src/main/java/io/dapr/client/DaprClientImpl.java index bcc1418b1..3e82862e2 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClientImpl.java +++ b/sdk/src/main/java/io/dapr/client/DaprClientImpl.java @@ -20,7 +20,9 @@ import io.dapr.client.domain.BulkPublishEntry; import io.dapr.client.domain.BulkPublishRequest; import io.dapr.client.domain.BulkPublishResponse; import io.dapr.client.domain.BulkPublishResponseFailedEntry; +import io.dapr.client.domain.ComponentMetadata; import io.dapr.client.domain.ConfigurationItem; +import io.dapr.client.domain.DaprMetadata; import io.dapr.client.domain.DeleteStateRequest; import io.dapr.client.domain.ExecuteStateTransactionRequest; import io.dapr.client.domain.GetBulkSecretRequest; @@ -36,11 +38,13 @@ import io.dapr.client.domain.PublishEventRequest; import io.dapr.client.domain.QueryStateItem; import io.dapr.client.domain.QueryStateRequest; import io.dapr.client.domain.QueryStateResponse; +import io.dapr.client.domain.RuleMetadata; import io.dapr.client.domain.SaveStateRequest; import io.dapr.client.domain.State; import io.dapr.client.domain.StateOptions; import io.dapr.client.domain.SubscribeConfigurationRequest; import io.dapr.client.domain.SubscribeConfigurationResponse; +import io.dapr.client.domain.SubscriptionMetadata; import io.dapr.client.domain.TransactionalStateOperation; import io.dapr.client.domain.UnlockRequest; import io.dapr.client.domain.UnlockResponseStatus; @@ -58,6 +62,10 @@ import io.dapr.utils.TypeRef; import io.dapr.v1.CommonProtos; import io.dapr.v1.DaprGrpc; import io.dapr.v1.DaprProtos; +import io.dapr.v1.DaprProtos.PubsubSubscription; +import io.dapr.v1.DaprProtos.PubsubSubscriptionRule; +import io.dapr.v1.DaprProtos.RegisteredComponents; +import io.grpc.CallOptions; import io.grpc.Channel; import io.grpc.stub.AbstractStub; import io.grpc.stub.StreamObserver; @@ -118,7 +126,8 @@ public class DaprClientImpl extends AbstractDaprClient { private final DaprHttp httpClient; /** - * Default access level constructor, in order to create an instance of this class use io.dapr.client.DaprClientBuilder + * Default access level constructor, in order to create an instance of this + * class use io.dapr.client.DaprClientBuilder * * @param channel Facade for the managed GRPC channel * @param asyncStub async gRPC stub @@ -1169,8 +1178,7 @@ public class DaprClientImpl extends AbstractDaprClient { key, configurationItem.getValue(), configurationItem.getVersion(), - configurationItem.getMetadataMap() - ); + configurationItem.getMetadataMap()); } /** @@ -1231,4 +1239,43 @@ public class DaprClientImpl extends AbstractDaprClient { } }; } -} + + @Override + public Mono getMetadata() { + DaprProtos.GetMetadataRequest metadataRequest = DaprProtos.GetMetadataRequest.newBuilder().build(); + return Mono.deferContextual( + context -> this.createMono( + it -> intercept(context, asyncStub).getMetadata(metadataRequest, it))) + .map( + it -> { + try { + return buildDaprMetadata(it); + } catch (IOException ex) { + throw DaprException.propagate(ex); + } + }); + } + + private DaprMetadata buildDaprMetadata( + DaprProtos.GetMetadataResponse response) throws IOException { + List registeredComponentsList = response.getRegisteredComponentsList(); + + List components = new ArrayList<>(); + for (RegisteredComponents rc : registeredComponentsList) { + components.add(new ComponentMetadata(rc.getName(), rc.getType(), rc.getVersion())); + } + + List subscriptionsList = response.getSubscriptionsList(); + List subscriptions = new ArrayList<>(); + for (PubsubSubscription s : subscriptionsList) { + List rulesList = s.getRules().getRulesList(); + List rules = new ArrayList<>(); + for (PubsubSubscriptionRule r : rulesList) { + rules.add(new RuleMetadata(r.getPath())); + } + subscriptions.add(new SubscriptionMetadata(s.getTopic(), s.getPubsubName(), s.getDeadLetterTopic(), rules)); + } + + return new DaprMetadata(response.getId(), response.getRuntimeVersion(), components, subscriptions); + } +} \ No newline at end of file diff --git a/sdk/src/main/java/io/dapr/client/domain/ComponentMetadata.java b/sdk/src/main/java/io/dapr/client/domain/ComponentMetadata.java new file mode 100644 index 000000000..8e7f1a8cc --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/ComponentMetadata.java @@ -0,0 +1,52 @@ +/* + * Copyright 2024 The Dapr 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 + * 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 io.dapr.client.domain; + +import java.util.Objects; + +/** + * ComponentMetadata describes a Dapr Component. + */ +public final class ComponentMetadata { + + private String name; + private String type; + private String version; + + /** + * Constructor for a ComponentMetadata. + * + * @param name of the component + * @param type component type + * @param version version of the component + */ + public ComponentMetadata(String name, String type, String version) { + this.name = name; + this.type = type; + this.version = version; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public String getVersion() { + return version; + } + +} diff --git a/sdk/src/main/java/io/dapr/client/domain/DaprMetadata.java b/sdk/src/main/java/io/dapr/client/domain/DaprMetadata.java new file mode 100644 index 000000000..c99804d96 --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/DaprMetadata.java @@ -0,0 +1,62 @@ +/* + * Copyright 2024 The Dapr 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 + * 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 io.dapr.client.domain; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * DaprMetadata describes the Dapr Metadata. + */ +public final class DaprMetadata { + + private String id; + private String runtimeVersion; + private List components; + private List subscriptions; + + /** + * Constructor for a DaprMetadata. + * + * @param id of the application + * @param runtimeVersion Dapr version + * @param components list of registered componnets + * @param subscriptions list of registered subscription + */ + public DaprMetadata(String id, String runtimeVersion, List components, + List subscriptions) { + this.id = id; + this.runtimeVersion = runtimeVersion; + this.components = components == null ? Collections.emptyList() : Collections.unmodifiableList(components); + this.subscriptions = subscriptions == null ? Collections.emptyList() : Collections.unmodifiableList(subscriptions); + } + + public String getId() { + return id; + } + + public String getRuntimeVersion() { + return runtimeVersion; + } + + public List getComponents() { + return components; + } + + public List getSubscriptions() { + return subscriptions; + } + +} diff --git a/sdk/src/main/java/io/dapr/client/domain/RuleMetadata.java b/sdk/src/main/java/io/dapr/client/domain/RuleMetadata.java new file mode 100644 index 000000000..61a033144 --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/RuleMetadata.java @@ -0,0 +1,30 @@ +/* + * Copyright 2024 The Dapr 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 + * 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 io.dapr.client.domain; + +/** + * RuleMetadata describes the Subscription Rule's Metadata. + */ +public final class RuleMetadata { + private String path; + + public RuleMetadata(String path) { + this.path = path; + } + + public String getPath() { + return path; + } + +} diff --git a/sdk/src/main/java/io/dapr/client/domain/SubscriptionMetadata.java b/sdk/src/main/java/io/dapr/client/domain/SubscriptionMetadata.java new file mode 100644 index 000000000..36509c216 --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/SubscriptionMetadata.java @@ -0,0 +1,62 @@ +/* + * Copyright 2024 The Dapr 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 + * 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 io.dapr.client.domain; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * SubscriptionMetadata describes the Subscription Metadata. + */ +public final class SubscriptionMetadata { + private String topic; + private String pubsubname; + private String deadLetterTopic; + private List rules; + + /** + * Constructor for a SubscriptionMetadata. + * + * @param topic of the pubsub component + * @param pubsubname component name + * @param deadLetterTopic dead letter topic + * @param rules subscription path rules + */ + public SubscriptionMetadata(String topic, String pubsubname, String deadLetterTopic, List rules) { + this.topic = topic; + this.pubsubname = pubsubname; + this.deadLetterTopic = deadLetterTopic; + this.rules = rules == null ? Collections.emptyList() : Collections.unmodifiableList(rules); + } + + public String getTopic() { + return topic; + } + + public String getPubsubname() { + return pubsubname; + } + + + public String getDeadLetterTopic() { + return deadLetterTopic; + } + + + public List getRules() { + return rules; + } + +} diff --git a/sdk/src/test/java/io/dapr/client/DaprClientGrpcTest.java b/sdk/src/test/java/io/dapr/client/DaprClientGrpcTest.java index 7d71939a1..93dd80203 100644 --- a/sdk/src/test/java/io/dapr/client/DaprClientGrpcTest.java +++ b/sdk/src/test/java/io/dapr/client/DaprClientGrpcTest.java @@ -17,6 +17,7 @@ import com.google.protobuf.Any; import com.google.protobuf.ByteString; import com.google.protobuf.Empty; import io.dapr.client.domain.ConfigurationItem; +import io.dapr.client.domain.DaprMetadata; import io.dapr.client.domain.DeleteStateRequest; import io.dapr.client.domain.ExecuteStateTransactionRequest; import io.dapr.client.domain.GetBulkStateRequest; @@ -28,12 +29,16 @@ import io.dapr.client.domain.SubscribeConfigurationResponse; import io.dapr.client.domain.TransactionalStateOperation; import io.dapr.client.domain.UnsubscribeConfigurationRequest; import io.dapr.client.domain.UnsubscribeConfigurationResponse; +import io.dapr.exceptions.DaprError; +import io.dapr.exceptions.DaprException; import io.dapr.serializer.DaprObjectSerializer; import io.dapr.serializer.DefaultObjectSerializer; import io.dapr.utils.TypeRef; import io.dapr.v1.CommonProtos; import io.dapr.v1.DaprGrpc; import io.dapr.v1.DaprProtos; +import io.dapr.v1.DaprProtos.PubsubSubscription; +import io.dapr.v1.DaprProtos.RegisteredComponents; import io.grpc.Status; import io.grpc.StatusRuntimeException; import io.grpc.protobuf.StatusProto; @@ -2078,4 +2083,65 @@ public class DaprClientGrpcTest { return StatusProto.toStatusRuntimeException(status); } -} + + @Test + public void getMetadataTest() { + + RegisteredComponents registeredComponents = DaprProtos.RegisteredComponents.newBuilder() + .setName("statestore") + .setType("state.redis") + .setVersion("v1") + .build(); + PubsubSubscription pubsubSubscription = DaprProtos.PubsubSubscription.newBuilder() + .setDeadLetterTopic("") + .setPubsubName("pubsub") + .setTopic("topic") + .setRules(DaprProtos.PubsubSubscriptionRules.newBuilder() + .addRules(DaprProtos.PubsubSubscriptionRule.newBuilder().setPath("/events").build()).build()) + .build(); + DaprProtos.GetMetadataResponse responseEnvelope = DaprProtos.GetMetadataResponse.newBuilder() + .setId("app") + .setRuntimeVersion("1.1x.x") + .addAllRegisteredComponents(Collections.singletonList(registeredComponents)) + .addAllSubscriptions(Collections.singletonList(pubsubSubscription)) + .build(); + doAnswer((Answer) invocation -> { + StreamObserver observer = (StreamObserver) invocation + .getArguments()[1]; + observer.onNext(responseEnvelope); + observer.onCompleted(); + return null; + }).when(daprStub).getMetadata(any(DaprProtos.GetMetadataRequest.class), any()); + + Mono result = client.getMetadata(); + DaprMetadata metadata = result.block(); + assertNotNull(metadata); + assertEquals("app", metadata.getId()); + assertEquals("1.1x.x", metadata.getRuntimeVersion()); + assertEquals(1, metadata.getComponents().size()); + assertEquals(registeredComponents.getName(), metadata.getComponents().get(0).getName()); + assertEquals(registeredComponents.getVersion(), metadata.getComponents().get(0).getVersion()); + assertEquals(registeredComponents.getType(), metadata.getComponents().get(0).getType()); + assertEquals(1, metadata.getSubscriptions().size()); + assertEquals(pubsubSubscription.getPubsubName(), metadata.getSubscriptions().get(0).getPubsubname()); + assertEquals(pubsubSubscription.getTopic(), metadata.getSubscriptions().get(0).getTopic()); + assertEquals(1, metadata.getSubscriptions().get(0).getRules().size()); + assertEquals(pubsubSubscription.getRules().getRules(0).getPath(), metadata.getSubscriptions().get(0).getRules().get(0).getPath()); + + } + + @Test + public void getMetadataExceptionTest() { + doAnswer((Answer) invocation -> { + throw new RuntimeException(); + }).when(daprStub).getMetadata(any(DaprProtos.GetMetadataRequest.class), any()); + + Mono result = client.getMetadata(); + + assertThrowsDaprException( + RuntimeException.class, + "UNKNOWN", + "UNKNOWN: ", + () -> result.block()); + } +} \ No newline at end of file