From 7c2b3a9fbf84cc58aa7546029c5c7c10a662c55a Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 4 Nov 2021 15:26:36 +0200 Subject: [PATCH] Improve REST API error response handling. Update swagger api. --- application/pom.xml | 11 +++ .../server/config/SwaggerConfiguration.java | 11 ++- .../server/controller/AdminController.java | 2 +- .../server/controller/BaseController.java | 70 ++++++++------- .../server/controller/CustomerController.java | 2 +- .../controller/DashboardController.java | 5 ++ .../ThingsboardErrorResponseHandler.java | 84 ++++++++++++------ .../src/main/resources/thingsboard.yml | 7 +- .../server/common/data/AdminSettings.java | 2 +- pom.xml | 2 +- transport/lwm2m/src/main/data/lwm2mserver.jks | Bin 3953 -> 3849 bytes 11 files changed, 133 insertions(+), 63 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index fd90d00d34..cc3bcef183 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -339,6 +339,17 @@ ${project.basedir}/src/main/resources + true + + thingsboard.yml + + + + ${project.basedir}/src/main/resources + false + + thingsboard.yml + diff --git a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java index 25cc862924..2927aff3f0 100644 --- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java @@ -26,10 +26,12 @@ import org.springframework.core.annotation.Order; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.exception.ThingsboardCredentialsExpiredResponse; import org.thingsboard.server.exception.ThingsboardErrorResponse; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.rest.LoginRequest; import org.thingsboard.server.service.security.auth.rest.LoginResponse; import springfox.documentation.builders.ApiInfoBuilder; @@ -81,6 +83,7 @@ import static springfox.documentation.builders.PathSelectors.regex; @Slf4j @Configuration +@TbCoreComponent public class SwaggerConfiguration { @Value("${swagger.api_path_regex}") @@ -105,6 +108,8 @@ public class SwaggerConfiguration { private String licenseUrl; @Value("${swagger.version}") private String version; + @Value("${app.version:unknown}") + private String appVersion; @Bean public Docket thingsboardApi() { @@ -231,13 +236,17 @@ public class SwaggerConfiguration { } private ApiInfo apiInfo() { + String apiVersion = version; + if (StringUtils.isEmpty(apiVersion)) { + apiVersion = appVersion; + } return new ApiInfoBuilder() .title(title) .description(description) .contact(new Contact(contactName, contactUrl, contactEmail)) .license(licenseTitle) .licenseUrl(licenseUrl) - .version(version) + .version(apiVersion) .build(); } diff --git a/application/src/main/java/org/thingsboard/server/controller/AdminController.java b/application/src/main/java/org/thingsboard/server/controller/AdminController.java index d9a8f436b9..c339d8d49e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -73,7 +73,7 @@ public class AdminController extends BaseController { @PathVariable("key") String key) throws ThingsboardException { try { accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); - AdminSettings adminSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key)); + AdminSettings adminSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key), "No Administration settings found for key: " + key); if (adminSettings.getKey().equals("mail")) { ((ObjectNode) adminSettings.getJsonValue()).remove("password"); } diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 6a5c7ae539..a6ebaa4c99 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -319,17 +319,25 @@ public abstract class BaseController { } T checkNotNull(T reference) throws ThingsboardException { + return checkNotNull(reference, "Requested item wasn't found!"); + } + + T checkNotNull(T reference, String notFoundMessage) throws ThingsboardException { if (reference == null) { - throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND); + throw new ThingsboardException(notFoundMessage, ThingsboardErrorCode.ITEM_NOT_FOUND); } return reference; } T checkNotNull(Optional reference) throws ThingsboardException { + return checkNotNull(reference, "Requested item wasn't found!"); + } + + T checkNotNull(Optional reference, String notFoundMessage) throws ThingsboardException { if (reference.isPresent()) { return reference.get(); } else { - throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND); + throw new ThingsboardException(notFoundMessage, ThingsboardErrorCode.ITEM_NOT_FOUND); } } @@ -389,7 +397,7 @@ public abstract class BaseController { try { validateId(tenantId, INCORRECT_TENANT_ID + tenantId); Tenant tenant = tenantService.findTenantById(tenantId); - checkNotNull(tenant); + checkNotNull(tenant, "Tenant with id [" + tenantId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.TENANT, operation, tenantId, tenant); return tenant; } catch (Exception e) { @@ -401,7 +409,7 @@ public abstract class BaseController { try { validateId(tenantId, INCORRECT_TENANT_ID + tenantId); TenantInfo tenant = tenantService.findTenantInfoById(tenantId); - checkNotNull(tenant); + checkNotNull(tenant, "Tenant with id [" + tenantId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.TENANT, operation, tenantId, tenant); return tenant; } catch (Exception e) { @@ -413,7 +421,7 @@ public abstract class BaseController { try { validateId(tenantProfileId, "Incorrect tenantProfileId " + tenantProfileId); TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(getTenantId(), tenantProfileId); - checkNotNull(tenantProfile); + checkNotNull(tenantProfile, "Tenant profile with id [" + tenantProfileId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.TENANT_PROFILE, operation); return tenantProfile; } catch (Exception e) { @@ -429,7 +437,7 @@ public abstract class BaseController { try { validateId(customerId, "Incorrect customerId " + customerId); Customer customer = customerService.findCustomerById(getTenantId(), customerId); - checkNotNull(customer); + checkNotNull(customer, "Customer with id [" + customerId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.CUSTOMER, operation, customerId, customer); return customer; } catch (Exception e) { @@ -441,7 +449,7 @@ public abstract class BaseController { try { validateId(userId, "Incorrect userId " + userId); User user = userService.findUserById(getCurrentUser().getTenantId(), userId); - checkNotNull(user); + checkNotNull(user, "User with id [" + userId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.USER, operation, userId, user); return user; } catch (Exception e) { @@ -460,7 +468,9 @@ public abstract class BaseController { protected void checkEntityId(EntityId entityId, Operation operation) throws ThingsboardException { try { - checkNotNull(entityId); + if (entityId == null) { + throw new ThingsboardException("Parameter entityId can't be empty!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } validateId(entityId.getId(), "Incorrect entityId " + entityId); switch (entityId.getEntityType()) { case ALARM: @@ -526,7 +536,7 @@ public abstract class BaseController { try { validateId(deviceId, "Incorrect deviceId " + deviceId); Device device = deviceService.findDeviceById(getCurrentUser().getTenantId(), deviceId); - checkNotNull(device); + checkNotNull(device, "Device with id [" + deviceId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, operation, deviceId, device); return device; } catch (Exception e) { @@ -538,7 +548,7 @@ public abstract class BaseController { try { validateId(deviceId, "Incorrect deviceId " + deviceId); DeviceInfo device = deviceService.findDeviceInfoById(getCurrentUser().getTenantId(), deviceId); - checkNotNull(device); + checkNotNull(device, "Device with id [" + deviceId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, operation, deviceId, device); return device; } catch (Exception e) { @@ -550,7 +560,7 @@ public abstract class BaseController { try { validateId(deviceProfileId, "Incorrect deviceProfileId " + deviceProfileId); DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(getCurrentUser().getTenantId(), deviceProfileId); - checkNotNull(deviceProfile); + checkNotNull(deviceProfile, "Device profile with id [" + deviceProfileId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE_PROFILE, operation, deviceProfileId, deviceProfile); return deviceProfile; } catch (Exception e) { @@ -562,7 +572,7 @@ public abstract class BaseController { try { validateId(entityViewId, "Incorrect entityViewId " + entityViewId); EntityView entityView = entityViewService.findEntityViewById(getCurrentUser().getTenantId(), entityViewId); - checkNotNull(entityView); + checkNotNull(entityView, "Entity view with id [" + entityViewId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, operation, entityViewId, entityView); return entityView; } catch (Exception e) { @@ -574,7 +584,7 @@ public abstract class BaseController { try { validateId(entityViewId, "Incorrect entityViewId " + entityViewId); EntityViewInfo entityView = entityViewService.findEntityViewInfoById(getCurrentUser().getTenantId(), entityViewId); - checkNotNull(entityView); + checkNotNull(entityView, "Entity view with id [" + entityViewId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, operation, entityViewId, entityView); return entityView; } catch (Exception e) { @@ -586,7 +596,7 @@ public abstract class BaseController { try { validateId(assetId, "Incorrect assetId " + assetId); Asset asset = assetService.findAssetById(getCurrentUser().getTenantId(), assetId); - checkNotNull(asset); + checkNotNull(asset, "Asset with id [" + assetId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, operation, assetId, asset); return asset; } catch (Exception e) { @@ -598,7 +608,7 @@ public abstract class BaseController { try { validateId(assetId, "Incorrect assetId " + assetId); AssetInfo asset = assetService.findAssetInfoById(getCurrentUser().getTenantId(), assetId); - checkNotNull(asset); + checkNotNull(asset, "Asset with id [" + assetId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, operation, assetId, asset); return asset; } catch (Exception e) { @@ -610,7 +620,7 @@ public abstract class BaseController { try { validateId(alarmId, "Incorrect alarmId " + alarmId); Alarm alarm = alarmService.findAlarmByIdAsync(getCurrentUser().getTenantId(), alarmId).get(); - checkNotNull(alarm); + checkNotNull(alarm, "Alarm with id [" + alarmId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.ALARM, operation, alarmId, alarm); return alarm; } catch (Exception e) { @@ -622,7 +632,7 @@ public abstract class BaseController { try { validateId(alarmId, "Incorrect alarmId " + alarmId); AlarmInfo alarmInfo = alarmService.findAlarmInfoByIdAsync(getCurrentUser().getTenantId(), alarmId).get(); - checkNotNull(alarmInfo); + checkNotNull(alarmInfo, "Alarm with id [" + alarmId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.ALARM, operation, alarmId, alarmInfo); return alarmInfo; } catch (Exception e) { @@ -634,7 +644,7 @@ public abstract class BaseController { try { validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId); WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleById(getCurrentUser().getTenantId(), widgetsBundleId); - checkNotNull(widgetsBundle); + checkNotNull(widgetsBundle, "Widgets bundle with id [" + widgetsBundleId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.WIDGETS_BUNDLE, operation, widgetsBundleId, widgetsBundle); return widgetsBundle; } catch (Exception e) { @@ -646,7 +656,7 @@ public abstract class BaseController { try { validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId); WidgetTypeDetails widgetTypeDetails = widgetTypeService.findWidgetTypeDetailsById(getCurrentUser().getTenantId(), widgetTypeId); - checkNotNull(widgetTypeDetails); + checkNotNull(widgetTypeDetails, "Widget type with id [" + widgetTypeId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, operation, widgetTypeId, widgetTypeDetails); return widgetTypeDetails; } catch (Exception e) { @@ -658,7 +668,7 @@ public abstract class BaseController { try { validateId(dashboardId, "Incorrect dashboardId " + dashboardId); Dashboard dashboard = dashboardService.findDashboardById(getCurrentUser().getTenantId(), dashboardId); - checkNotNull(dashboard); + checkNotNull(dashboard, "Dashboard with id [" + dashboardId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.DASHBOARD, operation, dashboardId, dashboard); return dashboard; } catch (Exception e) { @@ -670,7 +680,7 @@ public abstract class BaseController { try { validateId(edgeId, "Incorrect edgeId " + edgeId); Edge edge = edgeService.findEdgeById(getTenantId(), edgeId); - checkNotNull(edge); + checkNotNull(edge, "Edge with id [" + edgeId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, edgeId, edge); return edge; } catch (Exception e) { @@ -682,7 +692,7 @@ public abstract class BaseController { try { validateId(edgeId, "Incorrect edgeId " + edgeId); EdgeInfo edge = edgeService.findEdgeInfoById(getCurrentUser().getTenantId(), edgeId); - checkNotNull(edge); + checkNotNull(edge, "Edge with id [" + edgeId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, edgeId, edge); return edge; } catch (Exception e) { @@ -694,7 +704,7 @@ public abstract class BaseController { try { validateId(dashboardId, "Incorrect dashboardId " + dashboardId); DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(getCurrentUser().getTenantId(), dashboardId); - checkNotNull(dashboardInfo); + checkNotNull(dashboardInfo, "Dashboard with id [" + dashboardId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.DASHBOARD, operation, dashboardId, dashboardInfo); return dashboardInfo; } catch (Exception e) { @@ -732,7 +742,7 @@ public abstract class BaseController { protected RuleChain checkRuleChain(RuleChainId ruleChainId, Operation operation) throws ThingsboardException { validateId(ruleChainId, "Incorrect ruleChainId " + ruleChainId); RuleChain ruleChain = ruleChainService.findRuleChainById(getCurrentUser().getTenantId(), ruleChainId); - checkNotNull(ruleChain); + checkNotNull(ruleChain, "Rule chain with id [" + ruleChainId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.RULE_CHAIN, operation, ruleChainId, ruleChain); return ruleChain; } @@ -740,7 +750,7 @@ public abstract class BaseController { protected RuleNode checkRuleNode(RuleNodeId ruleNodeId, Operation operation) throws ThingsboardException { validateId(ruleNodeId, "Incorrect ruleNodeId " + ruleNodeId); RuleNode ruleNode = ruleChainService.findRuleNodeById(getTenantId(), ruleNodeId); - checkNotNull(ruleNode); + checkNotNull(ruleNode, "Rule node with id [" + ruleNodeId + "] is not found"); checkRuleChain(ruleNode.getRuleChainId(), operation); return ruleNode; } @@ -749,7 +759,7 @@ public abstract class BaseController { try { validateId(resourceId, "Incorrect resourceId " + resourceId); TbResource resource = resourceService.findResourceById(getCurrentUser().getTenantId(), resourceId); - checkNotNull(resource); + checkNotNull(resource, "Resource with id [" + resourceId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.TB_RESOURCE, operation, resourceId, resource); return resource; } catch (Exception e) { @@ -761,7 +771,7 @@ public abstract class BaseController { try { validateId(resourceId, "Incorrect resourceId " + resourceId); TbResourceInfo resourceInfo = resourceService.findResourceInfoById(getCurrentUser().getTenantId(), resourceId); - checkNotNull(resourceInfo); + checkNotNull(resourceInfo, "Resource with id [" + resourceId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.TB_RESOURCE, operation, resourceId, resourceInfo); return resourceInfo; } catch (Exception e) { @@ -773,7 +783,7 @@ public abstract class BaseController { try { validateId(otaPackageId, "Incorrect otaPackageId " + otaPackageId); OtaPackage otaPackage = otaPackageService.findOtaPackageById(getCurrentUser().getTenantId(), otaPackageId); - checkNotNull(otaPackage); + checkNotNull(otaPackage, "OTA package with id [" + otaPackageId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.OTA_PACKAGE, operation, otaPackageId, otaPackage); return otaPackage; } catch (Exception e) { @@ -785,7 +795,7 @@ public abstract class BaseController { try { validateId(otaPackageId, "Incorrect otaPackageId " + otaPackageId); OtaPackageInfo otaPackageIn = otaPackageService.findOtaPackageInfoById(getCurrentUser().getTenantId(), otaPackageId); - checkNotNull(otaPackageIn); + checkNotNull(otaPackageIn, "OTA package with id [" + otaPackageId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.OTA_PACKAGE, operation, otaPackageId, otaPackageIn); return otaPackageIn; } catch (Exception e) { @@ -797,7 +807,7 @@ public abstract class BaseController { try { validateId(rpcId, "Incorrect rpcId " + rpcId); Rpc rpc = rpcService.findById(getCurrentUser().getTenantId(), rpcId); - checkNotNull(rpc); + checkNotNull(rpc, "RPC with id [" + rpcId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.RPC, operation, rpcId, rpc); return rpc; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java index 12a5c74588..7611d2da25 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -240,7 +240,7 @@ public class CustomerController extends BaseController { @RequestParam String customerTitle) throws ThingsboardException { try { TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(customerService.findCustomerByTenantIdAndTitle(tenantId, customerTitle)); + return checkNotNull(customerService.findCustomerByTenantIdAndTitle(tenantId, customerTitle), "Customer with title [" + customerTitle + "] is not found"); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index 4bc23f3fb0..c10234042a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -19,6 +19,9 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.Example; +import io.swagger.annotations.ExampleProperty; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -106,6 +109,7 @@ public class DashboardController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/dashboard/serverTime", method = RequestMethod.GET) @ResponseBody + @ApiResponse(code = 200, message = "OK", examples = @Example(value = @ExampleProperty(value = "1636023857137", mediaType = "application/json"))) public long getServerTime() throws ThingsboardException { return System.currentTimeMillis(); } @@ -118,6 +122,7 @@ public class DashboardController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/dashboard/maxDatapointsLimit", method = RequestMethod.GET) @ResponseBody + @ApiResponse(code = 200, message = "OK", examples = @Example(value = @ExampleProperty(value = "5000", mediaType = "application/json"))) public long getMaxDatapointsLimit() throws ThingsboardException { return maxDatapointsLimit; } diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java index 906e87d608..36657e9a91 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java @@ -17,9 +17,13 @@ package org.thingsboard.server.exception; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.Nullable; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.DisabledException; @@ -30,7 +34,9 @@ import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; +import org.springframework.web.util.WebUtils; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.msg.tools.TbRateLimitsException; @@ -42,11 +48,49 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; @Slf4j @RestControllerAdvice public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHandler implements AccessDeniedHandler { + private static final Map statusToErrorCodeMap = new HashMap<>(); + static { + statusToErrorCodeMap.put(HttpStatus.BAD_REQUEST, ThingsboardErrorCode.BAD_REQUEST_PARAMS); + statusToErrorCodeMap.put(HttpStatus.UNAUTHORIZED, ThingsboardErrorCode.AUTHENTICATION); + statusToErrorCodeMap.put(HttpStatus.FORBIDDEN, ThingsboardErrorCode.PERMISSION_DENIED); + statusToErrorCodeMap.put(HttpStatus.NOT_FOUND, ThingsboardErrorCode.ITEM_NOT_FOUND); + statusToErrorCodeMap.put(HttpStatus.METHOD_NOT_ALLOWED, ThingsboardErrorCode.BAD_REQUEST_PARAMS); + statusToErrorCodeMap.put(HttpStatus.NOT_ACCEPTABLE, ThingsboardErrorCode.BAD_REQUEST_PARAMS); + statusToErrorCodeMap.put(HttpStatus.UNSUPPORTED_MEDIA_TYPE, ThingsboardErrorCode.BAD_REQUEST_PARAMS); + statusToErrorCodeMap.put(HttpStatus.TOO_MANY_REQUESTS, ThingsboardErrorCode.TOO_MANY_REQUESTS); + statusToErrorCodeMap.put(HttpStatus.INTERNAL_SERVER_ERROR, ThingsboardErrorCode.GENERAL); + statusToErrorCodeMap.put(HttpStatus.SERVICE_UNAVAILABLE, ThingsboardErrorCode.GENERAL); + } + private static final Map errorCodeToStatusMap = new HashMap<>(); + static { + errorCodeToStatusMap.put(ThingsboardErrorCode.GENERAL, HttpStatus.INTERNAL_SERVER_ERROR); + errorCodeToStatusMap.put(ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED); + errorCodeToStatusMap.put(ThingsboardErrorCode.JWT_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED); + errorCodeToStatusMap.put(ThingsboardErrorCode.CREDENTIALS_EXPIRED, HttpStatus.UNAUTHORIZED); + errorCodeToStatusMap.put(ThingsboardErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN); + errorCodeToStatusMap.put(ThingsboardErrorCode.INVALID_ARGUMENTS, HttpStatus.BAD_REQUEST); + errorCodeToStatusMap.put(ThingsboardErrorCode.BAD_REQUEST_PARAMS, HttpStatus.BAD_REQUEST); + errorCodeToStatusMap.put(ThingsboardErrorCode.ITEM_NOT_FOUND, HttpStatus.NOT_FOUND); + errorCodeToStatusMap.put(ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS); + errorCodeToStatusMap.put(ThingsboardErrorCode.TOO_MANY_UPDATES, HttpStatus.TOO_MANY_REQUESTS); + errorCodeToStatusMap.put(ThingsboardErrorCode.SUBSCRIPTION_VIOLATION, HttpStatus.FORBIDDEN); + } + + private static ThingsboardErrorCode statusToErrorCode(HttpStatus status) { + return statusToErrorCodeMap.getOrDefault(status, ThingsboardErrorCode.GENERAL); + } + + private static HttpStatus errorCodeToStatus(ThingsboardErrorCode errorCode) { + return errorCodeToStatusMap.getOrDefault(errorCode, HttpStatus.INTERNAL_SERVER_ERROR); + } + @Autowired private ObjectMapper mapper; @@ -95,36 +139,22 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand } } + @NotNull + @Override + protected ResponseEntity handleExceptionInternal( + @NotNull Exception ex, @Nullable Object body, + @NotNull HttpHeaders headers, @NotNull HttpStatus status, + @NotNull WebRequest request) { + if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) { + request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST); + } + ThingsboardErrorCode errorCode = statusToErrorCode(status); + return new ResponseEntity<>(ThingsboardErrorResponse.of(ex.getMessage(), errorCode, status), headers, status); + } private void handleThingsboardException(ThingsboardException thingsboardException, HttpServletResponse response) throws IOException { - ThingsboardErrorCode errorCode = thingsboardException.getErrorCode(); - HttpStatus status; - - switch (errorCode) { - case AUTHENTICATION: - status = HttpStatus.UNAUTHORIZED; - break; - case PERMISSION_DENIED: - status = HttpStatus.FORBIDDEN; - break; - case INVALID_ARGUMENTS: - status = HttpStatus.BAD_REQUEST; - break; - case ITEM_NOT_FOUND: - status = HttpStatus.NOT_FOUND; - break; - case BAD_REQUEST_PARAMS: - status = HttpStatus.BAD_REQUEST; - break; - case GENERAL: - status = HttpStatus.INTERNAL_SERVER_ERROR; - break; - default: - status = HttpStatus.INTERNAL_SERVER_ERROR; - break; - } - + HttpStatus status = errorCodeToStatus(errorCode); response.setStatus(status.value()); mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of(thingsboardException.getMessage(), errorCode, status)); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index a33beaa535..ebf5501f4a 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -89,6 +89,11 @@ server: # Default value of the server side RPC timeout. default_timeout: "${DEFAULT_SERVER_SIDE_RPC_TIMEOUT:10000}" +# Application info +app: + # Application version + version: "@project.version@" + # Zookeeper connection parameters. Used for service discovery. zk: # Enable/disable zookeeper discovery service. @@ -859,7 +864,7 @@ swagger: license: title: "${SWAGGER_LICENSE_TITLE:Apache License Version 2.0}" url: "${SWAGGER_LICENSE_URL:https://github.com/thingsboard/thingsboard/blob/master/LICENSE}" - version: "${SWAGGER_VERSION:2.0}" + version: "${SWAGGER_VERSION:}" queue: type: "${TB_QUEUE_TYPE:in-memory}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java index 61328dd352..5467db00e3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java @@ -58,7 +58,7 @@ public class AdminSettings extends BaseData { return super.getCreatedTime(); } - @ApiModelProperty(position = 3, value = "The Administration Settings key, (e.g. 'general' or 'mail')") + @ApiModelProperty(position = 3, value = "The Administration Settings key, (e.g. 'general' or 'mail')", example = "mail") public String getKey() { return key; } diff --git a/pom.xml b/pom.xml index 1c73f6b919..e5ce0161ac 100755 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ 4.8.0 2.19.1 3.0.2 - 3.0.3 + 3.0.4 1.6.3 0.7 1.15.0 diff --git a/transport/lwm2m/src/main/data/lwm2mserver.jks b/transport/lwm2m/src/main/data/lwm2mserver.jks index 9f6748f8fdab7179b895eccd71421fe1565d27db..5fab824aa1b19928fe711701cd27857472a9e739 100644 GIT binary patch delta 3653 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 X_d}DEK0Izji}dfgHE9+C0|ADhaZ(kz delta 3758 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 z