Search in sources :

Example 1 with CommandResponse

use of org.eclipse.hono.client.command.CommandResponse in project hono by eclipse.

the class AbstractVertxBasedHttpProtocolAdapter method uploadCommandResponseMessage.

/**
 * Uploads a command response message to Hono.
 *
 * @param ctx The routing context of the HTTP request.
 * @param tenant The tenant of the device from which the command response was received.
 * @param deviceId The device from which the command response was received.
 * @param commandRequestId The id of the command that the response has been sent in reply to.
 * @param responseStatus The HTTP status code that the device has provided in its request to indicate
 *                       the outcome of processing the command (may be {@code null}).
 * @throws NullPointerException if ctx, tenant or deviceId are {@code null}.
 */
public final void uploadCommandResponseMessage(final HttpContext ctx, final String tenant, final String deviceId, final String commandRequestId, final Integer responseStatus) {
    Objects.requireNonNull(ctx);
    Objects.requireNonNull(tenant);
    Objects.requireNonNull(deviceId);
    final Buffer payload = ctx.getRoutingContext().getBody();
    final String contentType = ctx.getContentType();
    log.debug("processing response to command [tenantId: {}, deviceId: {}, cmd-req-id: {}, status code: {}]", tenant, deviceId, commandRequestId, responseStatus);
    final Device authenticatedDevice = ctx.getAuthenticatedDevice();
    final Span currentSpan = TracingHelper.buildChildSpan(tracer, TracingHandler.serverSpanContext(ctx.getRoutingContext()), "upload Command response", getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenant).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).withTag(Constants.HEADER_COMMAND_RESPONSE_STATUS, responseStatus).withTag(Constants.HEADER_COMMAND_REQUEST_ID, commandRequestId).withTag(TracingHelper.TAG_AUTHENTICATED.getKey(), authenticatedDevice != null).start();
    final CommandResponse cmdResponseOrNull = CommandResponse.fromRequestId(commandRequestId, tenant, deviceId, payload, contentType, responseStatus);
    final Future<TenantObject> tenantTracker = getTenantConfiguration(tenant, currentSpan.context());
    final Future<CommandResponse> commandResponseTracker = cmdResponseOrNull != null ? Future.succeededFuture(cmdResponseOrNull) : Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, String.format("command-request-id [%s] or status code [%s] is missing/invalid", commandRequestId, responseStatus)));
    final int payloadSize = Optional.ofNullable(payload).map(Buffer::length).orElse(0);
    CompositeFuture.all(tenantTracker, commandResponseTracker).compose(commandResponse -> {
        final Future<RegistrationAssertion> deviceRegistrationTracker = getRegistrationAssertion(tenant, deviceId, authenticatedDevice, currentSpan.context());
        final Future<Void> tenantValidationTracker = CompositeFuture.all(isAdapterEnabled(tenantTracker.result()), checkMessageLimit(tenantTracker.result(), payloadSize, currentSpan.context())).map(ok -> null);
        return CompositeFuture.all(tenantValidationTracker, deviceRegistrationTracker).compose(ok -> sendCommandResponse(tenantTracker.result(), deviceRegistrationTracker.result(), commandResponseTracker.result(), currentSpan.context())).map(delivery -> {
            log.trace("delivered command response [command-request-id: {}] to application", commandRequestId);
            currentSpan.log("delivered command response to application");
            currentSpan.finish();
            metrics.reportCommand(Direction.RESPONSE, tenant, tenantTracker.result(), ProcessingOutcome.FORWARDED, payloadSize, getMicrometerSample(ctx.getRoutingContext()));
            ctx.response().setStatusCode(HttpURLConnection.HTTP_ACCEPTED);
            ctx.response().end();
            return delivery;
        });
    }).otherwise(t -> {
        log.debug("could not send command response [command-request-id: {}] to application", commandRequestId, t);
        TracingHelper.logError(currentSpan, t);
        currentSpan.finish();
        metrics.reportCommand(Direction.RESPONSE, tenant, tenantTracker.result(), ProcessingOutcome.from(t), payloadSize, getMicrometerSample(ctx.getRoutingContext()));
        ctx.fail(t);
        return null;
    });
}
Also used : Buffer(io.vertx.core.buffer.Buffer) HttpURLConnection(java.net.HttpURLConnection) HttpServer(io.vertx.core.http.HttpServer) Router(io.vertx.ext.web.Router) RoutingContext(io.vertx.ext.web.RoutingContext) BodyHandler(io.vertx.ext.web.handler.BodyHandler) Tags(io.opentracing.tag.Tags) ProcessingOutcome(org.eclipse.hono.service.metric.MetricsTags.ProcessingOutcome) EndpointType(org.eclipse.hono.service.metric.MetricsTags.EndpointType) TtdStatus(org.eclipse.hono.service.metric.MetricsTags.TtdStatus) DeviceCredentials(org.eclipse.hono.adapter.auth.device.DeviceCredentials) References(io.opentracing.References) Duration(java.time.Duration) Map(java.util.Map) WebSpanDecorator(org.eclipse.hono.service.http.WebSpanDecorator) TracingHelper(org.eclipse.hono.tracing.TracingHelper) CommandContext(org.eclipse.hono.client.command.CommandContext) MetricsTags(org.eclipse.hono.service.metric.MetricsTags) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) MessageHelper(org.eclipse.hono.util.MessageHelper) Future(io.vertx.core.Future) Device(org.eclipse.hono.auth.Device) Objects(java.util.Objects) List(java.util.List) ComponentMetaDataDecorator(org.eclipse.hono.service.http.ComponentMetaDataDecorator) TenantTraceSamplingHelper(org.eclipse.hono.tracing.TenantTraceSamplingHelper) Buffer(io.vertx.core.buffer.Buffer) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) HttpServerResponse(io.vertx.core.http.HttpServerResponse) DefaultFailureHandler(org.eclipse.hono.service.http.DefaultFailureHandler) Optional(java.util.Optional) Span(io.opentracing.Span) HttpContext(org.eclipse.hono.service.http.HttpContext) Command(org.eclipse.hono.client.command.Command) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) HashMap(java.util.HashMap) ClientErrorException(org.eclipse.hono.client.ClientErrorException) Constants(org.eclipse.hono.util.Constants) ArrayList(java.util.ArrayList) TracingHandler(org.eclipse.hono.service.http.TracingHandler) CompositeFuture(io.vertx.core.CompositeFuture) HttpUtils(org.eclipse.hono.service.http.HttpUtils) AsyncResult(io.vertx.core.AsyncResult) Strings(org.eclipse.hono.util.Strings) Route(io.vertx.ext.web.Route) CredentialsApiAuthProvider(org.eclipse.hono.adapter.auth.device.CredentialsApiAuthProvider) AbstractProtocolAdapterBase(org.eclipse.hono.adapter.AbstractProtocolAdapterBase) Direction(org.eclipse.hono.service.metric.MetricsTags.Direction) Promise(io.vertx.core.Promise) ServerErrorException(org.eclipse.hono.client.ServerErrorException) Sample(io.micrometer.core.instrument.Timer.Sample) CommandResponse(org.eclipse.hono.client.command.CommandResponse) TenantObject(org.eclipse.hono.util.TenantObject) SpanContext(io.opentracing.SpanContext) HttpServerOptions(io.vertx.core.http.HttpServerOptions) NoopSpan(io.opentracing.noop.NoopSpan) Handler(io.vertx.core.Handler) TenantObject(org.eclipse.hono.util.TenantObject) Device(org.eclipse.hono.auth.Device) ClientErrorException(org.eclipse.hono.client.ClientErrorException) Future(io.vertx.core.Future) CompositeFuture(io.vertx.core.CompositeFuture) CommandResponse(org.eclipse.hono.client.command.CommandResponse) Span(io.opentracing.Span) NoopSpan(io.opentracing.noop.NoopSpan)

Example 2 with CommandResponse

use of org.eclipse.hono.client.command.CommandResponse in project hono by eclipse.

the class AbstractProtocolAdapterBaseTest method testGetCommandResponseSenderSetOnCommandResponse.

/**
 * Verifies that the messaging type encoded in a command response message is getting used when sending the
 * command response.
 */
@Test
public void testGetCommandResponseSenderSetOnCommandResponse() {
    final CommandResponse kafkaResponse = CommandResponse.fromRequestId(Commands.encodeRequestIdParameters("", "replyTo", "4711", MessagingType.kafka), Constants.DEFAULT_TENANT, "4711", null, null, HttpURLConnection.HTTP_OK);
    final TenantObject tenant = new TenantObject("tenant", true);
    final var device = new RegistrationAssertion("4711");
    tenant.setProperty(TenantConstants.FIELD_EXT, Map.of(TenantConstants.FIELD_EXT_MESSAGING_TYPE, MessagingType.amqp.name()));
    adapter.sendCommandResponse(tenant, device, kafkaResponse, null);
    verify(kafkaCommandResponseSender).sendCommandResponse(eq(tenant), eq(device), eq(kafkaResponse), any());
    verify(amqpCommandResponseSender, never()).sendCommandResponse(any(TenantObject.class), any(RegistrationAssertion.class), any(CommandResponse.class), any());
    final CommandResponse amqpResponse = CommandResponse.fromRequestId(Commands.encodeRequestIdParameters("", "replyTo", "4711", MessagingType.amqp), Constants.DEFAULT_TENANT, "4711", null, null, HttpURLConnection.HTTP_OK);
    tenant.setProperty(TenantConstants.FIELD_EXT, Map.of(TenantConstants.FIELD_EXT_MESSAGING_TYPE, MessagingType.kafka.name()));
    adapter.sendCommandResponse(tenant, device, amqpResponse, null);
    verify(amqpCommandResponseSender).sendCommandResponse(eq(tenant), eq(device), eq(amqpResponse), any());
}
Also used : TenantObject(org.eclipse.hono.util.TenantObject) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) CommandResponse(org.eclipse.hono.client.command.CommandResponse) Test(org.junit.jupiter.api.Test)

Example 3 with CommandResponse

use of org.eclipse.hono.client.command.CommandResponse in project hono by eclipse.

the class AbstractProtocolAdapterBaseTest method testGetCommandResponseSenderConfiguredOnTenant.

/**
 * Verifies that when the messaging system to be used, as configured for the command response, is not available,
 * then the messaging system type configuration from the tenant is used.
 */
@Test
public void testGetCommandResponseSenderConfiguredOnTenant() {
    final var commandResponseSenderProvider = new MessagingClientProvider<CommandResponseSender>().setClient(amqpCommandResponseSender);
    messagingClientProviders = new MessagingClientProviders(new MessagingClientProvider<TelemetrySender>().setClient(amqpTelemetrySender), new MessagingClientProvider<EventSender>().setClient(amqpEventSender), commandResponseSenderProvider);
    properties = new ProtocolAdapterProperties();
    adapter = newProtocolAdapter(properties, ADAPTER_NAME);
    setCollaborators(adapter);
    final CommandResponse response = CommandResponse.fromRequestId(Commands.encodeRequestIdParameters("", "replyTo", "4711", MessagingType.kafka), Constants.DEFAULT_TENANT, "4711", null, null, HttpURLConnection.HTTP_OK);
    final TenantObject tenant = new TenantObject("tenant", true);
    final var device = new RegistrationAssertion("4711");
    tenant.setProperty(TenantConstants.FIELD_EXT, Map.of(TenantConstants.FIELD_EXT_MESSAGING_TYPE, MessagingType.amqp.name()));
    adapter.sendCommandResponse(tenant, device, response, null);
    verify(amqpCommandResponseSender).sendCommandResponse(eq(tenant), eq(device), eq(response), any());
}
Also used : ProtocolAdapterProperties(org.eclipse.hono.config.ProtocolAdapterProperties) TenantObject(org.eclipse.hono.util.TenantObject) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) CommandResponseSender(org.eclipse.hono.client.command.CommandResponseSender) EventSender(org.eclipse.hono.client.telemetry.EventSender) TelemetrySender(org.eclipse.hono.client.telemetry.TelemetrySender) CommandResponse(org.eclipse.hono.client.command.CommandResponse) Test(org.junit.jupiter.api.Test)

Example 4 with CommandResponse

use of org.eclipse.hono.client.command.CommandResponse in project hono by eclipse.

the class ProtonBasedCommandResponseSenderTest method testSenderClientCreationErrorIsMappedToServerError.

/**
 * Verifies that a ClientErrorException when creating an AMQP sender is returned as a server error
 * on the <em>sendCommandResponse</em> invocation.
 *
 * @param ctx The vert.x test context.
 */
@Test
public void testSenderClientCreationErrorIsMappedToServerError(final VertxTestContext ctx) {
    // GIVEN a scenario where creating the AMQP sender always fails with a client error
    when(connection.createSender(anyString(), any(), any())).thenReturn(Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_NOT_FOUND, "cannot open sender")));
    // WHEN sending a command response message
    final CommandResponse commandResponse = CommandResponse.fromRequestId(Commands.encodeRequestIdParameters(CORRELATION_ID, REPLY_TO_ID, DEVICE_ID, MessagingType.amqp), TENANT_ID, DEVICE_ID, null, null, HttpURLConnection.HTTP_OK);
    sender.sendCommandResponse(TenantObject.from(TENANT_ID), new RegistrationAssertion(DEVICE_ID), commandResponse, span.context()).onComplete(ctx.failing(thr -> {
        ctx.verify(() -> {
            // THEN the invocation is failed with a server error
            assertThat(thr).isInstanceOf(ServiceInvocationException.class);
            assertThat(((ServiceInvocationException) thr).getErrorCode()).isEqualTo(HttpURLConnection.HTTP_UNAVAILABLE);
        });
        ctx.completeNow();
    }));
}
Also used : ArgumentMatchers.any(org.mockito.ArgumentMatchers.any) HttpURLConnection(java.net.HttpURLConnection) VertxTestContext(io.vertx.junit5.VertxTestContext) BeforeEach(org.junit.jupiter.api.BeforeEach) ArgumentMatchers.anyLong(org.mockito.ArgumentMatchers.anyLong) ProtonDelivery(io.vertx.proton.ProtonDelivery) AmqpClientUnitTestHelper(org.eclipse.hono.client.amqp.test.AmqpClientUnitTestHelper) ClientErrorException(org.eclipse.hono.client.ClientErrorException) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) Commands(org.eclipse.hono.client.command.Commands) Timeout(io.vertx.junit5.Timeout) MessagingType(org.eclipse.hono.util.MessagingType) ArgumentCaptor(org.mockito.ArgumentCaptor) EventBus(io.vertx.core.eventbus.EventBus) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) Message(org.apache.qpid.proton.message.Message) TracingMockSupport(org.eclipse.hono.test.TracingMockSupport) HonoConnection(org.eclipse.hono.client.HonoConnection) ClientConfigProperties(org.eclipse.hono.config.ClientConfigProperties) ResourceLimits(org.eclipse.hono.util.ResourceLimits) Tracer(io.opentracing.Tracer) Vertx(io.vertx.core.Vertx) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) Mockito.when(org.mockito.Mockito.when) Truth.assertThat(com.google.common.truth.Truth.assertThat) Instant(java.time.Instant) MessageHelper(org.eclipse.hono.util.MessageHelper) VertxExtension(io.vertx.junit5.VertxExtension) Future(io.vertx.core.Future) Mockito.verify(org.mockito.Mockito.verify) CommandResponse(org.eclipse.hono.client.command.CommandResponse) TenantObject(org.eclipse.hono.util.TenantObject) TimeUnit(java.util.concurrent.TimeUnit) Test(org.junit.jupiter.api.Test) VertxMockSupport(org.eclipse.hono.test.VertxMockSupport) Span(io.opentracing.Span) ProtonSender(io.vertx.proton.ProtonSender) SendMessageSampler(org.eclipse.hono.client.SendMessageSampler) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) Mockito.mock(org.mockito.Mockito.mock) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) ClientErrorException(org.eclipse.hono.client.ClientErrorException) CommandResponse(org.eclipse.hono.client.command.CommandResponse) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) Test(org.junit.jupiter.api.Test)

Example 5 with CommandResponse

use of org.eclipse.hono.client.command.CommandResponse in project hono by eclipse.

the class ProtonBasedCommandResponseSender method sendCommandResponse.

@Override
public Future<Void> sendCommandResponse(final TenantObject tenant, final RegistrationAssertion device, final CommandResponse response, final SpanContext context) {
    Objects.requireNonNull(tenant);
    Objects.requireNonNull(device);
    Objects.requireNonNull(response);
    final var sender = createSender(response.getTenantId(), response.getReplyToId());
    return sender.recover(thr -> Future.failedFuture(StatusCodeMapper.toServerError(thr))).compose(s -> {
        final Message msg = createDownstreamMessage(response, tenant, device, response.getAdditionalProperties());
        final Span span = newChildSpan(context, "forward Command response");
        if (response.getMessagingType() != getMessagingType()) {
            span.log(String.format("using messaging type %s instead of type %s used for the original command", getMessagingType(), response.getMessagingType()));
        }
        return s.sendAndWaitForOutcome(msg, span);
    }).onSuccess(delivery -> sender.result().close()).mapEmpty();
}
Also used : AddressHelper(org.eclipse.hono.util.AddressHelper) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) ProtonHelper(io.vertx.proton.ProtonHelper) MessageHelper(org.eclipse.hono.util.MessageHelper) CommandResponseSender(org.eclipse.hono.client.command.CommandResponseSender) Future(io.vertx.core.Future) CommandResponse(org.eclipse.hono.client.command.CommandResponse) TenantObject(org.eclipse.hono.util.TenantObject) SpanContext(io.opentracing.SpanContext) Objects(java.util.Objects) AbstractServiceClient(org.eclipse.hono.client.amqp.AbstractServiceClient) DownstreamAmqpMessageFactory(org.eclipse.hono.client.amqp.DownstreamAmqpMessageFactory) DownstreamMessageProperties(org.eclipse.hono.client.util.DownstreamMessageProperties) StatusCodeMapper(org.eclipse.hono.client.StatusCodeMapper) Map(java.util.Map) Span(io.opentracing.Span) Message(org.apache.qpid.proton.message.Message) SendMessageSampler(org.eclipse.hono.client.SendMessageSampler) HonoConnection(org.eclipse.hono.client.HonoConnection) GenericSenderLink(org.eclipse.hono.client.amqp.GenericSenderLink) CommandConstants(org.eclipse.hono.util.CommandConstants) Message(org.apache.qpid.proton.message.Message) Span(io.opentracing.Span)

Aggregations

CommandResponse (org.eclipse.hono.client.command.CommandResponse)11 RegistrationAssertion (org.eclipse.hono.util.RegistrationAssertion)11 TenantObject (org.eclipse.hono.util.TenantObject)11 Span (io.opentracing.Span)8 Future (io.vertx.core.Future)7 HttpURLConnection (java.net.HttpURLConnection)6 Objects (java.util.Objects)6 ClientErrorException (org.eclipse.hono.client.ClientErrorException)6 CommandConstants (org.eclipse.hono.util.CommandConstants)6 Tags (io.opentracing.tag.Tags)5 Optional (java.util.Optional)5 TracingHelper (org.eclipse.hono.tracing.TracingHelper)5 MessageHelper (org.eclipse.hono.util.MessageHelper)5 SpanContext (io.opentracing.SpanContext)4 CompositeFuture (io.vertx.core.CompositeFuture)4 Buffer (io.vertx.core.buffer.Buffer)4 Map (java.util.Map)4 Message (org.apache.qpid.proton.message.Message)4 Test (org.junit.jupiter.api.Test)4 Sample (io.micrometer.core.instrument.Timer.Sample)3