diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index 7d2198f45a..e9674d767e 100644 --- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -16,12 +16,11 @@ package org.thingsboard.server.transport.http; import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; import com.google.gson.JsonParser; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -31,6 +30,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -56,6 +56,7 @@ import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; +import org.thingsboard.server.gen.transport.TransportProtos.GetOtaPackageResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; @@ -429,22 +430,17 @@ public class DeviceApiController implements TbTransportService { .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) .setType(firmwareType.name()).build(); - transportContext.getTransportService().process(sessionInfo, requestMsg, new GetOtaPackageCallback(responseWriter, title, version, size, chunk)); + transportContext.getTransportService().process(sessionInfo, requestMsg, new GetOtaPackageCallback(transportContext, responseWriter, title, version, size, chunk)); })); return responseWriter; } - private static class DeviceAuthCallback implements TransportServiceCallback { + @RequiredArgsConstructor + static class DeviceAuthCallback implements TransportServiceCallback { private final TransportContext transportContext; private final DeferredResult responseWriter; private final Consumer onSuccess; - DeviceAuthCallback(TransportContext transportContext, DeferredResult responseWriter, Consumer onSuccess) { - this.transportContext = transportContext; - this.responseWriter = responseWriter; - this.onSuccess = onSuccess; - } - @Override public void onSuccess(ValidateDeviceCredentialsResponse msg) { if (msg.hasDeviceInfo()) { @@ -456,18 +452,21 @@ public class DeviceApiController implements TbTransportService { @Override public void onError(Throwable e) { - log.warn("Failed to process request", e); - responseWriter.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + String body = null; + if (e instanceof HttpMessageNotReadableException || e instanceof JsonParseException) { + body = e.getMessage(); + log.debug("Failed to process request in DeviceAuthCallback: {}", body); + } else { + log.warn("Failed to process request in DeviceAuthCallback", e); + } + responseWriter.setResult(new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR)); } } - private static class DeviceProvisionCallback implements TransportServiceCallback { + @RequiredArgsConstructor + static class DeviceProvisionCallback implements TransportServiceCallback { private final DeferredResult responseWriter; - DeviceProvisionCallback(DeferredResult responseWriter) { - this.responseWriter = responseWriter; - } - @Override public void onSuccess(ProvisionDeviceResponseMsg msg) { responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK)); @@ -475,25 +474,25 @@ public class DeviceApiController implements TbTransportService { @Override public void onError(Throwable e) { - log.warn("Failed to process request", e); - responseWriter.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + String body = null; + if (e instanceof HttpMessageNotReadableException || e instanceof JsonParseException) { + body = e.getMessage(); + log.debug("Failed to process request in DeviceProvisionCallback: {}", body); + } else { + log.warn("Failed to process request in DeviceProvisionCallback", e); + } + responseWriter.setResult(new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR)); } } - private class GetOtaPackageCallback implements TransportServiceCallback { + @RequiredArgsConstructor + static class GetOtaPackageCallback implements TransportServiceCallback { + private final TransportContext transportContext; private final DeferredResult responseWriter; private final String title; private final String version; - private final int chuckSize; - private final int chuck; - - GetOtaPackageCallback(DeferredResult responseWriter, String title, String version, int chuckSize, int chuck) { - this.responseWriter = responseWriter; - this.title = title; - this.version = version; - this.chuckSize = chuckSize; - this.chuck = chuck; - } + private final int chunkSize; + private final int chunk; @Override public void onSuccess(TransportProtos.GetOtaPackageResponseMsg otaPackageResponseMsg) { @@ -501,7 +500,7 @@ public class DeviceApiController implements TbTransportService { responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND)); } else if (title.equals(otaPackageResponseMsg.getTitle()) && version.equals(otaPackageResponseMsg.getVersion())) { String otaPackageId = new UUID(otaPackageResponseMsg.getOtaPackageIdMSB(), otaPackageResponseMsg.getOtaPackageIdLSB()).toString(); - ByteArrayResource resource = new ByteArrayResource(transportContext.getOtaPackageDataCache().get(otaPackageId, chuckSize, chuck)); + ByteArrayResource resource = new ByteArrayResource(transportContext.getOtaPackageDataCache().get(otaPackageId, chunkSize, chunk)); ResponseEntity response = ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackageResponseMsg.getFileName()) .header("x-filename", otaPackageResponseMsg.getFileName()) @@ -516,8 +515,14 @@ public class DeviceApiController implements TbTransportService { @Override public void onError(Throwable e) { - log.warn("Failed to process request", e); - responseWriter.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + String body = null; + if (e instanceof HttpMessageNotReadableException || e instanceof JsonParseException) { + body = e.getMessage(); + log.debug("Failed to process request in GetOtaPackageCallback: {}", body); + } else { + log.warn("Failed to process request in GetOtaPackageCallback", e); + } + responseWriter.setResult(new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR)); } } diff --git a/common/transport/http/src/test/java/org/thingsboard/server/transport/http/DeviceApiControllerTest.java b/common/transport/http/src/test/java/org/thingsboard/server/transport/http/DeviceApiControllerTest.java new file mode 100644 index 0000000000..ce1e2b2fde --- /dev/null +++ b/common/transport/http/src/test/java/org/thingsboard/server/transport/http/DeviceApiControllerTest.java @@ -0,0 +1,82 @@ +/** + * Copyright © 2016-2024 The Thingsboard 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 org.thingsboard.server.transport.http; + +import com.google.gson.JsonParseException; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.web.context.request.async.DeferredResult; +import org.thingsboard.server.common.transport.TransportContext; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.io.IOException; +import java.util.function.Consumer; + +class DeviceApiControllerTest { + + @Test + void deviceAuthCallbackTest() { + TransportContext transportContext = Mockito.mock(TransportContext.class); + DeferredResult responseWriter = Mockito.mock(DeferredResult.class); + Consumer onSuccess = x -> { + }; + var callback = new DeviceApiController.DeviceAuthCallback(transportContext, responseWriter, onSuccess); + + callback.onError(new HttpMessageNotReadableException("JSON incorrect syntax")); + + callback.onError(new JsonParseException("Json ; expected")); + + callback.onError(new IOException("not found")); + + callback.onError(new RuntimeException("oops it is run time error")); + } + + @Test + void deviceProvisionCallbackTest() { + DeferredResult responseWriter = Mockito.mock(DeferredResult.class); + var callback = new DeviceApiController.DeviceProvisionCallback(responseWriter); + + callback.onError(new HttpMessageNotReadableException("JSON incorrect syntax")); + + callback.onError(new JsonParseException("Json ; expected")); + + callback.onError(new IOException("not found")); + + callback.onError(new RuntimeException("oops it is run time error")); + } + + @Test + void getOtaPackageCallback() { + TransportContext transportContext = Mockito.mock(TransportContext.class); + DeferredResult responseWriter = Mockito.mock(DeferredResult.class); + String title = "Title"; + String version = "version"; + int chunkSize = 11; + int chunk = 3; + + var callback = new DeviceApiController.GetOtaPackageCallback(transportContext, responseWriter, title, version, chunkSize, chunk); + + callback.onError(new HttpMessageNotReadableException("JSON incorrect syntax")); + + callback.onError(new JsonParseException("Json ; expected")); + + callback.onError(new IOException("not found")); + + callback.onError(new RuntimeException("oops it is run time error")); + } +} diff --git a/common/transport/http/src/test/resources/logback-test.xml b/common/transport/http/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..359007da5a --- /dev/null +++ b/common/transport/http/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + logger name="org.thingsboard.server.transport.http.DeviceApiController" level="DEBUG" /> + + + + + +