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;
});
}
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());
}
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());
}
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();
}));
}
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();
}
Aggregations