use of org.eclipse.hono.util.QoS in project hono by eclipse.
the class VertxBasedHttpProtocolAdapterTest method testPostTelemetrySucceedsForQoS1.
/**
* Verifies that a request (with valid credentials) to upload telemetry data with
* 'QoS-Level: 1' using POST succeeds with a 202.
*
* @param ctx The vert.x test context.
*/
@Test
public void testPostTelemetrySucceedsForQoS1(final VertxTestContext ctx) {
givenATelemetrySenderForAnyTenant();
mockSuccessfulAuthentication("DEFAULT_TENANT", "device_1");
httpClient.post("/telemetry").putHeader(HttpHeaders.CONTENT_TYPE.toString(), HttpUtils.CONTENT_TYPE_JSON).basicAuthentication("testuser@DEFAULT_TENANT", "password123").putHeader(HttpHeaders.ORIGIN.toString(), ORIGIN_HEADER_VALUE).putHeader(Constants.HEADER_QOS_LEVEL, String.valueOf(1)).expect(ResponsePredicate.status(HttpURLConnection.HTTP_ACCEPTED)).expect(this::assertCorsHeaders).sendJsonObject(new JsonObject(), ctx.succeeding(r -> {
ctx.verify(() -> assertTelemetryMessageHasBeenSentDownstream(QoS.AT_LEAST_ONCE, "DEFAULT_TENANT", "device_1", "application/json"));
ctx.completeNow();
}));
}
use of org.eclipse.hono.util.QoS in project hono by eclipse.
the class DownstreamMessageProperties method applyTelemetryLimits.
private void applyTelemetryLimits() {
final QoS qos = Optional.ofNullable(props.get(MessageHelper.APP_PROPERTY_QOS)).filter(Integer.class::isInstance).map(Integer.class::cast).map(QoS::from).orElse(QoS.AT_MOST_ONCE);
// make sure that we always remove the default properties from the map so that
// they are not being added to the downstream message later on
final long configuredDefaultTtlQoS0 = getNormalizedTtlPropertyValue(TenantConstants.FIELD_TTL_TELEMETRY_QOS0);
final long configuredDefaultTtlQoS1 = getNormalizedTtlPropertyValue(TenantConstants.FIELD_TTL_TELEMETRY_QOS1);
final long configuredMaxTtl;
final long configuredDefaultTtl;
switch(qos) {
case AT_MOST_ONCE:
configuredDefaultTtl = configuredDefaultTtlQoS0;
configuredMaxTtl = Optional.ofNullable(resourceLimits).map(ResourceLimits::getMaxTtlTelemetryQoS0).filter(v -> v > TenantConstants.UNLIMITED_TTL).orElse(TenantConstants.UNLIMITED_TTL);
break;
case AT_LEAST_ONCE:
default:
configuredDefaultTtl = configuredDefaultTtlQoS1;
configuredMaxTtl = Optional.ofNullable(resourceLimits).map(ResourceLimits::getMaxTtlTelemetryQoS1).filter(v -> v > TenantConstants.UNLIMITED_TTL).orElse(TenantConstants.UNLIMITED_TTL);
}
final long deviceProvidedTtl = getNormalizedTtlPropertyValue(MessageHelper.SYS_HEADER_PROPERTY_TTL);
if (deviceProvidedTtl > TenantConstants.UNLIMITED_TTL) {
setTtl(deviceProvidedTtl, configuredMaxTtl);
} else {
setTtl(configuredDefaultTtl, configuredMaxTtl);
}
}
use of org.eclipse.hono.util.QoS in project hono by eclipse.
the class ProtonBasedDownstreamSenderTest method testDownstreamTelemetryMessageHasCreationTimeAndTtl.
/**
* Verifies that a downstream telemetry message always contains a creation-time
* and a time-to-live as defined at the tenant level.
*
* @param qosLevel The quality of service used for sending the message.
* @param expectedTtl The time to live (in millis) expected to be set on the message.
*/
@ParameterizedTest
@CsvSource(value = { "AT_MOST_ONCE,10000", "AT_LEAST_ONCE,20000" })
public void testDownstreamTelemetryMessageHasCreationTimeAndTtl(final QoS qosLevel, final long expectedTtl) {
final TenantObject tenant = TenantObject.from(Constants.DEFAULT_TENANT, true);
tenant.setResourceLimits(new ResourceLimits().setMaxTtlTelemetryQoS0(10L).setMaxTtlTelemetryQoS1(20L));
final RegistrationAssertion device = new RegistrationAssertion("4711");
// WHEN sending a message without specifying any properties
sender.sendTelemetry(tenant, device, qosLevel, "text/plain", Buffer.buffer("hello"), null, span.context());
// THEN the message contains a creation-time
verify(protonSender).send(argThat(message -> message.getCreationTime() > 0 && message.getTtl() == expectedTtl), VertxMockSupport.anyHandler());
}
use of org.eclipse.hono.util.QoS in project hono by eclipse.
the class KafkaBasedTelemetrySenderTest method testSendTelemetryCreatesCorrectRecord.
/**
* Verifies that the Kafka record is created as expected when sending telemetry data.
*
* @param qos The quality of service used for sending the message.
* @param expectedTtl The ttl expected in the message.
* @param ctx The vert.x test context.
*/
@ParameterizedTest
@CsvSource(value = { "AT_MOST_ONCE,10000", "AT_LEAST_ONCE,60000" })
public void testSendTelemetryCreatesCorrectRecord(final QoS qos, final long expectedTtl, final VertxTestContext ctx) {
// GIVEN a telemetry sender
final String payload = "the-payload";
final String contentType = "text/plain";
final Map<String, Object> properties = Map.of("foo", "bar");
final var spanFinished = ctx.checkpoint();
final var messageHasHeaders = ctx.checkpoint();
final var span = TracingMockSupport.mockSpan();
doAnswer(invocation -> {
spanFinished.flag();
return null;
}).when(span).finish();
final var tracer = TracingMockSupport.mockTracer(span);
final var mockProducer = KafkaClientUnitTestHelper.newMockProducer(true);
final var factory = CachingKafkaProducerFactory.testFactory(vertxMock, (n, c) -> KafkaClientUnitTestHelper.newKafkaProducer(mockProducer));
final var sender = new KafkaBasedTelemetrySender(vertxMock, factory, kafkaProducerConfig, true, tracer);
tenant.setResourceLimits(new ResourceLimits().setMaxTtlTelemetryQoS0(10L).setMaxTtlTelemetryQoS1(60L));
// WHEN sending telemetry data
sender.sendTelemetry(tenant, device, qos, contentType, Buffer.buffer(payload), properties, null).onComplete(ctx.succeeding(t -> {
ctx.verify(() -> {
// THEN the producer record is created from the given values...
final var producerRecord = mockProducer.history().get(0);
assertThat(producerRecord.key()).isEqualTo(device.getDeviceId());
assertThat(producerRecord.topic()).isEqualTo(new HonoTopic(HonoTopic.Type.TELEMETRY, tenant.getTenantId()).toString());
assertThat(producerRecord.value().toString()).isEqualTo(payload);
KafkaClientUnitTestHelper.assertUniqueHeaderWithExpectedValue(producerRecord.headers(), "foo", "bar");
KafkaClientUnitTestHelper.assertUniqueHeaderWithExpectedValue(producerRecord.headers(), MessageHelper.SYS_HEADER_PROPERTY_TTL, expectedTtl);
// ...AND contains the standard headers
KafkaClientUnitTestHelper.assertStandardHeaders(producerRecord, device.getDeviceId(), contentType, qos.ordinal());
});
messageHasHeaders.flag();
}));
}
use of org.eclipse.hono.util.QoS in project hono by eclipse.
the class TelemetryHttpIT method testUploadQos1MessageFailsIfDeliveryStateNotUpdated.
/**
* Verifies that the upload of a QoS 1 telemetry message fails with a 503 status code
* when the consumer doesn't update the message delivery state and the
* <em>sendMessageTimeout</em> has elapsed.
*
* @param vertx The vert.x instance.
* @param ctx The test context
* @throws InterruptedException if test is interrupted while running.
*/
@Test
@AssumeMessagingSystem(type = MessagingType.amqp)
public void testUploadQos1MessageFailsIfDeliveryStateNotUpdated(final Vertx vertx, final VertxTestContext ctx) throws InterruptedException {
final AmqpApplicationClient amqpApplicationClient = (AmqpApplicationClient) helper.applicationClient;
// GIVEN a device and a north bound message consumer that doesn't update the message delivery state
final Tenant tenant = new Tenant();
final Checkpoint messageReceived = ctx.checkpoint();
final Checkpoint deliveryStateCheckDone = ctx.checkpoint();
final Checkpoint httpResponseReceived = ctx.checkpoint();
final VertxTestContext setup = new VertxTestContext();
final AtomicReference<ProtonDelivery> deliveryRef = new AtomicReference<>();
helper.registry.addDeviceForTenant(tenantId, tenant, deviceId, PWD).compose(ok -> amqpApplicationClient.createTelemetryConsumer(tenantId, msg -> {
final Promise<Void> result = Promise.promise();
final var delivery = msg.getMessageContext().getDelivery();
deliveryRef.set(delivery);
logger.debug("received message: {}", msg.getMessageContext().getRawMessage());
ctx.verify(() -> {
assertThat(delivery.remotelySettled()).isFalse();
assertThat(delivery.getRemoteState()).isNull();
});
messageReceived.flag();
// don't update the delivery state here
return result.future();
}, remoteClose -> {
})).onComplete(setup.succeedingThenComplete());
assertThat(setup.awaitCompletion(IntegrationTestSupport.getTestSetupTimeout(), TimeUnit.SECONDS)).isTrue();
if (setup.failed()) {
ctx.failNow(setup.causeOfFailure());
return;
}
// WHEN the device tries to upload a telemetry message
final MultiMap requestHeaders = MultiMap.caseInsensitiveMultiMap().add(HttpHeaders.CONTENT_TYPE, "binary/octet-stream").add(HttpHeaders.AUTHORIZATION, authorization).add(HttpHeaders.ORIGIN, ORIGIN_URI).add(Constants.HEADER_QOS_LEVEL, "1");
final Future<HttpResponse<Buffer>> httpResponseFuture = httpClient.create(getEndpointUri(), Buffer.buffer("hello"), requestHeaders, // THEN the message gets rejected by the HTTP adapter with a 503
ResponsePredicate.status(HttpURLConnection.HTTP_UNAVAILABLE));
httpResponseFuture.onComplete(ctx.succeeding(response -> {
ctx.verify(() -> {
final var body = response.bodyAsJsonObject();
assertThat(body.getString(RequestResponseApiConstants.FIELD_ERROR)).isEqualTo(ServiceInvocationException.getLocalizedMessage(SendMessageTimeoutException.CLIENT_FACING_MESSAGE_KEY));
});
httpResponseReceived.flag();
// verify that the telemetry message delivery is remotely settled via the timeout handling in the adapter
vertx.setTimer(50, tid -> {
ctx.verify(() -> {
final ProtonDelivery delivery = deliveryRef.get();
assertThat(delivery).isNotNull();
assertThat(delivery.remotelySettled()).isTrue();
assertThat(delivery.getRemoteState()).isNotNull();
assertThat(delivery.getRemoteState().getType()).isEqualTo(DeliveryState.DeliveryStateType.Released);
});
deliveryStateCheckDone.flag();
});
}));
}
Aggregations