LoraProtocolAdapter extends AbstractVertxBasedHttpProtocolAdapter<LoraProtocolAdapterProperties> { void handleProviderRoute(final HttpContext ctx, final LoraProvider provider) { LOG.debug("processing request from provider [name: {}, URI: {}", provider.getProviderName(), ctx.getRoutingContext().normalisedPath()); final Span currentSpan = TracingHelper.buildServerChildSpan( tracer, TracingHandler.serverSpanContext(ctx.getRoutingContext()), "process message", getClass().getSimpleName()) .start(); TAG_LORA_PROVIDER.set(currentSpan, provider.getProviderName()); ctx.put(LoraConstants.APP_PROPERTY_ORIG_LORA_PROVIDER, provider.getProviderName()); if (ctx.isDeviceAuthenticated()) { final Device gatewayDevice = ctx.getAuthenticatedDevice(); TracingHelper.setDeviceTags(currentSpan, gatewayDevice.getTenantId(), gatewayDevice.getDeviceId()); try { final LoraMessage loraMessage = provider.getMessage(ctx.getRoutingContext()); final LoraMessageType type = loraMessage.getType(); currentSpan.log(Map.of("message type", type)); final String deviceId = loraMessage.getDevEUIAsString(); currentSpan.setTag(TAG_LORA_DEVICE_ID, deviceId); switch (type) { case UPLINK: final UplinkLoraMessage uplinkMessage = (UplinkLoraMessage) loraMessage; final Buffer payload = uplinkMessage.getPayload(); Optional.ofNullable(uplinkMessage.getMetaData()) .ifPresent(metaData -> ctx.put(LoraConstants.APP_PROPERTY_META_DATA, metaData)); Optional.ofNullable(uplinkMessage.getAdditionalData()) .ifPresent(additionalData -> ctx.put(LoraConstants.APP_PROPERTY_ADDITIONAL_DATA, additionalData)); final String contentType; if (payload.length() > 0) { contentType = String.format( "%s%s", LoraConstants.CONTENT_TYPE_LORA_BASE, provider.getProviderName()); } else { contentType = EventConstants.CONTENT_TYPE_EMPTY_NOTIFICATION; } uploadTelemetryMessage(ctx, gatewayDevice.getTenantId(), deviceId, payload, contentType); break; default: LOG.debug("discarding message of unsupported type [tenant: {}, device-id: {}, type: {}]", gatewayDevice.getTenantId(), deviceId, type); currentSpan.log("discarding message of unsupported type"); handle202(ctx.getRoutingContext()); } } catch (final LoraProviderMalformedPayloadException e) { LOG.debug("error processing request from provider [name: {}]", provider.getProviderName(), e); TracingHelper.logError(currentSpan, "error processing request", e); handle400(ctx.getRoutingContext(), ERROR_MSG_INVALID_PAYLOAD); } } else { handleUnsupportedUserType(ctx.getRoutingContext(), currentSpan); } currentSpan.finish(); }  @Autowired(required = false) void setLoraProviders(final List<LoraProvider> providers); void setUsernamePasswordAuthProvider(final DeviceCredentialsAuthProvider<UsernamePasswordCredentials> provider); void setClientCertAuthProvider(final DeviceCredentialsAuthProvider<SubjectDnCredentials> provider);  }
@Test public void handleProviderRouteSuccessfullyForUplinkMessage() { final LoraProvider providerMock = getLoraProviderMock(); final HttpContext httpContext = newHttpContext(); final HttpServerRequest request = mock(HttpServerRequest.class); when(request.getHeader(eq(Constants.HEADER_QOS_LEVEL))).thenReturn(null); when(httpContext.request()).thenReturn(request); adapter.handleProviderRoute(httpContext, providerMock); verify(httpContext.getRoutingContext()).put(LoraConstants.APP_PROPERTY_ORIG_LORA_PROVIDER, TEST_PROVIDER); final ArgumentCaptor<Message> message = ArgumentCaptor.forClass(Message.class); verify(telemetrySender).send(message.capture(), any(SpanContext.class)); assertThat(MessageHelper.getPayload(message.getValue())).isEqualTo(Buffer.buffer(TEST_PAYLOAD)); assertThat(message.getValue().getContentType()).isEqualTo(LoraConstants.CONTENT_TYPE_LORA_BASE + providerMock.getProviderName()); final ApplicationProperties props = message.getValue().getApplicationProperties(); assertThat(MessageHelper.getApplicationProperty(props, LoraConstants.APP_PROPERTY_FUNCTION_PORT, Integer.class)) .isEqualTo(TEST_FUNCTION_PORT); final String metaData = MessageHelper.getApplicationProperty(props, LoraConstants.APP_PROPERTY_META_DATA, String.class); assertThat(metaData).isNotNull(); final JsonObject metaDataJson = new JsonObject(metaData); assertThat(metaDataJson.getInteger(LoraConstants.APP_PROPERTY_FUNCTION_PORT)).isEqualTo(TEST_FUNCTION_PORT); verify(httpContext.getRoutingContext().response()).setStatusCode(HttpResponseStatus.ACCEPTED.code()); verify(currentSpan).finish(); }
@Test public void handleProviderRouteDiscardsJoinMessages() { final LoraMessage message = mock(LoraMessage.class); when(message.getType()).thenReturn(LoraMessageType.JOIN); final LoraProvider providerMock = getLoraProviderMock(message); final HttpContext httpContext = newHttpContext(); adapter.handleProviderRoute(httpContext, providerMock); verify(httpContext.getRoutingContext()).put(LoraConstants.APP_PROPERTY_ORIG_LORA_PROVIDER, TEST_PROVIDER); verify(telemetrySender, never()).send(any(Message.class), any(SpanContext.class)); verify(telemetrySender, never()).sendAndWaitForOutcome(any(Message.class), any(SpanContext.class)); verify(httpContext.getRoutingContext().response()).setStatusCode(HttpResponseStatus.ACCEPTED.code()); verify(currentSpan).finish(); }
@Test public void handleProviderRouteDiscardsDownlinkMessages() { final LoraMessage message = mock(LoraMessage.class); when(message.getType()).thenReturn(LoraMessageType.DOWNLINK); final LoraProvider providerMock = getLoraProviderMock(message); final HttpContext httpContext = newHttpContext(); adapter.handleProviderRoute(httpContext, providerMock); verify(httpContext.getRoutingContext()).put(LoraConstants.APP_PROPERTY_ORIG_LORA_PROVIDER, TEST_PROVIDER); verify(telemetrySender, never()).send(any(Message.class), any(SpanContext.class)); verify(telemetrySender, never()).sendAndWaitForOutcome(any(Message.class), any(SpanContext.class)); verify(httpContext.response()).setStatusCode(HttpResponseStatus.ACCEPTED.code()); verify(currentSpan).finish(); }
@Test public void handleProviderRouteDiscardsOtherMessages() { final LoraMessage message = mock(LoraMessage.class); when(message.getType()).thenReturn(LoraMessageType.UNKNOWN); final LoraProvider providerMock = getLoraProviderMock(message); final HttpContext httpContext = newHttpContext(); adapter.handleProviderRoute(httpContext, providerMock); verify(httpContext.getRoutingContext()).put(LoraConstants.APP_PROPERTY_ORIG_LORA_PROVIDER, TEST_PROVIDER); verify(telemetrySender, never()).send(any(Message.class), any(SpanContext.class)); verify(telemetrySender, never()).sendAndWaitForOutcome(any(Message.class), any(SpanContext.class)); verify(httpContext.response()).setStatusCode(HttpResponseStatus.ACCEPTED.code()); verify(currentSpan).finish(); }
@Test public void handleProviderRouteCausesUnauthorizedForInvalidGatewayCredentials() { final LoraProvider providerMock = getLoraProviderMock(); final HttpContext httpContext = newHttpContext(); when(httpContext.getRoutingContext().user()).thenReturn(null); adapter.handleProviderRoute(httpContext, providerMock); verify(httpContext.getRoutingContext()).put(LoraConstants.APP_PROPERTY_ORIG_LORA_PROVIDER, TEST_PROVIDER); verify(telemetrySender, never()).send(any(Message.class), any(SpanContext.class)); verify(telemetrySender, never()).sendAndWaitForOutcome(any(Message.class), any(SpanContext.class)); verifyUnauthorized(httpContext.getRoutingContext()); verify(currentSpan).finish(); }
@Test public void handleProviderRouteCausesBadRequestForFailureToParseBody() { final LoraProvider providerMock = getLoraProviderMock(); when(providerMock.getMessage(any(RoutingContext.class))).thenThrow(new LoraProviderMalformedPayloadException("no device ID")); final HttpContext httpContext = newHttpContext(); adapter.handleProviderRoute(httpContext, providerMock); verify(httpContext.getRoutingContext()).put(LoraConstants.APP_PROPERTY_ORIG_LORA_PROVIDER, TEST_PROVIDER); verify(telemetrySender, never()).send(any(Message.class), any(SpanContext.class)); verify(telemetrySender, never()).sendAndWaitForOutcome(any(Message.class), any(SpanContext.class)); verifyBadRequest(httpContext.getRoutingContext()); verify(currentSpan).finish(); }