diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md index 6d8889f98d..f5d093f424 100644 --- a/.github/ISSUE_TEMPLATE/---bug-report.md +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -3,7 +3,7 @@ name: "\U0001F41E Bug report" about: Create a report to help us improve title: "Your title here" labels: ['bug', 'unconfirmed'] -assignees: AndriichnekoDm +assignees: Ultrazombie --- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index c73f810cd8..1168adaf15 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -3,7 +3,7 @@ name: Feature request about: Suggest an idea for this project title: "Your title here" labels: ['feature'] -assignees: 'AndriichnekoDm' +assignees: 'Ultrazombie' --- diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 2f15690f68..dea6318ac5 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -3,7 +3,7 @@ name: Question about: Describe your questions in detail title: "Your title here" labels: ['question'] -assignees: 'AndriichnekoDm' +assignees: 'Ultrazombie' --- diff --git a/application/pom.xml b/application/pom.xml index adc3f1e801..902b541566 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -262,7 +262,7 @@ opensmpp-core - org.springdoc + org.thingsboard springdoc-openapi-starter-webmvc-ui diff --git a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json index 83a96c6fe6..99ff00e602 100644 --- a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json +++ b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json @@ -13,6 +13,7 @@ "datasources": [ { "type": "entity", + "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018", "dataKeys": [ { "name": "ruleEngineException", @@ -51,7 +52,59 @@ "_hash": 0.7255162989552142 } ], - "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018" + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + }, + "latestDataKeys": [ + { + "name": "queueName", + "type": "entityField", + "label": "Queue name", + "color": "#ffc107", + "settings": { + "show": false, + "order": null, + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.8104572478982748, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "serviceId", + "type": "entityField", + "label": "Service Id", + "color": "#607d8b", + "settings": { + "show": false, + "order": null, + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.38329217099945034, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] } ], "timewindow": { @@ -71,7 +124,9 @@ "settings": { "showTimestamp": true, "displayPagination": true, - "defaultPageSize": 10 + "defaultPageSize": 10, + "enableSearch": true, + "enableSelectColumnDisplay": true }, "title": "Exceptions", "dropShadow": true, @@ -89,7 +144,10 @@ "iconColor": "rgba(0, 0, 0, 0.87)", "iconSize": "24px", "titleTooltip": "", - "displayTimewindow": true + "displayTimewindow": true, + "configMode": "basic", + "titleFont": null, + "titleColor": null }, "id": "5eb79712-5c24-3060-7e4f-6af36b8f842d", "typeFullFqn": "system.cards.timeseries_table" @@ -329,7 +387,25 @@ "statusList": [ "ACTIVE" ] - } + }, + "latestDataKeys": [ + { + "name": "queueName", + "type": "entityField", + "label": "Queue name", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8012481564934415 + }, + { + "name": "serviceId", + "type": "entityField", + "label": "Service Id", + "color": "#607d8b", + "settings": {}, + "_hash": 0.0724871638610094 + } + ] } ], "timewindow": { @@ -724,7 +800,25 @@ "statusList": [ "ACTIVE" ] - } + }, + "latestDataKeys": [ + { + "name": "queueName", + "type": "entityField", + "label": "Queue name", + "color": "#f44336", + "settings": {}, + "_hash": 0.7242351292118758 + }, + { + "name": "serviceId", + "type": "entityField", + "label": "Service Id", + "color": "#ffc107", + "settings": {}, + "_hash": 0.3347262075244206 + } + ] } ], "timewindow": { @@ -1004,12 +1098,9 @@ "id": "140f23dd-e3a0-ed98-6189-03c49d2d8018", "alias": "TbServiceQueues", "filter": { - "type": "assetType", + "type": "entityType", "resolveMultiple": true, - "assetNameFilter": "", - "assetTypes": [ - "TbServiceQueue" - ] + "entityType": "QUEUE_STATS" } } }, diff --git a/application/src/main/data/upgrade/3.6.3/schema_update.sql b/application/src/main/data/upgrade/3.6.3/schema_update.sql index e2d454353e..d7d8887d13 100644 --- a/application/src/main/data/upgrade/3.6.3/schema_update.sql +++ b/application/src/main/data/upgrade/3.6.3/schema_update.sql @@ -14,7 +14,6 @@ -- limitations under the License. -- - -- create new attribute_kv table schema DO $$ @@ -105,4 +104,33 @@ EXCEPTION ROLLBACK; RAISE EXCEPTION 'Error during COPY: %', SQLERRM; END -$$; \ No newline at end of file +$$; + +-- OAUTH2 PARAMS ALTER TABLE START + +ALTER TABLE oauth2_params + ADD COLUMN IF NOT EXISTS edge_enabled boolean DEFAULT false; + +-- OAUTH2 PARAMS ALTER TABLE END + +-- QUEUE STATS UPDATE START + +CREATE TABLE IF NOT EXISTS queue_stats ( + id uuid NOT NULL CONSTRAINT queue_stats_pkey PRIMARY KEY, + created_time bigint NOT NULL, + tenant_id uuid NOT NULL, + queue_name varchar(255) NOT NULL, + service_id varchar(255) NOT NULL, + CONSTRAINT queue_stats_name_unq_key UNIQUE (tenant_id, queue_name, service_id) +); + +INSERT INTO queue_stats + SELECT id, created_time, tenant_id, substring(name FROM 1 FOR position('_' IN name) - 1) AS queue_name, + substring(name FROM position('_' IN name) + 1) AS service_id + FROM asset + WHERE type = 'TbServiceQueue' and name LIKE '%\_%'; + +DELETE FROM asset WHERE type='TbServiceQueue'; +DELETE FROM asset_profile WHERE name ='TbServiceQueue'; + +-- QUEUE STATS UPDATE END diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index 6a10791091..640d47362a 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -79,6 +79,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; @@ -447,6 +448,11 @@ public class ActorSystemContext { @Getter private QueueService queueService; + @Lazy + @Autowired(required = false) + @Getter + private QueueStatsService queueStatsService; + @Lazy @Autowired(required = false) @Getter diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index d6b5da4ddf..51205e97c9 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -21,6 +21,7 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; +import jakarta.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.thingsboard.common.util.JacksonUtil; @@ -68,7 +69,6 @@ import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg; import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; import org.thingsboard.server.common.util.KvProtoUtil; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry; @@ -97,7 +97,6 @@ import org.thingsboard.server.service.rpc.RpcSubmitStrategy; import org.thingsboard.server.service.state.DefaultDeviceStateService; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; -import jakarta.annotation.Nullable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 79dddfdbe4..0dc9d68984 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -96,6 +96,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; @@ -768,6 +769,11 @@ class DefaultTbContext implements TbContext { return mainCtx.getQueueService(); } + @Override + public QueueStatsService getQueueStatsService() { + return mainCtx.getQueueStatsService(); + } + @Override public EventLoopGroup getSharedEventLoop() { return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup(); 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 21255bf1f7..963205c08b 100644 --- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java @@ -15,12 +15,17 @@ */ package org.thingsboard.server.config; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonNode; import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.core.converter.ModelConverter; import io.swagger.v3.core.converter.ModelConverters; +import io.swagger.v3.core.util.Json; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; import io.swagger.v3.oas.models.examples.Example; import io.swagger.v3.oas.models.info.Contact; import io.swagger.v3.oas.models.info.Info; @@ -33,29 +38,42 @@ import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; -import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.tags.Tag; import lombok.extern.slf4j.Slf4j; import org.springdoc.core.customizers.OpenApiCustomizer; +import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.customizers.RouterOperationCustomizer; +import org.springdoc.core.discoverer.SpringDocParameterNameDiscoverer; import org.springdoc.core.models.GroupedOpenApi; +import org.springdoc.core.properties.SpringDocConfigProperties; import org.springdoc.core.properties.SwaggerUiConfigProperties; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Profile; +import org.springframework.core.MethodParameter; import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.RequestParam; +import org.thingsboard.common.util.JacksonUtil; 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 java.util.Collections; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @@ -67,8 +85,14 @@ public class SwaggerConfiguration { public static final String LOGIN_ENDPOINT = "/api/auth/login"; + private static final ApiResponses loginResponses = loginResponses(); + private static final ApiResponses defaultErrorResponses = defaultErrorResponses(false); + private static final ApiResponses defaultPostErrorResponses = defaultErrorResponses(true); + @Value("${swagger.api_path:/api/**}") private String apiPath; + @Value("${swagger.security_path_regex}") + private String securityPathRegex; @Value("${swagger.non_security_path_regex}") private String nonSecurityPathRegex; @Value("${swagger.title}") @@ -90,6 +114,7 @@ public class SwaggerConfiguration { @Value("${app.version:unknown}") private String appVersion; + @Bean public OpenAPI thingsboardApi() { Contact contact = new Contact() @@ -115,93 +140,24 @@ public class SwaggerConfiguration { SecurityScheme securityScheme = new SecurityScheme() .type(SecurityScheme.Type.HTTP) - .scheme("bearer") - .bearerFormat("JWT") - .in(SecurityScheme.In.HEADER) - .description("Enter Username / Password"); + .description("Enter Username / Password") + .scheme("loginPassword") + .bearerFormat("/api/auth/login|X-Authorization"); var openApi = new OpenAPI() - .addServersItem(new Server().url("/").description("Default Server URL")) .components(new Components().addSecuritySchemes("HTTP login form", securityScheme)) .info(info); + addDefaultSchemas(openApi); addLoginOperation(openApi); return openApi; } - public void addLoginOperation(OpenAPI openAPI) { - openAPI.getComponents() - .addSchemas("LoginRequest", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(LoginRequest.class)).schema) - .addSchemas("LoginResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(LoginResponse.class)).schema) - .addSchemas("ThingsboardErrorResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(ThingsboardErrorResponse.class)).schema) - .addSchemas("ThingsboardCredentialsExpiredResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(ThingsboardCredentialsExpiredResponse.class)).schema); - - var operation = new Operation(); - operation.summary("Login method to get user JWT token data"); - operation.description("Login method used to authenticate user and get JWT token data.\n\nValue of the response **token** " + - "field can be used as **X-Authorization** header value:\n\n`X-Authorization: Bearer $JWT_TOKEN_VALUE`."); - var requestBody = new RequestBody().content(new Content().addMediaType(APPLICATION_JSON_VALUE, - new MediaType().schema(new Schema().$ref("#/components/schemas/LoginRequest")))); - operation.requestBody(requestBody); - operation.responses(getResponses()); - operation.addTagsItem("login-endpoint"); - var pathItem = new PathItem().post(operation); - openAPI.path(LOGIN_ENDPOINT, pathItem); - } - - private ApiResponses getResponses() { - ApiResponses apiResponses = new ApiResponses(); - - apiResponses.addApiResponse("200", new ApiResponse().description("OK") - .content(new Content().addMediaType(APPLICATION_JSON_VALUE, - new MediaType().schema(new Schema().$ref("#/components/schemas/LoginResponse"))))); - - ApiResponse unauthorizedResponse = new ApiResponse().description("Unauthorized"); - Content content = new Content(); - MediaType mediaType = new MediaType().schema(new Schema().$ref("#/components/schemas/ThingsboardErrorResponse")); - - Map examples = Map.of( - "bad-credentials", errorExample("Bad credentials", - ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)), - "token-expired", errorExample("JWT token expired", - ThingsboardErrorResponse.of("Token has expired", ThingsboardErrorCode.JWT_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED)), - "account-disabled", errorExample("Disabled account", - ThingsboardErrorResponse.of("User account is not active", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)), - "account-locked", errorExample("Locked account", - ThingsboardErrorResponse.of("User account is locked due to security policy", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)), - "authentication-failed", errorExample("General authentication error", - ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)) - ); - - mediaType.setExamples(examples); - content.addMediaType(APPLICATION_JSON_VALUE, mediaType); - unauthorizedResponse.setContent(content); - apiResponses.addApiResponse("401", unauthorizedResponse); - - ApiResponse expiredCredentialsResponse = new ApiResponse().description("Unauthorized (**Expired credentials**)"); - Content expiredContent = new Content(); - MediaType expiredMediaType = new MediaType().schema(new Schema().$ref("#/components/schemas/ThingsboardCredentialsExpiredResponse")); - expiredMediaType.addExamples("credentials-expired", errorExample("Expired credentials", - ThingsboardCredentialsExpiredResponse.of("User password expired!", StringUtils.randomAlphanumeric(30)))); - expiredContent.addMediaType(APPLICATION_JSON_VALUE, expiredMediaType); - expiredCredentialsResponse.setContent(expiredContent); - apiResponses.addApiResponse("401 ", expiredCredentialsResponse); - - return apiResponses; - } - - private Example errorExample(String summary, ThingsboardErrorResponse example) { - return new Example() - .summary(summary) - .value(example); - } - @Bean - public GroupedOpenApi groupedApi() { - return GroupedOpenApi.builder() - .group("thingsboard") - .pathsToMatch(apiPath) - .addOpenApiCustomizer(customOpenApiCustomizer()) - .build(); + @Primary + public SpringDocConfigProperties springDocConfig(SpringDocConfigProperties springDocProperties) { + springDocProperties.getApiDocs().setVersion(SpringDocConfigProperties.ApiDocs.OpenApiVersion.OPENAPI_3_1); + springDocProperties.setRemoveBrokenReferenceDefinitions(false); + return springDocProperties; } @Bean @@ -232,35 +188,166 @@ public class SwaggerConfiguration { return uiProperties; } - public OpenApiCustomizer customOpenApiCustomizer() { - var loginForm = new SecurityRequirement().addList("HTTP login form"); - return openAPI -> openAPI.getPaths().entrySet().stream().peek(entry -> { - securityCustomization(loginForm, entry); - if (!entry.getKey().equals(LOGIN_ENDPOINT)) { - defaultErrorResponsesCustomization(entry.getValue()); + private void addLoginOperation(OpenAPI openAPI) { + var operation = new Operation(); + operation.summary("Login method to get user JWT token data"); + operation.description(""" + Login method used to authenticate user and get JWT token data. + + Value of the response **token** field can be used as **X-Authorization** header value: + + `X-Authorization: Bearer $JWT_TOKEN_VALUE`."""); + var requestBody = new RequestBody().description("Login request") + .content(new Content().addMediaType(APPLICATION_JSON_VALUE, + new MediaType().schema(new Schema().$ref("#/components/schemas/LoginRequest")))); + operation.requestBody(requestBody); + + operation.responses(loginResponses); + + operation.addTagsItem("login-endpoint"); + var pathItem = new PathItem().post(operation); + openAPI.path(LOGIN_ENDPOINT, pathItem); + } + + @Bean + public GroupedOpenApi groupedApi(SpringDocParameterNameDiscoverer localSpringDocParameterNameDiscoverer) { + return GroupedOpenApi.builder() + .group("thingsboard") + .pathsToMatch(apiPath) + .addRouterOperationCustomizer(routerOperationCustomizer(localSpringDocParameterNameDiscoverer)) + .addOperationCustomizer(operationCustomizer()) + .addOpenApiCustomizer(customOpenApiCustomizer()) + .build(); + } + + @Bean + @Lazy(false) + ModelConverter mapAwareConverter() { + return (type, context, chain) -> { + if (chain.hasNext()) { + Schema schema = chain.next().resolve(type, context, chain); + JavaType javaType = Json.mapper().constructType(type.getType()); + if (javaType != null) { + Class cls = javaType.getRawClass(); + if (Map.class.isAssignableFrom(cls)) { + if (schema != null && schema.getProperties() != null) { + schema.getProperties().remove("empty"); + if (schema.getProperties().isEmpty()) { + schema.setProperties(null); + } + } + } + } + return schema; + } else { + return null; } - }).map(this::tagsCustomization).forEach(openAPI::addTagsItem); + }; } - private Tag tagsCustomization(Map.Entry entry) { - var operations = entry.getValue().readOperationsMap().values(); - var tagItem = operations.stream().findAny().get().getTags().get(0); - return tagFromTagItem(tagItem); + private void addDefaultSchemas(OpenAPI openAPI) { + var jsonNodeSchema = ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(JsonNode.class)).schema; + jsonNodeSchema.setType("any"); + //noinspection unchecked + jsonNodeSchema.setExamples(List.of(JacksonUtil.newObjectNode())); + jsonNodeSchema.setDescription("A value representing the any type (object or primitive)"); + openAPI.getComponents() + .addSchemas("JsonNode", jsonNodeSchema) + .addSchemas("LoginRequest", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(LoginRequest.class)).schema) + .addSchemas("LoginResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(LoginResponse.class)).schema) + .addSchemas("ThingsboardErrorResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(ThingsboardErrorResponse.class)).schema) + .addSchemas("ThingsboardCredentialsExpiredResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(ThingsboardCredentialsExpiredResponse.class)).schema); } - private void defaultErrorResponsesCustomization(PathItem pathItem) { - pathItem.readOperationsMap().forEach(((httpMethod, operation) -> { - operation.setResponses(getResponses(operation.getResponses(), httpMethod.equals(PathItem.HttpMethod.POST))); - })); + private RouterOperationCustomizer routerOperationCustomizer(SpringDocParameterNameDiscoverer localSpringDocParameterNameDiscoverer) { + return (routerOperation, handlerMethod) -> { + String[] pNames = localSpringDocParameterNameDiscoverer.getParameterNames(handlerMethod.getMethod()); + String[] reflectionParametersNames = Arrays.stream(handlerMethod.getMethod().getParameters()).map(java.lang.reflect.Parameter::getName).toArray(String[]::new); + if (pNames == null || Arrays.stream(pNames).anyMatch(Objects::isNull)) + pNames = reflectionParametersNames; + MethodParameter[] parameters = handlerMethod.getMethodParameters(); + List requestParams = new ArrayList<>(); + for (var i = 0; i < parameters.length; i++) { + var methodParameter = parameters[i]; + RequestParam requestParam = methodParameter.getParameterAnnotation(RequestParam.class); + if (requestParam != null) { + String pName = StringUtils.isNotBlank(requestParam.value()) ? requestParam.value() : + pNames[i]; + if (StringUtils.isNotBlank(pName)) { + requestParams.add(pName); + } + } + } + if (!requestParams.isEmpty()) { + var path = routerOperation.getPath() + "{?" + String.join(",", requestParams) + "}"; + routerOperation.setPath(path); + } + return routerOperation; + }; } - private void securityCustomization(SecurityRequirement loginForm, Map.Entry entry) { - if (!(entry.getKey().matches(nonSecurityPathRegex) || entry.getKey().equals(LOGIN_ENDPOINT))) { - entry.getValue() - .readOperationsMap() - .values() - .forEach(operation -> operation.addSecurityItem(loginForm)); + private OperationCustomizer operationCustomizer() { + return (operation, handlerMethod) -> { + if (StringUtils.isBlank(operation.getSummary())) { + operation.setSummary(operation.getOperationId()); + } + return operation; + }; + } + + private OpenApiCustomizer customOpenApiCustomizer() { + var loginForm = new SecurityRequirement().addList("HTTP login form", Arrays.asList( + Authority.SYS_ADMIN.name(), + Authority.TENANT_ADMIN.name(), + Authority.CUSTOMER_USER.name() + )); + return openAPI -> { + var paths = openAPI.getPaths(); + paths.entrySet().stream().peek(entry -> { + securityCustomization(loginForm, entry); + if (!entry.getKey().equals(LOGIN_ENDPOINT)) { + defaultErrorResponsesCustomization(entry.getValue()); + } + }).map(this::tagsCustomization).filter(Objects::nonNull).distinct().sorted(Comparator.comparing(Tag::getName)).forEach(openAPI::addTagsItem); + + var pathItemsByTags = new TreeMap>(); + paths.forEach((k, v) -> { + var tagItem = tagItemFromPathItem(v); + if (tagItem != null) { + var pathItemMap = pathItemsByTags.computeIfAbsent(tagItem, k1 -> new TreeMap<>()); + pathItemMap.put(k, v); + } + }); + var sortedPaths = new Paths(); + pathItemsByTags.forEach((tagItem, pathItemMap) -> { + pathItemMap.forEach(sortedPaths::addPathItem); + }); + sortedPaths.setExtensions(paths.getExtensions()); + openAPI.setPaths(sortedPaths); + var sortedSchemas = new TreeMap<>(openAPI.getComponents().getSchemas()); + openAPI.getComponents().setSchemas(new LinkedHashMap<>(sortedSchemas)); + }; + } + + + private Tag tagsCustomization(Map.Entry entry) { + var tagItem = tagItemFromPathItem(entry.getValue()); + if (tagItem != null) { + return tagFromTagItem(tagItem); } + return null; + } + + private String tagItemFromPathItem(PathItem item) { + var operations = item.readOperationsMap().values(); + var operation = operations.stream().findAny(); + if (operation.isPresent()) { + var tags = operation.get().getTags(); + if (tags != null && !tags.isEmpty()) { + return tags.get(0); + } + } + return null; } private Tag tagFromTagItem(String tagItem) { @@ -276,32 +363,113 @@ public class SwaggerConfiguration { return new Tag().name(tagItem).description(sb.toString().trim()); } - private ApiResponses getResponses(ApiResponses apiResponses, boolean isPost) { - if (apiResponses == null) { - apiResponses = new ApiResponses(); + private void defaultErrorResponsesCustomization(PathItem pathItem) { + pathItem.readOperationsMap().forEach(((httpMethod, operation) -> { + var errorResponses = httpMethod.equals(PathItem.HttpMethod.POST) ? defaultPostErrorResponses : defaultErrorResponses; + var responses = operation.getResponses(); + if (responses == null) { + responses = errorResponses; + } else { + ApiResponses updated = responses; + errorResponses.forEach((key, apiResponse) -> { + if (!updated.containsKey(key)) { + updated.put(key, apiResponse); + } + }); + } + operation.setResponses(responses); + })); + } + + private void securityCustomization(SecurityRequirement loginForm, Map.Entry entry) { + var path = entry.getKey(); + if (path.matches(securityPathRegex) && !path.matches(nonSecurityPathRegex) && !path.equals(LOGIN_ENDPOINT)) { + entry.getValue() + .readOperationsMap() + .values() + .forEach(operation -> operation.addSecurityItem(loginForm)); } + } - apiResponses.addApiResponse("400", new ApiResponse().description("Bad Request") - .content(getErrorContent(ThingsboardErrorResponse.of(isPost ? "Invalid request body" : "Invalid UUID string: 123", ThingsboardErrorCode.BAD_REQUEST_PARAMS, HttpStatus.BAD_REQUEST)))); + private static ApiResponses loginResponses() { + ApiResponses apiResponses = new ApiResponses(); + apiResponses.addApiResponse("200", new ApiResponse().description("OK") + .content(new Content().addMediaType(APPLICATION_JSON_VALUE, + new MediaType().schema(new Schema().$ref("#/components/schemas/LoginResponse"))))); + apiResponses.putAll(loginErrorResponses()); + return apiResponses; + } + + private static ApiResponses defaultErrorResponses(boolean isPost) { + ApiResponses apiResponses = new ApiResponses(); + apiResponses.addApiResponse("400", errorResponse("400", "Bad Request", + ThingsboardErrorResponse.of(isPost ? "Invalid request body" : "Invalid UUID string: 123", ThingsboardErrorCode.BAD_REQUEST_PARAMS, HttpStatus.BAD_REQUEST))); - apiResponses.addApiResponse("401", new ApiResponse().description("Unauthorized") - .content(getErrorContent(ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)))); + apiResponses.addApiResponse("401", errorResponse("401", "Unauthorized", + ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED))); - apiResponses.addApiResponse("403", new ApiResponse().description("Forbidden") - .content(getErrorContent(ThingsboardErrorResponse.of("You don't have permission to perform this operation!", ThingsboardErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN)))); + apiResponses.addApiResponse("403", errorResponse("403", "Forbidden", + ThingsboardErrorResponse.of("You don't have permission to perform this operation!", ThingsboardErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN))); - apiResponses.addApiResponse("404", new ApiResponse().description("Not Found") - .content(getErrorContent(ThingsboardErrorResponse.of("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND, HttpStatus.NOT_FOUND)))); + apiResponses.addApiResponse("404", errorResponse("404", "Not Found", + ThingsboardErrorResponse.of("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND, HttpStatus.NOT_FOUND))); - apiResponses.addApiResponse("429", new ApiResponse().description("Too Many Requests") - .content(getErrorContent(ThingsboardErrorResponse.of("Too many requests for current tenant!", ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS)))); + apiResponses.addApiResponse("429", errorResponse("429", "Too Many Requests", + ThingsboardErrorResponse.of("Too many requests for current tenant!", ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS))); return apiResponses; } - private Content getErrorContent(ThingsboardErrorResponse errorResponse) { - return new Content().addMediaType(org.springframework.http.MediaType.APPLICATION_JSON_VALUE, - new MediaType().schema(new Schema().example(errorResponse))); + private static ApiResponses loginErrorResponses() { + ApiResponses apiResponses = new ApiResponses(); + + apiResponses.addApiResponse("401", errorResponse("Unauthorized", + Map.of( + "bad-credentials", errorExample("Bad credentials", + ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)), + "token-expired", errorExample("JWT token expired", + ThingsboardErrorResponse.of("Token has expired", ThingsboardErrorCode.JWT_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED)), + "account-disabled", errorExample("Disabled account", + ThingsboardErrorResponse.of("User account is not active", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)), + "account-locked", errorExample("Locked account", + ThingsboardErrorResponse.of("User account is locked due to security policy", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)), + "authentication-failed", errorExample("General authentication error", + ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)) + ) + )); + var credentialsExpiredSchema = new Schema(); + credentialsExpiredSchema.$ref("#/components/schemas/ThingsboardCredentialsExpiredResponse"); + apiResponses.addApiResponse("401 ", errorResponse("Unauthorized (**Expired credentials**)", + Map.of( + "credentials-expired", errorExample("Expired credentials", + ThingsboardCredentialsExpiredResponse.of("User password expired!", StringUtils.randomAlphanumeric(30))) + ), + credentialsExpiredSchema + )); + return apiResponses; + } + + private static ApiResponse errorResponse(String code, String description, ThingsboardErrorResponse example) { + return errorResponse(description, Map.of("error-code-" + code, errorExample(description, example))); + } + + private static ApiResponse errorResponse(String description, Map examples) { + var schema = new Schema(); + schema.$ref("#/components/schemas/ThingsboardErrorResponse"); + return errorResponse(description, examples, schema); + } + + private static ApiResponse errorResponse(String description, Map examples, Schema errorResponseSchema) { + MediaType mediaType = new MediaType().schema(errorResponseSchema); + mediaType.setExamples(examples); + Content content = new Content().addMediaType(org.springframework.http.MediaType.APPLICATION_JSON_VALUE, mediaType); + return new ApiResponse().description(description).content(content); + } + + private static Example errorExample(String summary, ThingsboardErrorResponse example) { + return new Example() + .summary(summary) + .value(example); } } diff --git a/application/src/main/java/org/thingsboard/server/config/WebConfig.java b/application/src/main/java/org/thingsboard/server/config/WebConfig.java index 81597952c0..70afc8b99c 100644 --- a/application/src/main/java/org/thingsboard/server/config/WebConfig.java +++ b/application/src/main/java/org/thingsboard/server/config/WebConfig.java @@ -37,4 +37,9 @@ public class WebConfig { response.sendRedirect(baseUrl + "/swagger-ui/"); } + @RequestMapping("/swagger-ui/") + public String redirectSwaggerIndex() throws IOException { + return "forward:/swagger-ui/index.html"; + } + } 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 739f386dc9..818a2a15a6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -184,8 +184,7 @@ public class AdminController extends BaseController { } @ApiOperation(value = "Get the JWT Settings object (getJwtSettings)", - notes = "Get the JWT Settings object that contains JWT token policy, etc. " + SYSTEM_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Get the JWT Settings object that contains JWT token policy, etc. " + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") @RequestMapping(value = "/jwtSettings", method = RequestMethod.GET) @ResponseBody @@ -195,8 +194,7 @@ public class AdminController extends BaseController { } @ApiOperation(value = "Update JWT Settings (saveJwtSettings)", - notes = "Updates the JWT Settings object that contains JWT token policy, etc. The tokenSigningKey field is a Base64 encoded string." + SYSTEM_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Updates the JWT Settings object that contains JWT token policy, etc. The tokenSigningKey field is a Base64 encoded string." + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") @RequestMapping(value = "/jwtSettings", method = RequestMethod.POST) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java index cca82f5ee5..15918f0a08 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java @@ -69,13 +69,12 @@ public class AlarmCommentController extends BaseController { "Referencing non-existing Alarm Comment Id will cause 'Not Found' error. " + "\n\n To create new Alarm comment entity it is enough to specify 'comment' json element with 'text' node, for example: {\"comment\": { \"text\": \"my comment\"}}. " + "\n\n If comment type is not specified the default value 'OTHER' will be saved. If 'alarmId' or 'userId' specified in body it will be ignored." + - TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH - , responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/comment", method = RequestMethod.POST) @ResponseBody public AlarmComment saveAlarmComment(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) - @PathVariable(ALARM_ID) String strAlarmId, @Parameter(description = "A JSON value representing the comment.") @RequestBody AlarmComment alarmComment) throws ThingsboardException { + @PathVariable(ALARM_ID) String strAlarmId, @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the comment.") @RequestBody AlarmComment alarmComment) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); Alarm alarm = checkAlarmInfoId(alarmId, Operation.WRITE); @@ -84,7 +83,7 @@ public class AlarmCommentController extends BaseController { } @ApiOperation(value = "Delete Alarm comment (deleteAlarmComment)", - notes = "Deletes the Alarm comment. Referencing non-existing Alarm comment Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Deletes the Alarm comment. Referencing non-existing Alarm comment Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/comment/{commentId}", method = RequestMethod.DELETE) @ResponseBody @@ -100,7 +99,7 @@ public class AlarmCommentController extends BaseController { @ApiOperation(value = "Get Alarm comments (getAlarmComments)", notes = "Returns a page of alarm comments for specified alarm. " + - PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/comment", method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index d983f08ec2..e78313f6d8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -104,7 +104,7 @@ public class AlarmController extends BaseController { "filled in the AlarmInfo object field: 'originatorName' or will returns as null."; @ApiOperation(value = "Get Alarm (getAlarmById)", - notes = "Fetch the Alarm object based on the provided Alarm Id. " + ALARM_SECURITY_CHECK, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Fetch the Alarm object based on the provided Alarm Id. " + ALARM_SECURITY_CHECK) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.GET) @ResponseBody @@ -117,7 +117,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Get Alarm Info (getAlarmInfoById)", notes = "Fetch the Alarm Info object based on the provided Alarm Id. " + - ALARM_SECURITY_CHECK + ALARM_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + ALARM_SECURITY_CHECK + ALARM_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/info/{alarmId}", method = RequestMethod.GET) @ResponseBody @@ -139,11 +139,11 @@ public class AlarmController extends BaseController { "If the user clears the alarm (see 'Clear Alarm(clearAlarm)'), than new alarm with the same type and same device may be created. " + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Alarm entity. " + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH - , responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + ) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm", method = RequestMethod.POST) @ResponseBody - public Alarm saveAlarm(@Parameter(description = "A JSON value representing the alarm.") @RequestBody Alarm alarm) throws ThingsboardException { + public Alarm saveAlarm(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the alarm.") @RequestBody Alarm alarm) throws ThingsboardException { alarm.setTenantId(getTenantId()); checkNotNull(alarm.getOriginator()); checkEntity(alarm.getId(), alarm, Resource.ALARM); @@ -155,7 +155,7 @@ public class AlarmController extends BaseController { } @ApiOperation(value = "Delete Alarm (deleteAlarm)", - notes = "Deletes the Alarm. Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Deletes the Alarm. Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.DELETE) @ResponseBody @@ -169,7 +169,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Acknowledge Alarm (ackAlarm)", notes = "Acknowledge the Alarm. " + "Once acknowledged, the 'ack_ts' field will be set to current timestamp and special rule chain event 'ALARM_ACK' will be generated. " + - "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/ack", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) @@ -184,7 +184,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Clear Alarm (clearAlarm)", notes = "Clear the Alarm. " + "Once cleared, the 'clear_ts' field will be set to current timestamp and special rule chain event 'ALARM_CLEAR' will be generated. " + - "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/clear", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) @@ -200,7 +200,7 @@ public class AlarmController extends BaseController { notes = "Assign the Alarm. " + "Once assigned, the 'assign_ts' field will be set to current timestamp and special rule chain event 'ALARM_ASSIGNED' " + "(or ALARM_REASSIGNED in case of assigning already assigned alarm) will be generated. " + - "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/assign/{assigneeId}", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) @@ -221,7 +221,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Unassign Alarm (unassignAlarm)", notes = "Unassign the Alarm. " + "Once unassigned, the 'assign_ts' field will be set to current timestamp and special rule chain event 'ALARM_UNASSIGNED' will be generated. " + - "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/assign", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.OK) @@ -236,7 +236,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Get Alarms (getAlarms)", notes = "Returns a page of alarms for the selected entity. Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error. " + - PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET) @ResponseBody @@ -292,7 +292,7 @@ public class AlarmController extends BaseController { "If the user has the authority of 'Tenant Administrator', the server returns alarms that belongs to the tenant of current user. " + "If the user has the authority of 'Customer User', the server returns alarms that belongs to the customer of current user. " + "Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error. " + - PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarms", method = RequestMethod.GET) @ResponseBody @@ -341,7 +341,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Get Alarms (getAlarmsV2)", notes = "Returns a page of alarms for the selected entity. " + - PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/v2/alarm/{entityType}/{entityId}", method = RequestMethod.GET) @ResponseBody @@ -407,7 +407,7 @@ public class AlarmController extends BaseController { notes = "Returns a page of alarms that belongs to the current user owner. " + "If the user has the authority of 'Tenant Administrator', the server returns alarms that belongs to the tenant of current user. " + "If the user has the authority of 'Customer User', the server returns alarms that belongs to the customer of current user. " + - PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/v2/alarms", method = RequestMethod.GET) @ResponseBody @@ -468,7 +468,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Get Highest Alarm Severity (getHighestAlarmSeverity)", notes = "Search the alarms by originator ('entityType' and entityId') and optional 'status' or 'searchStatus' filters and returns the highest AlarmSeverity(CRITICAL, MAJOR, MINOR, WARNING or INDETERMINATE). " + "Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH - , responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + ) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/highestSeverity/{entityType}/{entityId}", method = RequestMethod.GET) @ResponseBody @@ -499,8 +499,7 @@ public class AlarmController extends BaseController { } @ApiOperation(value = "Get Alarm Types (getAlarmTypes)", - notes = "Returns a set of unique alarm types based on alarms that are either owned by the tenant or assigned to the customer which user is performing the request.", - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Returns a set of unique alarm types based on alarms that are either owned by the tenant or assigned to the customer which user is performing the request.") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/types", method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index 6f51202c36..8c03d34e8e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -103,7 +103,7 @@ public class AssetController extends BaseController { notes = "Fetch the Asset object based on the provided Asset Id. " + "If the user has the authority of 'Tenant Administrator', the server checks that the asset is owned by the same tenant. " + "If the user has the authority of 'Customer User', the server checks that the asset is assigned to the same customer." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH - , responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + ) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/asset/{assetId}", method = RequestMethod.GET) @ResponseBody @@ -118,7 +118,7 @@ public class AssetController extends BaseController { notes = "Fetch the Asset Info object based on the provided Asset Id. " + "If the user has the authority of 'Tenant Administrator', the server checks that the asset is owned by the same tenant. " + "If the user has the authority of 'Customer User', the server checks that the asset is assigned to the same customer. " - + ASSET_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + ASSET_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/asset/info/{assetId}", method = RequestMethod.GET) @ResponseBody @@ -135,11 +135,11 @@ public class AssetController extends BaseController { "Specify existing Asset id to update the asset. " + "Referencing non-existing Asset Id will cause 'Not Found' error. " + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Asset entity. " - + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/asset", method = RequestMethod.POST) @ResponseBody - public Asset saveAsset(@Parameter(description = "A JSON value representing the asset.") @RequestBody Asset asset) throws Exception { + public Asset saveAsset(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the asset.") @RequestBody Asset asset) throws Exception { asset.setTenantId(getTenantId()); checkEntity(asset.getId(), asset, Resource.ASSET); return tbAssetService.save(asset, getCurrentUser()); @@ -158,7 +158,7 @@ public class AssetController extends BaseController { } @ApiOperation(value = "Assign asset to customer (assignAssetToCustomer)", - notes = "Creates assignment of the asset to customer. Customer will be able to query asset afterwards." + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Creates assignment of the asset to customer. Customer will be able to query asset afterwards." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/{customerId}/asset/{assetId}", method = RequestMethod.POST) @ResponseBody @@ -174,7 +174,7 @@ public class AssetController extends BaseController { } @ApiOperation(value = "Unassign asset from customer (unassignAssetFromCustomer)", - notes = "Clears assignment of the asset to customer. Customer will not be able to query asset afterwards." + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Clears assignment of the asset to customer. Customer will not be able to query asset afterwards." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/asset/{assetId}", method = RequestMethod.DELETE) @ResponseBody @@ -192,7 +192,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Make asset publicly available (assignAssetToPublicCustomer)", notes = "Asset will be available for non-authorized (not logged-in) users. " + "This is useful to create dashboards that you plan to share/embed on a publicly available website. " + - "However, users that are logged-in and belong to different tenant will not be able to access the asset." + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "However, users that are logged-in and belong to different tenant will not be able to access the asset." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/public/asset/{assetId}", method = RequestMethod.POST) @ResponseBody @@ -205,7 +205,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Get Tenant Assets (getTenantAssets)", notes = "Returns a page of assets owned by tenant. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/assets", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -233,7 +233,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Get Tenant Asset Infos (getTenantAssetInfos)", notes = "Returns a page of assets info objects owned by tenant. " + - PAGE_DATA_PARAMETERS + ASSET_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + ASSET_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/assetInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -266,7 +266,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Get Tenant Asset (getTenantAsset)", notes = "Requested asset must be owned by tenant that the user belongs to. " + - "Asset name is an unique property of asset. So it can be used to identify the asset." + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Asset name is an unique property of asset. So it can be used to identify the asset." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/assets", params = {"assetName"}, method = RequestMethod.GET) @ResponseBody @@ -279,7 +279,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Get Customer Assets (getCustomerAssets)", notes = "Returns a page of assets objects assigned to customer. " + - PAGE_DATA_PARAMETERS, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/customer/{customerId}/assets", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -312,7 +312,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Get Customer Asset Infos (getCustomerAssetInfos)", notes = "Returns a page of assets info objects assigned to customer. " + - PAGE_DATA_PARAMETERS + ASSET_INFO_DESCRIPTION, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + ASSET_INFO_DESCRIPTION) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/customer/{customerId}/assetInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -349,7 +349,7 @@ public class AssetController extends BaseController { } @ApiOperation(value = "Get Assets By Ids (getAssetsByIds)", - notes = "Requested assets must be owned by tenant or assigned to customer which user is performing the request. ", responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Requested assets must be owned by tenant or assigned to customer which user is performing the request. ") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/assets", params = {"assetIds"}, method = RequestMethod.GET) @ResponseBody @@ -376,7 +376,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Find related assets (findByQuery)", notes = "Returns all assets that are related to the specific entity. " + "The entity id, relation type, asset types, depth of the search, and other query parameters defined using complex 'AssetSearchQuery' object. " + - "See 'Model' tab of the Parameters for more info.", responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "See 'Model' tab of the Parameters for more info.") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/assets", method = RequestMethod.POST) @ResponseBody @@ -398,8 +398,7 @@ public class AssetController extends BaseController { } @ApiOperation(value = "Get Asset Types (getAssetTypes)", - notes = "Deprecated. See 'getAssetProfileNames' API from Asset Profile Controller instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Deprecated. See 'getAssetProfileNames' API from Asset Profile Controller instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/asset/types", method = RequestMethod.GET) @ResponseBody @@ -416,8 +415,7 @@ public class AssetController extends BaseController { EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION + "Second, remote edge service will receive a copy of assignment asset " + EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + - "Third, once asset will be delivered to edge service, it's going to be available for usage on remote edge instance.", - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Third, once asset will be delivered to edge service, it's going to be available for usage on remote edge instance.") @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.POST) @ResponseBody @@ -440,8 +438,7 @@ public class AssetController extends BaseController { EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION + "Second, remote edge service will receive an 'unassign' command to remove asset " + EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + - "Third, once 'unassign' command will be delivered to edge service, it's going to remove asset locally.", - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Third, once 'unassign' command will be delivered to edge service, it's going to remove asset locally.") @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.DELETE) @ResponseBody @@ -460,7 +457,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Get assets assigned to edge (getEdgeAssets)", notes = "Returns a page of assets assigned to edge. " + - PAGE_DATA_PARAMETERS, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edge/{edgeId}/assets", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -510,7 +507,7 @@ public class AssetController extends BaseController { } @ApiOperation(value = "Import the bulk of assets (processAssetsBulkImport)", - notes = "There's an ability to import the bulk of assets using the only .csv file.", responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "There's an ability to import the bulk of assets using the only .csv file.") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @PostMapping("/asset/bulk_import") public BulkImportResult processAssetsBulkImport(@RequestBody BulkImportRequest request) throws Exception { diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java b/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java index a6126011cc..a4dc7a1cb0 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java @@ -78,8 +78,7 @@ public class AssetProfileController extends BaseController { @ApiOperation(value = "Get Asset Profile (getAssetProfileById)", notes = "Fetch the Asset Profile object based on the provided Asset Profile Id. " + - "The server checks that the asset profile is owned by the same tenant. " + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "The server checks that the asset profile is owned by the same tenant. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/assetProfile/{assetProfileId}", method = RequestMethod.GET) @ResponseBody @@ -99,8 +98,7 @@ public class AssetProfileController extends BaseController { @ApiOperation(value = "Get Asset Profile Info (getAssetProfileInfoById)", notes = "Fetch the Asset Profile Info object based on the provided Asset Profile Id. " - + ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/assetProfileInfo/{assetProfileId}", method = RequestMethod.GET) @ResponseBody @@ -114,8 +112,7 @@ public class AssetProfileController extends BaseController { @ApiOperation(value = "Get Default Asset Profile (getDefaultAssetProfileInfo)", notes = "Fetch the Default Asset Profile Info object. " + - ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/assetProfileInfo/default", method = RequestMethod.GET) @ResponseBody @@ -130,8 +127,7 @@ public class AssetProfileController extends BaseController { "Referencing non-existing asset profile Id will cause 'Not Found' error. " + NEW_LINE + "Asset profile name is unique in the scope of tenant. Only one 'default' asset profile may exist in scope of tenant. " + "Remove 'id', 'tenantId' from the request body example (below) to create new Asset Profile entity. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/assetProfile", method = RequestMethod.POST) @ResponseBody @@ -145,8 +141,7 @@ public class AssetProfileController extends BaseController { @ApiOperation(value = "Delete asset profile (deleteAssetProfile)", notes = "Deletes the asset profile. Referencing non-existing asset profile Id will cause an error. " + - "Can't delete the asset profile if it is referenced by existing assets." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Can't delete the asset profile if it is referenced by existing assets." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/assetProfile/{assetProfileId}", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.OK) @@ -160,8 +155,7 @@ public class AssetProfileController extends BaseController { } @ApiOperation(value = "Make Asset Profile Default (setDefaultAssetProfile)", - notes = "Marks asset profile as default within a tenant scope." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Marks asset profile as default within a tenant scope." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/assetProfile/{assetProfileId}/default", method = RequestMethod.POST) @ResponseBody @@ -177,8 +171,7 @@ public class AssetProfileController extends BaseController { @ApiOperation(value = "Get Asset Profiles (getAssetProfiles)", notes = "Returns a page of asset profile objects owned by tenant. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/assetProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -199,8 +192,7 @@ public class AssetProfileController extends BaseController { @ApiOperation(value = "Get Asset Profile infos (getAssetProfileInfos)", notes = "Returns a page of asset profile info objects owned by tenant. " + - PAGE_DATA_PARAMETERS + ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/assetProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java index 5e58b28ab0..c8ea4073b1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java @@ -74,8 +74,7 @@ public class AuditLogController extends BaseController { @ApiOperation(value = "Get audit logs by customer id (getAuditLogsByCustomerId)", notes = "Returns a page of audit logs related to the targeted customer entities (devices, assets, etc.), " + "and users actions (login, logout, etc.) that belong to this customer. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/audit/logs/customer/{customerId}", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -108,8 +107,7 @@ public class AuditLogController extends BaseController { @ApiOperation(value = "Get audit logs by user id (getAuditLogsByUserId)", notes = "Returns a page of audit logs related to the actions of targeted user. " + "For example, RPC call to a particular device, or alarm acknowledgment for a specific device, etc. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/audit/logs/user/{userId}", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -143,8 +141,7 @@ public class AuditLogController extends BaseController { notes = "Returns a page of audit logs related to the actions on the targeted entity. " + "Basically, this API call is used to get the full lifecycle of some specific entity. " + "For example to see when a device was created, updated, assigned to some customer, or even deleted from the system. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/audit/logs/entity/{entityType}/{entityId}", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -179,8 +176,7 @@ public class AuditLogController extends BaseController { @ApiOperation(value = "Get all audit logs (getAuditLogs)", notes = "Returns a page of audit logs related to all entities in the scope of the current user's Tenant. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/audit/logs", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody 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 9c437821c6..0942329f40 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -506,7 +506,7 @@ public abstract class BaseController { TenantProfile checkTenantProfileId(TenantProfileId tenantProfileId, Operation operation) throws ThingsboardException { try { - validateId(tenantProfileId, "Incorrect tenantProfileId " + tenantProfileId); + validateId(tenantProfileId, id -> "Incorrect tenantProfileId " + id); TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(getTenantId(), tenantProfileId); checkNotNull(tenantProfile, "Tenant profile with id [" + tenantProfileId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.TENANT_PROFILE, operation); @@ -541,7 +541,7 @@ public abstract class BaseController { if (entityId == null) { throw new ThingsboardException("Parameter entityId can't be empty!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } - validateId(entityId.getId(), "Incorrect entityId " + entityId); + validateId(entityId.getId(), id -> "Incorrect entityId " + id); switch (entityId.getEntityType()) { case ALARM: checkAlarmId(new AlarmId(entityId.getId()), operation); @@ -668,7 +668,7 @@ public abstract class BaseController { AlarmComment checkAlarmCommentId(AlarmCommentId alarmCommentId, AlarmId alarmId) throws ThingsboardException { try { - validateId(alarmCommentId, "Incorrect alarmCommentId " + alarmCommentId); + validateId(alarmCommentId, id -> "Incorrect alarmCommentId " + id); AlarmComment alarmComment = alarmCommentService.findAlarmCommentByIdAsync(getCurrentUser().getTenantId(), alarmCommentId).get(); checkNotNull(alarmComment, "Alarm comment with id [" + alarmCommentId + "] is not found"); if (!alarmId.equals(alarmComment.getAlarmId())) { @@ -736,7 +736,7 @@ public abstract class BaseController { } protected RuleNode checkRuleNode(RuleNodeId ruleNodeId, Operation operation) throws ThingsboardException { - validateId(ruleNodeId, "Incorrect ruleNodeId " + ruleNodeId); + validateId(ruleNodeId, id -> "Incorrect ruleNodeId " + id); RuleNode ruleNode = ruleChainService.findRuleNodeById(getTenantId(), ruleNodeId); checkNotNull(ruleNode, "Rule node with id [" + ruleNodeId + "] is not found"); checkRuleChain(ruleNode.getRuleChainId(), operation); 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 c24c345f76..a46cbe7dba 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -130,7 +130,7 @@ public class CustomerController extends BaseController { @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer", method = RequestMethod.POST) @ResponseBody - public Customer saveCustomer(@Parameter(description = "A JSON value representing the customer.") @RequestBody Customer customer) throws Exception { + public Customer saveCustomer(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the customer.") @RequestBody Customer customer) throws Exception { customer.setTenantId(getTenantId()); checkEntity(customer.getId(), customer, Resource.CUSTOMER); return tbCustomerService.save(customer, getCurrentUser()); 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 0683bc21be..013575361d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -133,9 +133,7 @@ public class DashboardController extends BaseController { } @ApiOperation(value = "Get Dashboard Info (getDashboardInfoById)", - notes = "Get the information about the dashboard based on 'dashboardId' parameter. " + DASHBOARD_INFO_DEFINITION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)) - ) + notes = "Get the information about the dashboard based on 'dashboardId' parameter. " + DASHBOARD_INFO_DEFINITION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/dashboard/info/{dashboardId}", method = RequestMethod.GET) @ResponseBody @@ -148,8 +146,7 @@ public class DashboardController extends BaseController { } @ApiOperation(value = "Get Dashboard (getDashboardById)", - notes = "Get the dashboard based on 'dashboardId' parameter. " + DASHBOARD_DEFINITION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)) + notes = "Get the dashboard based on 'dashboardId' parameter. " + DASHBOARD_DEFINITION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH ) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/dashboard/{dashboardId}", method = RequestMethod.GET) @@ -174,13 +171,12 @@ public class DashboardController extends BaseController { "Specify existing Dashboard id to update the dashboard. " + "Referencing non-existing dashboard Id will cause 'Not Found' error. " + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Dashboard entity. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/dashboard", method = RequestMethod.POST) @ResponseBody public Dashboard saveDashboard( - @Parameter(description = "A JSON value representing the dashboard.") + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the dashboard.") @RequestBody Dashboard dashboard) throws Exception { dashboard.setTenantId(getTenantId()); checkEntity(dashboard.getId(), dashboard, Resource.DASHBOARD); @@ -203,8 +199,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Assign the Dashboard (assignDashboardToCustomer)", notes = "Assign the Dashboard to specified Customer or do nothing if the Dashboard is already assigned to that Customer. " + - "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/{customerId}/dashboard/{dashboardId}", method = RequestMethod.POST) @ResponseBody @@ -226,8 +221,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Unassign the Dashboard (unassignDashboardFromCustomer)", notes = "Unassign the Dashboard from specified Customer or do nothing if the Dashboard is already assigned to that Customer. " + - "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/{customerId}/dashboard/{dashboardId}", method = RequestMethod.DELETE) @ResponseBody @@ -247,8 +241,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Update the Dashboard Customers (updateDashboardCustomers)", notes = "Updates the list of Customers that this Dashboard is assigned to. Removes previous assignments to customers that are not in the provided list. " + - "Returns the Dashboard object. " + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Returns the Dashboard object. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/dashboard/{dashboardId}/customers", method = RequestMethod.POST) @@ -267,8 +260,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Adds the Dashboard Customers (addDashboardCustomers)", notes = "Adds the list of Customers to the existing list of assignments for the Dashboard. Keeps previous assignments to customers that are not in the provided list. " + - "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/dashboard/{dashboardId}/customers/add", method = RequestMethod.POST) @ResponseBody @@ -286,8 +278,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Remove the Dashboard Customers (removeDashboardCustomers)", notes = "Removes the list of Customers from the existing list of assignments for the Dashboard. Keeps other assignments to customers that are not in the provided list. " + - "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/dashboard/{dashboardId}/customers/remove", method = RequestMethod.POST) @ResponseBody @@ -309,8 +300,7 @@ public class DashboardController extends BaseController { "Be aware that making the dashboard public does not mean that it automatically makes all devices and assets you use in the dashboard to be public." + "Use [assign Asset to Public Customer](#!/asset-controller/assignAssetToPublicCustomerUsingPOST) and " + "[assign Device to Public Customer](#!/device-controller/assignDeviceToPublicCustomerUsingPOST) for this purpose. " + - "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.POST) @ResponseBody @@ -325,8 +315,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Unassign the Dashboard from Public Customer (unassignDashboardFromPublicCustomer)", notes = "Unassigns the dashboard from a special, auto-generated 'Public' Customer. Once unassigned, unauthenticated users may no longer browse the dashboard. " + - "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.DELETE) @ResponseBody @@ -341,8 +330,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Get Tenant Dashboards by System Administrator (getTenantDashboards)", notes = "Returns a page of dashboard info objects owned by tenant. " + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + - SYSTEM_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") @RequestMapping(value = "/tenant/{tenantId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -367,8 +355,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Get Tenant Dashboards (getTenantDashboards)", notes = "Returns a page of dashboard info objects owned by the tenant of a current user. " - + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -396,8 +383,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Get Customer Dashboards (getCustomerDashboards)", notes = "Returns a page of dashboard info objects owned by the specified customer. " - + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/customer/{customerId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -432,8 +418,7 @@ public class DashboardController extends BaseController { notes = "Returns the home dashboard object that is configured as 'homeDashboardId' parameter in the 'additionalInfo' of the User. " + "If 'homeDashboardId' parameter is not set on the User level and the User has authority 'CUSTOMER_USER', check the same parameter for the corresponding Customer. " + "If 'homeDashboardId' parameter is not set on the User and Customer levels then checks the same parameter for the Tenant that owns the user. " - + DASHBOARD_DEFINITION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + DASHBOARD_DEFINITION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/dashboard/home", method = RequestMethod.GET) @ResponseBody @@ -465,8 +450,7 @@ public class DashboardController extends BaseController { notes = "Returns the home dashboard info object that is configured as 'homeDashboardId' parameter in the 'additionalInfo' of the User. " + "If 'homeDashboardId' parameter is not set on the User level and the User has authority 'CUSTOMER_USER', check the same parameter for the corresponding Customer. " + "If 'homeDashboardId' parameter is not set on the User and Customer levels then checks the same parameter for the Tenant that owns the user. " + - TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/dashboard/home/info", method = RequestMethod.GET) @ResponseBody @@ -496,8 +480,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Get Tenant Home Dashboard Info (getTenantHomeDashboardInfo)", notes = "Returns the home dashboard info object that is configured as 'homeDashboardId' parameter in the 'additionalInfo' of the corresponding tenant. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.GET) @ResponseBody @@ -518,8 +501,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Update Tenant Home Dashboard Info (getTenantHomeDashboardInfo)", notes = "Update the home dashboard assignment for the current tenant. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) @@ -584,8 +566,7 @@ public class DashboardController extends BaseController { "Second, remote edge service will receive a copy of assignment dashboard " + EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + "Third, once dashboard will be delivered to edge service, it's going to be available for usage on remote edge instance." + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.POST) @ResponseBody @@ -608,8 +589,7 @@ public class DashboardController extends BaseController { "Second, remote edge service will receive an 'unassign' command to remove dashboard " + EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + "Third, once 'unassign' command will be delivered to edge service, it's going to remove dashboard locally." + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.DELETE) @ResponseBody @@ -629,8 +609,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Get Edge Dashboards (getEdgeDashboards)", notes = "Returns a page of dashboard info objects assigned to the specified edge. " - + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edge/{edgeId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 555769370c..fedbd48503 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -177,7 +177,7 @@ public class DeviceController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/device", method = RequestMethod.POST) @ResponseBody - public Device saveDevice(@Parameter(description = "A JSON value representing the device.") @RequestBody Device device, + public Device saveDevice(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the device.") @RequestBody Device device, @Parameter(description = "Optional value of the device credentials to be used during device creation. " + "If omitted, access token will be auto-generated.") @RequestParam(name = "accessToken", required = false) String accessToken) throws Exception { device.setTenantId(getCurrentUser().getTenantId()); @@ -534,8 +534,7 @@ public class DeviceController extends BaseController { } @ApiOperation(value = "Get Device Types (getDeviceTypes)", - notes = "Deprecated. See 'getDeviceProfileNames' API from Device Profile Controller instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Deprecated. See 'getDeviceProfileNames' API from Device Profile Controller instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/device/types", method = RequestMethod.GET) @ResponseBody @@ -668,8 +667,7 @@ public class DeviceController extends BaseController { EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION + "Second, remote edge service will receive a copy of assignment device " + EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + - "Third, once device will be delivered to edge service, it's going to be available for usage on remote edge instance." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Third, once device will be delivered to edge service, it's going to be available for usage on remote edge instance." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.POST) @ResponseBody @@ -693,8 +691,7 @@ public class DeviceController extends BaseController { EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION + "Second, remote edge service will receive an 'unassign' command to remove device " + EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + - "Third, once 'unassign' command will be delivered to edge service, it's going to remove device locally." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Third, once 'unassign' command will be delivered to edge service, it's going to remove device locally." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.DELETE) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java index 02cf15c81d..b84e801d31 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -86,8 +86,7 @@ public class DeviceProfileController extends BaseController { @ApiOperation(value = "Get Device Profile (getDeviceProfileById)", notes = "Fetch the Device Profile object based on the provided Device Profile Id. " + - "The server checks that the device profile is owned by the same tenant. " + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "The server checks that the device profile is owned by the same tenant. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/deviceProfile/{deviceProfileId}", method = RequestMethod.GET) @ResponseBody @@ -107,8 +106,7 @@ public class DeviceProfileController extends BaseController { @ApiOperation(value = "Get Device Profile Info (getDeviceProfileInfoById)", notes = "Fetch the Device Profile Info object based on the provided Device Profile Id. " - + DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/deviceProfileInfo/{deviceProfileId}", method = RequestMethod.GET) @ResponseBody @@ -122,8 +120,7 @@ public class DeviceProfileController extends BaseController { @ApiOperation(value = "Get Default Device Profile (getDefaultDeviceProfileInfo)", notes = "Fetch the Default Device Profile Info object. " + - DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/deviceProfileInfo/default", method = RequestMethod.GET) @ResponseBody @@ -136,8 +133,7 @@ public class DeviceProfileController extends BaseController { "If profile is not set returns a list of unique keys among all profiles. " + "The call is used for auto-complete in the UI forms. " + "The implementation limits the number of devices that participate in search to 100 as a trade of between accurate results and time-consuming queries. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/deviceProfile/devices/keys/timeseries", method = RequestMethod.GET) @ResponseBody @@ -160,8 +156,7 @@ public class DeviceProfileController extends BaseController { "If profile is not set returns a list of unique keys among all profiles. " + "The call is used for auto-complete in the UI forms. " + "The implementation limits the number of devices that participate in search to 100 as a trade of between accurate results and time-consuming queries. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/deviceProfile/devices/keys/attributes", method = RequestMethod.GET) @ResponseBody @@ -186,8 +181,7 @@ public class DeviceProfileController extends BaseController { "Referencing non-existing device profile Id will cause 'Not Found' error. " + NEW_LINE + "Device profile name is unique in the scope of tenant. Only one 'default' device profile may exist in scope of tenant." + DEVICE_PROFILE_DATA + "Remove 'id', 'tenantId' from the request body example (below) to create new Device Profile entity. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/deviceProfile", method = RequestMethod.POST) @ResponseBody @@ -215,8 +209,7 @@ public class DeviceProfileController extends BaseController { } @ApiOperation(value = "Make Device Profile Default (setDefaultDeviceProfile)", - notes = "Marks device profile as default within a tenant scope." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Marks device profile as default within a tenant scope." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/deviceProfile/{deviceProfileId}/default", method = RequestMethod.POST) @ResponseBody @@ -232,8 +225,7 @@ public class DeviceProfileController extends BaseController { @ApiOperation(value = "Get Device Profiles (getDeviceProfiles)", notes = "Returns a page of devices profile objects owned by tenant. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/deviceProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -254,8 +246,7 @@ public class DeviceProfileController extends BaseController { @ApiOperation(value = "Get Device Profiles for transport type (getDeviceProfileInfos)", notes = "Returns a page of devices profile info objects owned by tenant. " + - PAGE_DATA_PARAMETERS + DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/deviceProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java index 9781d48828..3042b8f2a1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -120,8 +120,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Get Edge (getEdgeById)", - notes = "Get the Edge object based on the provided Edge Id. " + EDGE_SECURITY_CHECK + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Get the Edge object based on the provided Edge Id. " + EDGE_SECURITY_CHECK + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.GET) @ResponseBody @@ -133,8 +132,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Get Edge Info (getEdgeInfoById)", - notes = "Get the Edge Info object based on the provided Edge Id. " + EDGE_SECURITY_CHECK + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Get the Edge Info object based on the provided Edge Id. " + EDGE_SECURITY_CHECK + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edge/info/{edgeId}", method = RequestMethod.GET) @ResponseBody @@ -152,8 +150,7 @@ public class EdgeController extends BaseController { "Referencing non-existing Edge Id will cause 'Not Found' error." + "\n\nEdge name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the edge names and non-unique 'label' field for user-friendly visualization purposes." + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Edge entity. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge", method = RequestMethod.POST) @ResponseBody @@ -193,7 +190,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Get Tenant Edges (getEdges)", notes = "Returns a page of edges owned by tenant. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edges", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -213,8 +210,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Assign edge to customer (assignEdgeToCustomer)", - notes = "Creates assignment of the edge to customer. Customer will be able to query edge afterwards." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Creates assignment of the edge to customer. Customer will be able to query edge afterwards." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/{customerId}/edge/{edgeId}", method = RequestMethod.POST) @ResponseBody @@ -232,8 +228,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Unassign edge from customer (unassignEdgeFromCustomer)", - notes = "Clears assignment of the edge to customer. Customer will not be able to query edge afterwards." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Clears assignment of the edge to customer. Customer will not be able to query edge afterwards." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/edge/{edgeId}", method = RequestMethod.DELETE) @ResponseBody @@ -253,8 +248,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Make edge publicly available (assignEdgeToPublicCustomer)", notes = "Edge will be available for non-authorized (not logged-in) users. " + "This is useful to create dashboards that you plan to share/embed on a publicly available website. " + - "However, users that are logged-in and belong to different tenant will not be able to access the edge." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "However, users that are logged-in and belong to different tenant will not be able to access the edge." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/public/edge/{edgeId}", method = RequestMethod.POST) @ResponseBody @@ -268,7 +262,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Get Tenant Edges (getTenantEdges)", notes = "Returns a page of edges owned by tenant. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/edges", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -296,8 +290,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Get Tenant Edge Infos (getTenantEdgeInfos)", notes = "Returns a page of edges info objects owned by tenant. " + - PAGE_DATA_PARAMETERS + EDGE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + EDGE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -325,8 +318,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Get Tenant Edge (getTenantEdge)", notes = "Requested edge must be owned by tenant or customer that the user belongs to. " + - "Edge name is an unique property of edge. So it can be used to identify the edge." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Edge name is an unique property of edge. So it can be used to identify the edge." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/edges", params = {"edgeName"}, method = RequestMethod.GET) @ResponseBody @@ -338,8 +330,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Set root rule chain for provided edge (setEdgeRootRuleChain)", notes = "Change root rule chain of the edge to the new provided rule chain. \n" + - "This operation will send a notification to update root rule chain on remote edge service." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "This operation will send a notification to update root rule chain on remote edge service." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/{ruleChainId}/root", method = RequestMethod.POST) @ResponseBody @@ -359,7 +350,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Get Customer Edges (getCustomerEdges)", notes = "Returns a page of edges objects assigned to customer. " + - PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/customer/{customerId}/edges", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -395,7 +386,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Get Customer Edge Infos (getCustomerEdgeInfos)", notes = "Returns a page of edges info objects assigned to customer. " + - PAGE_DATA_PARAMETERS + EDGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + EDGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/customer/{customerId}/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -430,8 +421,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Get Edges By Ids (getEdgesByIds)", - notes = "Requested edges must be owned by tenant or assigned to customer which user is performing the request." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Requested edges must be owned by tenant or assigned to customer which user is performing the request." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edges", params = {"edgeIds"}, method = RequestMethod.GET) @ResponseBody @@ -459,8 +449,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Find related edges (findByQuery)", notes = "Returns all edges that are related to the specific entity. " + "The entity id, relation type, edge types, depth of the search, and other query parameters defined using complex 'EdgeSearchQuery' object. " + - "See 'Model' tab of the Parameters for more info." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "See 'Model' tab of the Parameters for more info." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edges", method = RequestMethod.POST) @ResponseBody @@ -485,8 +474,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Get Edge Types (getEdgeTypes)", notes = "Returns a set of unique edge types based on edges that are either owned by the tenant or assigned to the customer which user is performing the request." - + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edge/types", method = RequestMethod.GET) @ResponseBody @@ -542,8 +530,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Import the bulk of edges (processEdgesBulkImport)", - notes = "There's an ability to import the bulk of edges using the only .csv file." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "There's an ability to import the bulk of edges using the only .csv file." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @PostMapping("/edge/bulk_import") public BulkImportResult processEdgesBulkImport(@RequestBody BulkImportRequest request) throws Exception { @@ -557,8 +544,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Get Edge Install Instructions (getEdgeInstallInstructions)", - notes = "Get an install instructions for provided edge id." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Get an install instructions for provided edge id." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/instructions/install/{edgeId}/{method}", method = RequestMethod.GET) @ResponseBody @@ -579,8 +565,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Get Edge Upgrade Instructions (getEdgeUpgradeInstructions)", - notes = "Get an upgrade instructions for provided edge version." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Get an upgrade instructions for provided edge version." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/instructions/upgrade/{edgeVersion}/{method}", method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java index 8828a714fc..d1912ba38f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java @@ -60,7 +60,7 @@ public class EdgeEventController extends BaseController { @ApiOperation(value = "Get Edge Events (getEdgeEvents)", notes = "Returns a page of edge events for the requested edge. " + - PAGE_DATA_PARAMETERS, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/events", method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java index e6e57e8b8c..505244539b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java @@ -20,7 +20,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java index 3932e483d5..0cd31a729b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -135,8 +135,7 @@ public class EntityRelationController extends BaseController { } @ApiOperation(value = "Get Relation (getRelation)", - notes = "Returns relation object between two specified entities if present. Otherwise throws exception. " + SECURITY_CHECKS_ENTITIES_DESCRIPTION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Returns relation object between two specified entities if present. Otherwise throws exception. " + SECURITY_CHECKS_ENTITIES_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relation", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE}) @ResponseBody @@ -161,8 +160,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Get List of Relations (findByFrom)", notes = "Returns list of relation objects for the specified entity by the 'from' direction. " + - SECURITY_CHECKS_ENTITY_DESCRIPTION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SECURITY_CHECKS_ENTITY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE}) @ResponseBody @@ -180,8 +178,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Get List of Relation Infos (findInfoByFrom)", notes = "Returns list of relation info objects for the specified entity by the 'from' direction. " + - SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE}) @ResponseBody @@ -199,8 +196,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Get List of Relations (findByFrom)", notes = "Returns list of relation objects for the specified entity by the 'from' direction and relation type. " + - SECURITY_CHECKS_ENTITY_DESCRIPTION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SECURITY_CHECKS_ENTITY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE, RELATION_TYPE}) @ResponseBody @@ -220,8 +216,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Get List of Relations (findByTo)", notes = "Returns list of relation objects for the specified entity by the 'to' direction. " + - SECURITY_CHECKS_ENTITY_DESCRIPTION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SECURITY_CHECKS_ENTITY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {TO_ID, TO_TYPE}) @ResponseBody @@ -239,8 +234,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Get List of Relation Infos (findInfoByTo)", notes = "Returns list of relation info objects for the specified entity by the 'to' direction. " + - SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {TO_ID, TO_TYPE}) @ResponseBody @@ -258,8 +252,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Get List of Relations (findByTo)", notes = "Returns list of relation objects for the specified entity by the 'to' direction and relation type. " + - SECURITY_CHECKS_ENTITY_DESCRIPTION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SECURITY_CHECKS_ENTITY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {TO_ID, TO_TYPE, RELATION_TYPE}) @ResponseBody @@ -280,7 +273,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Find related entities (findByQuery)", notes = "Returns all entities that are related to the specific entity. " + "The entity id, relation type, entity types, depth of the search, and other query parameters defined using complex 'EntityRelationsQuery' object. " + - "See 'Model' tab of the Parameters for more info.", responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "See 'Model' tab of the Parameters for more info.") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.POST) @ResponseBody @@ -296,7 +289,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Find related entity infos (findInfoByQuery)", notes = "Returns all entity infos that are related to the specific entity. " + "The entity id, relation type, entity types, depth of the search, and other query parameters defined using complex 'EntityRelationsQuery' object. " + - "See 'Model' tab of the Parameters for more info. " + RELATION_INFO_DESCRIPTION, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "See 'Model' tab of the Parameters for more info. " + RELATION_INFO_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations/info", method = RequestMethod.POST) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java index 59877bd4d2..8b33aeeb15 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -97,8 +97,7 @@ public class EntityViewController extends BaseController { @ApiOperation(value = "Get entity view (getEntityViewById)", notes = "Fetch the EntityView object based on the provided entity view id. " - + ENTITY_VIEW_DESCRIPTION + MODEL_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + ENTITY_VIEW_DESCRIPTION + MODEL_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.GET) @ResponseBody @@ -111,8 +110,7 @@ public class EntityViewController extends BaseController { @ApiOperation(value = "Get Entity View info (getEntityViewInfoById)", notes = "Fetch the Entity View info object based on the provided Entity View Id. " - + ENTITY_VIEW_INFO_DESCRIPTION + MODEL_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + ENTITY_VIEW_INFO_DESCRIPTION + MODEL_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/entityView/info/{entityViewId}", method = RequestMethod.GET) @ResponseBody @@ -127,8 +125,7 @@ public class EntityViewController extends BaseController { @ApiOperation(value = "Save or update entity view (saveEntityView)", notes = ENTITY_VIEW_DESCRIPTION + MODEL_DESCRIPTION + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Entity View entity." + - TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/entityView", method = RequestMethod.POST) @ResponseBody @@ -162,8 +159,7 @@ public class EntityViewController extends BaseController { } @ApiOperation(value = "Get Entity View by name (getTenantEntityView)", - notes = "Fetch the Entity View object based on the tenant id and entity view name. " + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Fetch the Entity View object based on the tenant id and entity view name. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/entityViews", params = {"entityViewName"}, method = RequestMethod.GET) @ResponseBody @@ -399,8 +395,7 @@ public class EntityViewController extends BaseController { EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION + "Second, remote edge service will receive a copy of assignment entity view " + EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + - "Third, once entity view will be delivered to edge service, it's going to be available for usage on remote edge instance.", - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Third, once entity view will be delivered to edge service, it's going to be available for usage on remote edge instance.") @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.POST) @ResponseBody @@ -424,8 +419,7 @@ public class EntityViewController extends BaseController { EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION + "Second, remote edge service will receive an 'unassign' command to remove entity view " + EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + - "Third, once 'unassign' command will be delivered to edge service, it's going to remove entity view locally.", - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Third, once 'unassign' command will be delivered to edge service, it's going to remove entity view locally.") @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.DELETE) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/EventController.java b/application/src/main/java/org/thingsboard/server/controller/EventController.java index b1e2405ae6..372d724912 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EventController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EventController.java @@ -108,7 +108,7 @@ public class EventController extends BaseController { @ApiOperation(value = "Get Events by type (getEvents)", notes = "Returns a page of events for specified entity by specifying event type. " + - PAGE_DATA_PARAMETERS, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/events/{entityType}/{entityId}/{eventType}", method = RequestMethod.GET) @ResponseBody @@ -150,7 +150,7 @@ public class EventController extends BaseController { "The call was deprecated to improve the performance of the system. " + "Current implementation will return 'Lifecycle' events only. " + "Use 'Get events by type' or 'Get events by filter' instead. " + - PAGE_DATA_PARAMETERS, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.GET) @ResponseBody @@ -190,8 +190,7 @@ public class EventController extends BaseController { @ApiOperation(value = "Get Events by event filter (getEvents)", notes = "Returns a page of events for the chosen entity by specifying the event filter. " + PAGE_DATA_PARAMETERS + NEW_LINE + - EVENT_FILTER_DEFINITION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + EVENT_FILTER_DEFINITION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.POST) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java index f6670193d9..25e49eb198 100644 --- a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java +++ b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java @@ -59,8 +59,7 @@ public class Lwm2mController extends BaseController { @ApiOperation(value = "Get Lwm2m Bootstrap SecurityInfo (getLwm2mBootstrapSecurityInfo)", notes = "Get the Lwm2m Bootstrap SecurityInfo object (of the current server) based on the provided isBootstrapServer parameter. If isBootstrapServer == true, get the parameters of the current Bootstrap Server. If isBootstrapServer == false, get the parameters of the current Lwm2m Server. Used for client settings when starting the client in Bootstrap mode. " + - TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/lwm2m/deviceProfile/bootstrap/{isBootstrapServer}", method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java index 4ceb39534f..f2b77c3b7b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java @@ -105,8 +105,7 @@ public class OtaPackageController extends BaseController { @ApiOperation(value = "Get OTA Package Info (getOtaPackageInfoById)", notes = "Fetch the OTA Package Info object based on the provided OTA Package Id. " + - OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE))) + OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/otaPackage/info/{otaPackageId}", method = RequestMethod.GET) @ResponseBody @@ -119,8 +118,7 @@ public class OtaPackageController extends BaseController { @ApiOperation(value = "Get OTA Package (getOtaPackageById)", notes = "Fetch the OTA Package object based on the provided OTA Package Id. " + - "The server checks that the OTA Package is owned by the same tenant. " + OTA_PACKAGE_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE))) + "The server checks that the OTA Package is owned by the same tenant. " + OTA_PACKAGE_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.GET) @ResponseBody @@ -136,8 +134,7 @@ public class OtaPackageController extends BaseController { "The newly created OTA Package id will be present in the response. " + "Specify existing OTA Package id to update the OTA Package Info. " + "Referencing non-existing OTA Package Id will cause 'Not Found' error. " + - "\n\nOTA Package combination of the title with the version is unique in the scope of tenant. " + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE))) + "\n\nOTA Package combination of the title with the version is unique in the scope of tenant. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/otaPackage", method = RequestMethod.POST) @ResponseBody @@ -151,7 +148,6 @@ public class OtaPackageController extends BaseController { @ApiOperation(value = "Save OTA Package data (saveOtaPackageData)", notes = "Update the OTA Package. Adds the date to the existing OTA Package Info" + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE)), requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(content = @Content(mediaType = MULTIPART_FORM_DATA_VALUE))) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.POST, consumes = MULTIPART_FORM_DATA_VALUE) @@ -176,8 +172,7 @@ public class OtaPackageController extends BaseController { @ApiOperation(value = "Get OTA Package Infos (getOtaPackages)", notes = "Returns a page of OTA Package Info objects owned by tenant. " + - PAGE_DATA_PARAMETERS + OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/otaPackages", method = RequestMethod.GET) @ResponseBody @@ -197,8 +192,7 @@ public class OtaPackageController extends BaseController { @ApiOperation(value = "Get OTA Package Infos (getOtaPackages)", notes = "Returns a page of OTA Package Info objects owned by tenant. " + - PAGE_DATA_PARAMETERS + OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/otaPackages/{deviceProfileId}/{type}", method = RequestMethod.GET) @ResponseBody @@ -225,8 +219,7 @@ public class OtaPackageController extends BaseController { @ApiOperation(value = "Delete OTA Package (deleteOtaPackage)", notes = "Deletes the OTA Package. Referencing non-existing OTA Package Id will cause an error. " + - "Can't delete the OTA Package if it is referenced by existing devices or device profile." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE))) + "Can't delete the OTA Package if it is referenced by existing devices or device profile." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.DELETE) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index e7c6e138ca..8d51daab8b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -371,7 +371,7 @@ public class RuleChainController extends BaseController { public JsonNode testScript( @Parameter(description = "Script language: JS or TBEL") @RequestParam(required = false) ScriptLanguage scriptLang, - @Parameter(description = "Test JS request. See API call description above.") + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Test JS request. See API call description above.") @RequestBody JsonNode inputParams) throws ThingsboardException, JsonProcessingException { String script = inputParams.get("script").asText(); String scriptType = inputParams.get("scriptType").asText(); @@ -499,8 +499,7 @@ public class RuleChainController extends BaseController { "Second, remote edge service will receive a copy of assignment rule chain " + EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + "Third, once rule chain will be delivered to edge service, it's going to start processing messages locally. " + - "\n\nOnly rule chain with type 'EDGE' can be assigned to edge." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "\n\nOnly rule chain with type 'EDGE' can be assigned to edge." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.POST) @ResponseBody @@ -522,8 +521,7 @@ public class RuleChainController extends BaseController { EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION + "Second, remote edge service will receive an 'unassign' command to remove rule chain " + EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + - "Third, once 'unassign' command will be delivered to edge service, it's going to remove rule chain locally." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Third, once 'unassign' command will be delivered to edge service, it's going to remove rule chain locally." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.DELETE) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index bf59f78519..1908459200 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -146,8 +146,7 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Get Resource Info (getResourceInfoById)", notes = "Fetch the Resource Info object based on the provided Resource Id. " + - RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/resource/info/{resourceId}") public TbResourceInfo getResourceInfoById(@Parameter(description = RESOURCE_ID_PARAM_DESCRIPTION) @@ -159,8 +158,7 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Get Resource (getResourceById)", notes = "Fetch the Resource object based on the provided Resource Id. " + - RESOURCE_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)), hidden = true) + RESOURCE_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, hidden = true) @Deprecated // resource's data should be fetched with a download request @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/resource/{resourceId}") @@ -178,8 +176,7 @@ public class TbResourceController extends BaseController { "Referencing non-existing Resource Id will cause 'Not Found' error. " + "\n\nResource combination of the title with the key is unique in the scope of tenant. " + "Remove 'id', 'tenantId' from the request body example (below) to create new Resource entity." + - SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PostMapping(value = "/resource") public TbResourceInfo saveResource(@Parameter(description = "A JSON value representing the Resource.") @@ -191,8 +188,7 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Get Resource Infos (getResources)", notes = "Returns a page of Resource Info objects owned by tenant or sysadmin. " + - PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/resource") public PageData getResources(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @@ -227,8 +223,7 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Get All Resource Infos (getAllResources)", notes = "Returns a page of Resource Info objects owned by tenant. " + - PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @GetMapping(value = "/resource/tenant") public PageData getTenantResources(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @@ -251,8 +246,7 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Get LwM2M Objects (getLwm2mListObjectsPage)", notes = "Returns a page of LwM2M objects parsed from Resources with type 'LWM2M_MODEL' owned by tenant or sysadmin. " + - PAGE_DATA_PARAMETERS + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @GetMapping(value = "/resource/lwm2m/page") public List getLwm2mListObjectsPage(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @@ -271,8 +265,7 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Get LwM2M Objects (getLwm2mListObjects)", notes = "Returns a page of LwM2M objects parsed from Resources with type 'LWM2M_MODEL' owned by tenant or sysadmin. " + - "You can specify parameters to filter the results. " + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "You can specify parameters to filter the results. " + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @GetMapping(value = "/resource/lwm2m") public List getLwm2mListObjects(@Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}, required = true)) diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java index 9ca600603c..c73ac09cf1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -177,8 +177,7 @@ public class TelemetryController extends BaseController { "\n\n * SERVER_SCOPE - supported for all entity types;" + "\n * CLIENT_SCOPE - supported for devices;" + "\n * SHARED_SCOPE - supported for devices. " - + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/keys/attributes", method = RequestMethod.GET) @ResponseBody @@ -193,8 +192,7 @@ public class TelemetryController extends BaseController { "\n\n * SERVER_SCOPE - supported for all entity types;" + "\n * CLIENT_SCOPE - supported for devices;" + "\n * SHARED_SCOPE - supported for devices. " - + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/keys/attributes/{scope}", method = RequestMethod.GET) @ResponseBody @@ -212,8 +210,7 @@ public class TelemetryController extends BaseController { + MARKDOWN_CODE_BLOCK_START + ATTRIBUTE_DATA_EXAMPLE + MARKDOWN_CODE_BLOCK_END - + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/values/attributes", method = RequestMethod.GET) @ResponseBody @@ -235,8 +232,7 @@ public class TelemetryController extends BaseController { + MARKDOWN_CODE_BLOCK_START + ATTRIBUTE_DATA_EXAMPLE + MARKDOWN_CODE_BLOCK_END - + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/values/attributes/{scope}", method = RequestMethod.GET) @ResponseBody @@ -252,8 +248,7 @@ public class TelemetryController extends BaseController { @ApiOperation(value = "Get time-series keys (getTimeseriesKeys)", notes = "Returns a set of unique time-series key names for the selected entity. " + - "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/keys/timeseries", method = RequestMethod.GET) @ResponseBody @@ -275,8 +270,7 @@ public class TelemetryController extends BaseController { + MARKDOWN_CODE_BLOCK_START + LATEST_TS_STRICT_DATA_EXAMPLE + MARKDOWN_CODE_BLOCK_END - + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET) @ResponseBody @@ -299,8 +293,7 @@ public class TelemetryController extends BaseController { + MARKDOWN_CODE_BLOCK_START + TS_STRICT_DATA_EXAMPLE + MARKDOWN_CODE_BLOCK_END - + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET, params = {"keys", "startTs", "endTs"}) @ResponseBody @@ -348,8 +341,7 @@ public class TelemetryController extends BaseController { @ApiOperation(value = "Save device attributes (saveDeviceAttributes)", notes = "Creates or updates the device attributes based on device id and specified attribute scope. " + SAVE_ATTRIBUTES_REQUEST_PAYLOAD - + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = SAVE_ATTIRIBUTES_STATUS_OK + "Platform creates an audit log event about device attributes updates with action type 'ATTRIBUTES_UPDATED', " + @@ -365,7 +357,7 @@ public class TelemetryController extends BaseController { public DeferredResult saveDeviceAttributes( @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("deviceId") String deviceIdStr, @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, required = true)) @PathVariable("scope") AttributeScope scope, - @Parameter(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); return saveAttributes(getTenantId(), entityId, scope, request); } @@ -374,8 +366,7 @@ public class TelemetryController extends BaseController { notes = "Creates or updates the entity attributes based on Entity Id and the specified attribute scope. " + ENTITY_SAVE_ATTRIBUTE_SCOPES + SAVE_ATTRIBUTES_REQUEST_PAYLOAD - + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = SAVE_ATTIRIBUTES_STATUS_OK + SAVE_ENTITY_ATTRIBUTES_STATUS_OK), @ApiResponse(responseCode = "400", description = SAVE_ATTIRIBUTES_STATUS_BAD_REQUEST), @@ -389,7 +380,7 @@ public class TelemetryController extends BaseController { @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"})) @PathVariable("scope")AttributeScope scope, - @Parameter(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody JsonNode request) throws ThingsboardException { + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return saveAttributes(getTenantId(), entityId, scope, request); } @@ -398,8 +389,7 @@ public class TelemetryController extends BaseController { notes = "Creates or updates the entity attributes based on Entity Id and the specified attribute scope. " + ENTITY_SAVE_ATTRIBUTE_SCOPES + SAVE_ATTRIBUTES_REQUEST_PAYLOAD - + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = SAVE_ATTIRIBUTES_STATUS_OK + SAVE_ENTITY_ATTRIBUTES_STATUS_OK), @ApiResponse(responseCode = "400", description = SAVE_ATTIRIBUTES_STATUS_BAD_REQUEST), @@ -413,7 +403,7 @@ public class TelemetryController extends BaseController { @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, required = true)) @PathVariable("scope")AttributeScope scope, - @Parameter(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody JsonNode request) throws ThingsboardException { + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return saveAttributes(getTenantId(), entityId, scope, request); } @@ -423,8 +413,7 @@ public class TelemetryController extends BaseController { notes = "Creates or updates the entity time-series data based on the Entity Id and request payload." + SAVE_TIMESERIES_REQUEST_PAYLOAD + "\n\n The scope parameter is not used in the API call implementation but should be specified whatever value because it is used as a path variable. " - + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = SAVE_ENTITY_TIMESERIES_STATUS_OK), @ApiResponse(responseCode = "400", description = INVALID_STRUCTURE_OF_THE_REQUEST), @@ -438,7 +427,7 @@ public class TelemetryController extends BaseController { @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")String scope, - @Parameter(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException { + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return saveTelemetry(getTenantId(), entityId, requestBody, 0L); } @@ -448,8 +437,7 @@ public class TelemetryController extends BaseController { SAVE_TIMESERIES_REQUEST_PAYLOAD + "\n\n The scope parameter is not used in the API call implementation but should be specified whatever value because it is used as a path variable. " + "\n\nThe ttl parameter takes affect only in case of Cassandra DB." - + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = SAVE_ENTITY_TIMESERIES_STATUS_OK), @ApiResponse(responseCode = "400", description = INVALID_STRUCTURE_OF_THE_REQUEST), @@ -464,7 +452,7 @@ public class TelemetryController extends BaseController { @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")AttributeScope scope, @Parameter(description = "A long value representing TTL (Time to Live) parameter.", required = true)@PathVariable("ttl")Long ttl, - @Parameter(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException { + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return saveTelemetry(getTenantId(), entityId, requestBody, ttl); } @@ -476,8 +464,7 @@ public class TelemetryController extends BaseController { " Use 'deleteLatest' to delete latest value (stored in separate table for performance) if the value's timestamp matches the time-range. " + " Use 'rewriteLatestIfDeleted' to rewrite latest value (stored in separate table for performance) if the value's timestamp matches the time-range and 'deleteLatest' param is true." + " The replacement value will be fetched from the 'time-series' table, and its timestamp will be the most recent one before the defined time-range. " + - TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Timeseries for the selected keys in the request was removed. " + "Platform creates an audit log event about entity timeseries removal with action type 'TIMESERIES_DELETED'."), @@ -552,8 +539,7 @@ public class TelemetryController extends BaseController { @ApiOperation(value = "Delete device attributes (deleteDeviceAttributes)", notes = "Delete device attributes using provided Device Id, scope and a list of keys. " + - "Referencing a non-existing Device Id will cause an error" + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Referencing a non-existing Device Id will cause an error" + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Device attributes was removed for the selected keys in the request. " + "Platform creates an audit log event about device attributes removal with action type 'ATTRIBUTES_DELETED'."), @@ -575,8 +561,7 @@ public class TelemetryController extends BaseController { @ApiOperation(value = "Delete entity attributes (deleteEntityAttributes)", notes = "Delete entity attributes using provided Entity Id, scope and a list of keys. " + - INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Entity attributes was removed for the selected keys in the request. " + "Platform creates an audit log event about entity attributes removal with action type 'ATTRIBUTES_DELETED'."), diff --git a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java index db051ca0da..447c993e55 100644 --- a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java +++ b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java @@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasName; @@ -127,20 +128,20 @@ public class EntityActionService { } else { entityNode = JacksonUtil.newObjectNode(); if (actionType == ActionType.ATTRIBUTES_UPDATED) { - String scope = extractParameter(String.class, 0, additionalInfo); + AttributeScope scope = extractParameter(AttributeScope.class, 0, additionalInfo); @SuppressWarnings("unchecked") List attributes = extractParameter(List.class, 1, additionalInfo); - metaData.putValue(DataConstants.SCOPE, scope); + metaData.putValue(DataConstants.SCOPE, scope.name()); if (attributes != null) { for (AttributeKvEntry attr : attributes) { JacksonUtil.addKvEntry(entityNode, attr); } } } else if (actionType == ActionType.ATTRIBUTES_DELETED) { - String scope = extractParameter(String.class, 0, additionalInfo); + AttributeScope scope = extractParameter(AttributeScope.class, 0, additionalInfo); @SuppressWarnings("unchecked") List keys = extractParameter(List.class, 1, additionalInfo); - metaData.putValue(DataConstants.SCOPE, scope); + metaData.putValue(DataConstants.SCOPE, scope.name()); ArrayNode attrsArrayNode = entityNode.putArray("attributes"); if (keys != null) { keys.forEach(attrsArrayNode::add); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java index eede4a3afc..4d1fb8a12a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java @@ -42,6 +42,7 @@ import org.thingsboard.server.service.edge.rpc.processor.device.DeviceEdgeProces import org.thingsboard.server.service.edge.rpc.processor.device.profile.DeviceProfileEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.edge.EdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.oauth2.OAuth2EdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.ota.OtaPackageEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.queue.QueueEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessor; @@ -126,6 +127,9 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @Autowired private ResourceEdgeProcessor resourceEdgeProcessor; + @Autowired + private OAuth2EdgeProcessor oAuth2EdgeProcessor; + @Autowired protected ApplicationEventPublisher eventPublisher; @@ -231,6 +235,9 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { case TB_RESOURCE: resourceEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); break; + case OAUTH2: + oAuth2EdgeProcessor.processOAuth2Notification(tenantId, edgeNotificationMsg); + break; default: log.warn("[{}] Edge event type [{}] is not designed to be pushed to edge", tenantId, type); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java index f5b769e568..5ef8819df0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java @@ -32,6 +32,7 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.resource.ResourceService; @@ -61,6 +62,7 @@ import org.thingsboard.server.service.edge.rpc.processor.device.profile.DevicePr import org.thingsboard.server.service.edge.rpc.processor.edge.EdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorFactory; +import org.thingsboard.server.service.edge.rpc.processor.oauth2.OAuth2EdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.ota.OtaPackageEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.queue.QueueEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessor; @@ -151,6 +153,9 @@ public class EdgeContextComponent { @Autowired private ResourceService resourceService; + @Autowired + private OAuth2Service oAuth2Service; + @Autowired private RateLimitService rateLimitService; @@ -220,6 +225,9 @@ public class EdgeContextComponent { @Autowired private ResourceEdgeProcessor resourceEdgeProcessor; + @Autowired + private OAuth2EdgeProcessor oAuth2EdgeProcessor; + @Autowired private EdgeMsgConstructor edgeMsgConstructor; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java index e54adb05fa..d975217e1f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChain; @@ -159,43 +160,41 @@ public class EdgeEventSourcingListener { private boolean isValidSaveEntityEventForEdgeProcessing(SaveEntityEvent event) { Object entity = event.getEntity(); Object oldEntity = event.getOldEntity(); - switch (event.getEntityId().getEntityType()) { - case RULE_CHAIN: - if (entity instanceof RuleChain) { - RuleChain ruleChain = (RuleChain) entity; - return RuleChainType.EDGE.equals(ruleChain.getType()); - } - break; - case USER: - if (entity instanceof User) { - User user = (User) entity; - if (Authority.SYS_ADMIN.equals(user.getAuthority())) { - return false; + if (event.getEntityId() != null) { + switch (event.getEntityId().getEntityType()) { + case RULE_CHAIN: + if (entity instanceof RuleChain ruleChain) { + return RuleChainType.EDGE.equals(ruleChain.getType()); + } + break; + case USER: + if (entity instanceof User user) { + if (Authority.SYS_ADMIN.equals(user.getAuthority())) { + return false; + } + if (oldEntity != null) { + User oldUser = (User) oldEntity; + cleanUpUserAdditionalInfo(oldUser); + cleanUpUserAdditionalInfo(user); + return !user.equals(oldUser); + } } - if (oldEntity != null) { - User oldUser = (User) oldEntity; - cleanUpUserAdditionalInfo(oldUser); - cleanUpUserAdditionalInfo(user); - return !user.equals(oldUser); + break; + case OTA_PACKAGE: + if (entity instanceof OtaPackageInfo otaPackageInfo) { + return otaPackageInfo.hasUrl() || otaPackageInfo.isHasData(); + } + break; + case ALARM: + if (entity instanceof AlarmApiCallResult || entity instanceof Alarm) { + return false; } - } - break; - case OTA_PACKAGE: - if (entity instanceof OtaPackageInfo) { - OtaPackageInfo otaPackageInfo = (OtaPackageInfo) entity; - return otaPackageInfo.hasUrl() || otaPackageInfo.isHasData(); - } - break; - case ALARM: - if (entity instanceof AlarmApiCallResult || entity instanceof Alarm) { + break; + case TENANT: + return !event.getCreated(); + case API_USAGE_STATE, EDGE: return false; - } - break; - case TENANT: - return !event.getCreated(); - case API_USAGE_STATE: - case EDGE: - return false; + } } // Default: If the entity doesn't match any of the conditions, consider it as valid. return true; @@ -206,8 +205,7 @@ public class EdgeEventSourcingListener { if (user.getAdditionalInfo() instanceof NullNode) { user.setAdditionalInfo(null); } - if (user.getAdditionalInfo() instanceof ObjectNode) { - ObjectNode additionalInfo = ((ObjectNode) user.getAdditionalInfo()); + if (user.getAdditionalInfo() instanceof ObjectNode additionalInfo) { additionalInfo.remove(UserServiceImpl.FAILED_LOGIN_ATTEMPTS); additionalInfo.remove(UserServiceImpl.LAST_LOGIN_TS); if (additionalInfo.isEmpty()) { @@ -221,6 +219,8 @@ public class EdgeEventSourcingListener { private EdgeEventType getEdgeEventTypeForEntityEvent(Object entity) { if (entity instanceof AlarmComment) { return EdgeEventType.ALARM_COMMENT; + } else if (entity instanceof OAuth2Info) { + return EdgeEventType.OAUTH2; } return null; } @@ -228,6 +228,8 @@ public class EdgeEventSourcingListener { private String getBodyMsgForEntityEvent(Object entity) { if (entity instanceof AlarmComment) { return JacksonUtil.toString(entity); + } else if (entity instanceof OAuth2Info) { + return JacksonUtil.toString(entity); } return null; } @@ -238,4 +240,5 @@ public class EdgeEventSourcingListener { } return isCreated ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED; } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index a73d3a20ff..75bb8e10e9 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -498,9 +498,9 @@ public final class EdgeGrpcSession implements Closeable { log.trace("[{}][{}][{}] downlink msg(s) are going to be send.", this.tenantId, this.sessionId, copy.size()); for (DownlinkMsg downlinkMsg : copy) { if (this.clientMaxInboundMessageSize != 0 && downlinkMsg.getSerializedSize() > this.clientMaxInboundMessageSize) { - String error = String.format("Client max inbound message size [{%s}] is exceeded. Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE " + + String error = String.format("Client max inbound message size %s is exceeded. Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE " + "env variable on the edge and restart it.", this.clientMaxInboundMessageSize); - String message = String.format("Downlink msg size [{%s}] exceeds client max inbound message size [{%s}]. " + + String message = String.format("Downlink msg size %s exceeds client max inbound message size %s. " + "Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE env variable on the edge and restart it.", downlinkMsg.getSerializedSize(), this.clientMaxInboundMessageSize); log.error("[{}][{}][{}] {} Message {}", this.tenantId, edge.getId(), this.sessionId, message, downlinkMsg); ctx.getNotificationRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId) @@ -551,35 +551,14 @@ public final class EdgeGrpcSession implements Closeable { DownlinkMsg downlinkMsg = null; try { switch (edgeEvent.getAction()) { - case UPDATED: - case ADDED: - case DELETED: - case ASSIGNED_TO_EDGE: - case UNASSIGNED_FROM_EDGE: - case ALARM_ACK: - case ALARM_CLEAR: - case ALARM_DELETE: - case CREDENTIALS_UPDATED: - case RELATION_ADD_OR_UPDATE: - case RELATION_DELETED: - case CREDENTIALS_REQUEST: - case RPC_CALL: - case ASSIGNED_TO_CUSTOMER: - case UNASSIGNED_FROM_CUSTOMER: - case ADDED_COMMENT: - case UPDATED_COMMENT: - case DELETED_COMMENT: + case UPDATED, ADDED, DELETED, ASSIGNED_TO_EDGE, UNASSIGNED_FROM_EDGE, ALARM_ACK, ALARM_CLEAR, ALARM_DELETE, CREDENTIALS_UPDATED, RELATION_ADD_OR_UPDATE, RELATION_DELETED, CREDENTIALS_REQUEST, RPC_CALL, ASSIGNED_TO_CUSTOMER, UNASSIGNED_FROM_CUSTOMER, ADDED_COMMENT, UPDATED_COMMENT, DELETED_COMMENT -> { downlinkMsg = convertEntityEventToDownlink(edgeEvent); log.trace("[{}][{}] entity message processed [{}]", this.tenantId, this.sessionId, downlinkMsg); - break; - case ATTRIBUTES_UPDATED: - case POST_ATTRIBUTES: - case ATTRIBUTES_DELETED: - case TIMESERIES_UPDATED: - downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edgeEvent); - break; - default: - log.warn("[{}][{}] Unsupported action type [{}]", this.tenantId, this.sessionId, edgeEvent.getAction()); + } + case ATTRIBUTES_UPDATED, POST_ATTRIBUTES, ATTRIBUTES_DELETED, TIMESERIES_UPDATED -> + downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edge, edgeEvent); + default -> + log.warn("[{}][{}] Unsupported action type [{}]", this.tenantId, this.sessionId, edgeEvent.getAction()); } } catch (Exception e) { log.error("[{}][{}] Exception during converting edge event to downlink msg", this.tenantId, this.sessionId, e); @@ -705,6 +684,8 @@ public final class EdgeGrpcSession implements Closeable { return ctx.getTenantEdgeProcessor().convertTenantEventToDownlink(edgeEvent, this.edgeVersion); case TENANT_PROFILE: return ctx.getTenantProfileEdgeProcessor().convertTenantProfileEventToDownlink(edgeEvent, this.edgeVersion); + case OAUTH2: + return ctx.getOAuth2EdgeProcessor().convertOAuth2EventToDownlink(edgeEvent); default: log.warn("[{}] Unsupported edge event type [{}]", this.tenantId, edgeEvent); return null; @@ -855,7 +836,7 @@ public final class EdgeGrpcSession implements Closeable { .build(); } String error = "Failed to validate the edge!"; - String failureMsg = String.format("{%s} Provided request secret: %s", error, request.getEdgeSecret()); + String failureMsg = String.format("%s Provided request secret: %s", error, request.getEdgeSecret()); ctx.getNotificationRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) .customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(failureMsg).error(error).build()); return ConnectResponseMsg.newBuilder() diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java index 8583e8725e..5f7ffc08e3 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java @@ -30,6 +30,7 @@ import org.thingsboard.server.service.edge.rpc.fetch.DeviceProfilesEdgeEventFetc import org.thingsboard.server.service.edge.rpc.fetch.DevicesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.EdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.EntityViewsEdgeEventFetcher; +import org.thingsboard.server.service.edge.rpc.fetch.OAuth2EdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.OtaPackagesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.QueuesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.RuleChainsEdgeEventFetcher; @@ -79,6 +80,7 @@ public class EdgeSyncCursor { fetchers.add(new TenantWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService())); fetchers.add(new OtaPackagesEdgeEventFetcher(ctx.getOtaPackageService())); fetchers.add(new TenantResourcesEdgeEventFetcher(ctx.getResourceService())); + fetchers.add(new OAuth2EdgeEventFetcher(ctx.getOAuth2Service())); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/oauth2/OAuth2MsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/oauth2/OAuth2MsgConstructor.java new file mode 100644 index 0000000000..c628b042a2 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/oauth2/OAuth2MsgConstructor.java @@ -0,0 +1,32 @@ +/** + * 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.service.edge.rpc.constructor.oauth2; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class OAuth2MsgConstructor { + + public OAuth2UpdateMsg constructOAuth2UpdateMsg(OAuth2Info oAuth2Info) { + return OAuth2UpdateMsg.newBuilder().setEntity(JacksonUtil.toString(oAuth2Info)).build(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java index a7372ddfaf..fc244417bc 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java @@ -73,6 +73,24 @@ public class TenantMsgConstructorV1 implements TenantMsgConstructor { @Override public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { + tenantProfile = JacksonUtil.clone(tenantProfile); + // clear all config + var tenantProfileData = tenantProfile.getProfileData(); + var configuration = tenantProfile.getDefaultProfileConfiguration(); + configuration.setRpcTtlDays(0); + configuration.setMaxJSExecutions(0); + configuration.setMaxREExecutions(0); + configuration.setMaxDPStorageDays(0); + configuration.setMaxTbelExecutions(0); + configuration.setQueueStatsTtlDays(0); + configuration.setMaxTransportMessages(0); + configuration.setDefaultStorageTtlDays(0); + configuration.setMaxTransportDataPoints(0); + configuration.setRuleEngineExceptionsTtlDays(0); + configuration.setMaxRuleNodeExecutionsPerMessage(0); + tenantProfileData.setConfiguration(configuration); + tenantProfile.setProfileData(tenantProfileData); + ByteString profileData = EdgeVersionUtils.isEdgeVersionOlderThan(edgeVersion, EdgeVersion.V_3_6_2) ? ByteString.empty() : ByteString.copyFrom(tenantProfile.getProfileDataBytes()); TenantProfileUpdateMsg.Builder builder = TenantProfileUpdateMsg.newBuilder() @@ -88,4 +106,5 @@ public class TenantMsgConstructorV1 implements TenantMsgConstructor { } return builder.build(); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java index c6ccb439ea..274e18e7f3 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java @@ -19,6 +19,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; @@ -36,6 +37,23 @@ public class TenantMsgConstructorV2 implements TenantMsgConstructor { @Override public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { + tenantProfile = JacksonUtil.clone(tenantProfile); + // clear all config + var configuration = tenantProfile.getDefaultProfileConfiguration(); + configuration.setRpcTtlDays(0); + configuration.setMaxJSExecutions(0); + configuration.setMaxREExecutions(0); + configuration.setMaxDPStorageDays(0); + configuration.setMaxTbelExecutions(0); + configuration.setQueueStatsTtlDays(0); + configuration.setMaxTransportMessages(0); + configuration.setDefaultStorageTtlDays(0); + configuration.setMaxTransportDataPoints(0); + configuration.setRuleEngineExceptionsTtlDays(0); + configuration.setMaxRuleNodeExecutionsPerMessage(0); + tenantProfile.getProfileData().setConfiguration(configuration); + return TenantProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenantProfile)).build(); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/CustomerEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/CustomerEdgeEventFetcher.java index 57596a76de..ff34e618bc 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/CustomerEdgeEventFetcher.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/CustomerEdgeEventFetcher.java @@ -46,7 +46,7 @@ public class CustomerEdgeEventFetcher implements EdgeEventFetcher { List result = new ArrayList<>(); result.add(EdgeUtils.constructEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, customerId, null)); - // @voba - returns PageData object to be in sync with other fetchers + // returns PageData object to be in sync with other fetchers return new PageData<>(result, 1, result.size(), false); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/OAuth2EdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/OAuth2EdgeEventFetcher.java new file mode 100644 index 0000000000..4412f91183 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/OAuth2EdgeEventFetcher.java @@ -0,0 +1,56 @@ +/** + * 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.service.edge.rpc.fetch; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.oauth2.OAuth2Service; + +import java.util.ArrayList; +import java.util.List; + +@AllArgsConstructor +@Slf4j +public class OAuth2EdgeEventFetcher implements EdgeEventFetcher { + + private final OAuth2Service oAuth2Service; + + @Override + public PageLink getPageLink(int pageSize) { + return null; + } + + @Override + public PageData fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) { + List result = new ArrayList<>(); + OAuth2Info oAuth2Info = oAuth2Service.findOAuth2Info(); + result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.OAUTH2, + EdgeEventActionType.ADDED, null, JacksonUtil.valueToTree(oAuth2Info))); + // returns PageData object to be in sync with other fetchers + return new PageData<>(result, 1, result.size(), false); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java index cd3eea4bef..db0bbd8aa5 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java @@ -25,7 +25,6 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Dashboard; -import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EdgeUtils; @@ -51,9 +50,8 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.msg.TbMsgType; -import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.page.PageDataIterableByTenantIdEntityId; -import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChain; @@ -75,6 +73,7 @@ import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.edge.EdgeSynchronizationManager; import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; @@ -93,7 +92,6 @@ import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueMsgMetadata; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; -import org.thingsboard.server.service.entitiy.TbLogEntityActionService; import org.thingsboard.server.service.edge.rpc.constructor.alarm.AlarmMsgConstructorFactory; import org.thingsboard.server.service.edge.rpc.constructor.asset.AssetMsgConstructorFactory; import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructorFactory; @@ -101,6 +99,7 @@ import org.thingsboard.server.service.edge.rpc.constructor.dashboard.DashboardMs import org.thingsboard.server.service.edge.rpc.constructor.device.DeviceMsgConstructorFactory; import org.thingsboard.server.service.edge.rpc.constructor.edge.EdgeMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.oauth2.OAuth2MsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorFactory; import org.thingsboard.server.service.edge.rpc.constructor.queue.QueueMsgConstructorFactory; import org.thingsboard.server.service.edge.rpc.constructor.relation.RelationMsgConstructorFactory; @@ -114,6 +113,7 @@ import org.thingsboard.server.service.edge.rpc.constructor.widget.WidgetMsgConst import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessorFactory; import org.thingsboard.server.service.edge.rpc.processor.asset.AssetEdgeProcessorFactory; import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorFactory; +import org.thingsboard.server.service.entitiy.TbLogEntityActionService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.profile.TbAssetProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache; @@ -226,6 +226,9 @@ public abstract class BaseEdgeProcessor { @Autowired protected ResourceService resourceService; + @Autowired + protected OAuth2Service oAuth2Service; + @Autowired @Lazy protected TbQueueProducerProvider producerProvider; @@ -257,6 +260,9 @@ public abstract class BaseEdgeProcessor { @Autowired protected EntityDataMsgConstructor entityDataMsgConstructor; + @Autowired + protected OAuth2MsgConstructor oAuth2MsgConstructor; + @Autowired protected RuleChainMsgConstructorFactory ruleChainMsgConstructorFactory; @@ -349,35 +355,15 @@ public abstract class BaseEdgeProcessor { private boolean doSaveIfEdgeIsOffline(EdgeEventType type, EdgeEventActionType action) { - switch (action) { - case TIMESERIES_UPDATED: - case ALARM_ACK: - case ALARM_CLEAR: - case ALARM_ASSIGNED: - case ALARM_UNASSIGNED: - case CREDENTIALS_REQUEST: - case ADDED_COMMENT: - case UPDATED_COMMENT: - return true; - } - switch (type) { - case ALARM: - case ALARM_COMMENT: - case RULE_CHAIN: - case RULE_CHAIN_METADATA: - case USER: - case CUSTOMER: - case TENANT: - case TENANT_PROFILE: - case WIDGETS_BUNDLE: - case WIDGET_TYPE: - case ADMIN_SETTINGS: - case OTA_PACKAGE: - case QUEUE: - case RELATION: - return true; - } - return false; + return switch (action) { + case TIMESERIES_UPDATED, ALARM_ACK, ALARM_CLEAR, ALARM_ASSIGNED, ALARM_UNASSIGNED, CREDENTIALS_REQUEST, ADDED_COMMENT, UPDATED_COMMENT -> + true; + default -> switch (type) { + case ALARM, ALARM_COMMENT, RULE_CHAIN, RULE_CHAIN_METADATA, USER, CUSTOMER, TENANT, TENANT_PROFILE, WIDGETS_BUNDLE, WIDGET_TYPE, ADMIN_SETTINGS, OTA_PACKAGE, QUEUE, RELATION -> + true; + default -> false; + }; + }; } private ListenableFuture doSaveEdgeEvent(TenantId tenantId, EdgeId edgeId, EdgeEventType type, EdgeEventActionType action, EntityId entityId, JsonNode body) { @@ -392,18 +378,13 @@ public abstract class BaseEdgeProcessor { protected ListenableFuture processActionForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId, - EdgeId sourceEdgeId) { + JsonNode body, EdgeId sourceEdgeId) { List> futures = new ArrayList<>(); if (TenantId.SYS_TENANT_ID.equals(tenantId)) { - PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); - PageData tenantsIds; - do { - tenantsIds = tenantService.findTenantsIds(pageLink); - for (TenantId tenantId1 : tenantsIds.getData()) { - futures.addAll(processActionForAllEdgesByTenantId(tenantId1, type, actionType, entityId, null, sourceEdgeId)); - } - pageLink = pageLink.nextPageLink(); - } while (tenantsIds.hasNext()); + PageDataIterable tenantIds = new PageDataIterable<>(link -> tenantService.findTenantsIds(link), 1024); + for (TenantId tenantId1 : tenantIds) { + futures.addAll(processActionForAllEdgesByTenantId(tenantId1, type, actionType, entityId, body, sourceEdgeId)); + } } else { futures = processActionForAllEdgesByTenantId(tenantId, type, actionType, entityId, null, sourceEdgeId); } @@ -416,22 +397,13 @@ public abstract class BaseEdgeProcessor { EntityId entityId, JsonNode body, EdgeId sourceEdgeId) { - PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); - PageData pageData; List> futures = new ArrayList<>(); - do { - pageData = edgeService.findEdgesByTenantId(tenantId, pageLink); - if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { - for (Edge edge : pageData.getData()) { - if (!edge.getId().equals(sourceEdgeId)) { - futures.add(saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, body)); - } - } - if (pageData.hasNext()) { - pageLink = pageLink.nextPageLink(); - } + PageDataIterable edges = new PageDataIterable<>(link -> edgeService.findEdgesByTenantId(tenantId, link), 1024); + for (Edge edge : edges) { + if (!edge.getId().equals(sourceEdgeId)) { + futures.add(saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, body)); } - } while (pageData != null && pageData.hasNext()); + } return futures; } @@ -442,31 +414,17 @@ public abstract class BaseEdgeProcessor { } protected UpdateMsgType getUpdateMsgType(EdgeEventActionType actionType) { - switch (actionType) { - case UPDATED: - case CREDENTIALS_UPDATED: - case ASSIGNED_TO_CUSTOMER: - case UNASSIGNED_FROM_CUSTOMER: - case UPDATED_COMMENT: - return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; - case ADDED: - case ASSIGNED_TO_EDGE: - case RELATION_ADD_OR_UPDATE: - case ADDED_COMMENT: - return UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE; - case DELETED: - case UNASSIGNED_FROM_EDGE: - case RELATION_DELETED: - case DELETED_COMMENT: - case ALARM_DELETE: - return UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; - case ALARM_ACK: - return UpdateMsgType.ALARM_ACK_RPC_MESSAGE; - case ALARM_CLEAR: - return UpdateMsgType.ALARM_CLEAR_RPC_MESSAGE; - default: - throw new RuntimeException("Unsupported actionType [" + actionType + "]"); - } + return switch (actionType) { + case UPDATED, CREDENTIALS_UPDATED, ASSIGNED_TO_CUSTOMER, UNASSIGNED_FROM_CUSTOMER, UPDATED_COMMENT -> + UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; + case ADDED, ASSIGNED_TO_EDGE, RELATION_ADD_OR_UPDATE, ADDED_COMMENT -> + UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE; + case DELETED, UNASSIGNED_FROM_EDGE, RELATION_DELETED, DELETED_COMMENT, ALARM_DELETE -> + UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; + case ALARM_ACK -> UpdateMsgType.ALARM_ACK_RPC_MESSAGE; + case ALARM_CLEAR -> UpdateMsgType.ALARM_CLEAR_RPC_MESSAGE; + default -> throw new RuntimeException("Unsupported actionType [" + actionType + "]"); + }; } public ListenableFuture processEntityNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { @@ -539,48 +497,33 @@ public abstract class BaseEdgeProcessor { } private ListenableFuture updateDependentRuleChains(TenantId tenantId, RuleChainId processingRuleChainId, EdgeId edgeId) { - PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); - PageData pageData; List> futures = new ArrayList<>(); - do { - pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); - if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { - for (RuleChain ruleChain : pageData.getData()) { - if (!ruleChain.getId().equals(processingRuleChainId)) { - List connectionInfos = - ruleChainService.loadRuleChainMetaData(ruleChain.getTenantId(), ruleChain.getId()).getRuleChainConnections(); - if (connectionInfos != null && !connectionInfos.isEmpty()) { - for (RuleChainConnectionInfo connectionInfo : connectionInfos) { - if (connectionInfo.getTargetRuleChainId().equals(processingRuleChainId)) { - futures.add(saveEdgeEvent(tenantId, - edgeId, - EdgeEventType.RULE_CHAIN_METADATA, - EdgeEventActionType.UPDATED, - ruleChain.getId(), - null)); - } - } - } + PageDataIterable ruleChains = new PageDataIterable<>(link -> ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, link), 1024); + for (RuleChain ruleChain : ruleChains) { + List connectionInfos = + ruleChainService.loadRuleChainMetaData(ruleChain.getTenantId(), ruleChain.getId()).getRuleChainConnections(); + if (connectionInfos != null && !connectionInfos.isEmpty()) { + for (RuleChainConnectionInfo connectionInfo : connectionInfos) { + if (connectionInfo.getTargetRuleChainId().equals(processingRuleChainId)) { + futures.add(saveEdgeEvent(tenantId, + edgeId, + EdgeEventType.RULE_CHAIN_METADATA, + EdgeEventActionType.UPDATED, + ruleChain.getId(), + null)); } } - if (pageData.hasNext()) { - pageLink = pageLink.nextPageLink(); - } } - } while (pageData != null && pageData.hasNext()); + } return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService); } private ListenableFuture processEntityNotificationForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId, EdgeId sourceEdgeId) { - switch (actionType) { - case ADDED: - case UPDATED: - case DELETED: - case CREDENTIALS_UPDATED: // used by USER entity - return processActionForAllEdges(tenantId, type, actionType, entityId, sourceEdgeId); - default: - return Futures.immediateFuture(null); - } + return switch (actionType) { + case ADDED, UPDATED, DELETED, CREDENTIALS_UPDATED -> // used by USER entity + processActionForAllEdges(tenantId, type, actionType, entityId, null, sourceEdgeId); + default -> Futures.immediateFuture(null); + }; } protected EntityId constructEntityId(String entityTypeStr, long entityIdMSB, long entityIdLSB) { @@ -623,26 +566,18 @@ public abstract class BaseEdgeProcessor { } protected boolean isEntityExists(TenantId tenantId, EntityId entityId) { - switch (entityId.getEntityType()) { - case TENANT: - return tenantService.findTenantById(tenantId) != null; - case DEVICE: - return deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId())) != null; - case ASSET: - return assetService.findAssetById(tenantId, new AssetId(entityId.getId())) != null; - case ENTITY_VIEW: - return entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId())) != null; - case CUSTOMER: - return customerService.findCustomerById(tenantId, new CustomerId(entityId.getId())) != null; - case USER: - return userService.findUserById(tenantId, new UserId(entityId.getId())) != null; - case DASHBOARD: - return dashboardService.findDashboardById(tenantId, new DashboardId(entityId.getId())) != null; - case EDGE: - return edgeService.findEdgeById(tenantId, new EdgeId(entityId.getId())) != null; - default: - return false; - } + return switch (entityId.getEntityType()) { + case TENANT -> tenantService.findTenantById(tenantId) != null; + case DEVICE -> deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId())) != null; + case ASSET -> assetService.findAssetById(tenantId, new AssetId(entityId.getId())) != null; + case ENTITY_VIEW -> + entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId())) != null; + case CUSTOMER -> customerService.findCustomerById(tenantId, new CustomerId(entityId.getId())) != null; + case USER -> userService.findUserById(tenantId, new UserId(entityId.getId())) != null; + case DASHBOARD -> dashboardService.findDashboardById(tenantId, new DashboardId(entityId.getId())) != null; + case EDGE -> edgeService.findEdgeById(tenantId, new EdgeId(entityId.getId())) != null; + default -> false; + }; } protected void createRelationFromEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId) { @@ -681,37 +616,25 @@ public abstract class BaseEdgeProcessor { } protected AssetProfile checkIfAssetProfileDefaultFieldsAssignedToEdge(TenantId tenantId, EdgeId edgeId, AssetProfile assetProfile, EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_3: - case V_3_3_0: - case V_3_4_0: - if (assetProfile.getDefaultDashboardId() != null - && isEntityNotAssignedToEdge(tenantId, assetProfile.getDefaultDashboardId(), edgeId)) { - assetProfile.setDefaultDashboardId(null); - } - if (assetProfile.getDefaultEdgeRuleChainId() != null - && isEntityNotAssignedToEdge(tenantId, assetProfile.getDefaultEdgeRuleChainId(), edgeId)) { - assetProfile.setDefaultEdgeRuleChainId(null); - } - break; + if (EdgeVersion.V_3_3_0.equals(edgeVersion) || EdgeVersion.V_3_3_3.equals(edgeVersion) || EdgeVersion.V_3_4_0.equals(edgeVersion)) { + if (assetProfile.getDefaultDashboardId() != null && isEntityNotAssignedToEdge(tenantId, assetProfile.getDefaultDashboardId(), edgeId)) { + assetProfile.setDefaultDashboardId(null); + } + if (assetProfile.getDefaultEdgeRuleChainId() != null && isEntityNotAssignedToEdge(tenantId, assetProfile.getDefaultEdgeRuleChainId(), edgeId)) { + assetProfile.setDefaultEdgeRuleChainId(null); + } } return assetProfile; } protected DeviceProfile checkIfDeviceProfileDefaultFieldsAssignedToEdge(TenantId tenantId, EdgeId edgeId, DeviceProfile deviceProfile, EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_3: - case V_3_3_0: - case V_3_4_0: - if (deviceProfile.getDefaultDashboardId() != null - && isEntityNotAssignedToEdge(tenantId, deviceProfile.getDefaultDashboardId(), edgeId)) { - deviceProfile.setDefaultDashboardId(null); - } - if (deviceProfile.getDefaultEdgeRuleChainId() != null - && isEntityNotAssignedToEdge(tenantId, deviceProfile.getDefaultEdgeRuleChainId(), edgeId)) { - deviceProfile.setDefaultEdgeRuleChainId(null); - } - break; + if (EdgeVersion.V_3_3_0.equals(edgeVersion) || EdgeVersion.V_3_3_3.equals(edgeVersion) || EdgeVersion.V_3_4_0.equals(edgeVersion)) { + if (deviceProfile.getDefaultDashboardId() != null && isEntityNotAssignedToEdge(tenantId, deviceProfile.getDefaultDashboardId(), edgeId)) { + deviceProfile.setDefaultDashboardId(null); + } + if (deviceProfile.getDefaultEdgeRuleChainId() != null && isEntityNotAssignedToEdge(tenantId, deviceProfile.getDefaultEdgeRuleChainId(), edgeId)) { + deviceProfile.setDefaultEdgeRuleChainId(null); + } } return deviceProfile; } @@ -726,4 +649,5 @@ public abstract class BaseEdgeProcessor { } return true; } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java index 203c6466e0..d8122e1b6c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java @@ -32,7 +32,6 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsgMetaData; -import org.thingsboard.server.dao.asset.BaseAssetService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; @@ -115,7 +114,7 @@ public abstract class AssetEdgeProcessor extends BaseAssetProcessor implements A case ASSIGNED_TO_CUSTOMER: case UNASSIGNED_FROM_CUSTOMER: Asset asset = assetService.findAssetById(edgeEvent.getTenantId(), assetId); - if (asset != null && !BaseAssetService.TB_SERVICE_QUEUE.equals(asset.getType())) { + if (asset != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); AssetUpdateMsg assetUpdateMsg = ((AssetMsgConstructor) assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructAssetUpdatedMsg(msgType, asset); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java index 46daccfbe4..f6403629eb 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java @@ -29,8 +29,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; @@ -85,20 +84,11 @@ public class CustomerEdgeProcessor extends BaseEdgeProcessor { CustomerId customerId = new CustomerId(EntityIdFactory.getByEdgeEventTypeAndUuid(type, uuid).getId()); switch (actionType) { case UPDATED: - PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); - PageData pageData; List> futures = new ArrayList<>(); - do { - pageData = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink); - if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { - for (Edge edge : pageData.getData()) { - futures.add(saveEdgeEvent(tenantId, edge.getId(), type, actionType, customerId, null)); - } - if (pageData.hasNext()) { - pageLink = pageLink.nextPageLink(); - } - } - } while (pageData != null && pageData.hasNext()); + PageDataIterable edges = new PageDataIterable<>(link -> edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, link), 1024); + for (Edge edge : edges) { + futures.add(saveEdgeEvent(tenantId, edge.getId(), type, actionType, customerId, null)); + } return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService); case DELETED: EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB())); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/oauth2/OAuth2EdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/oauth2/OAuth2EdgeProcessor.java new file mode 100644 index 0000000000..ce0600b491 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/oauth2/OAuth2EdgeProcessor.java @@ -0,0 +1,63 @@ +/** + * 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.service.edge.rpc.processor.oauth2; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; + +@Slf4j +@Component +@TbCoreComponent +public class OAuth2EdgeProcessor extends BaseEdgeProcessor { + + public DownlinkMsg convertOAuth2EventToDownlink(EdgeEvent edgeEvent) { + DownlinkMsg downlinkMsg = null; + OAuth2Info oAuth2Info = JacksonUtil.convertValue(edgeEvent.getBody(), OAuth2Info.class); + if (oAuth2Info != null) { + OAuth2UpdateMsg oAuth2UpdateMsg = oAuth2MsgConstructor.constructOAuth2UpdateMsg(oAuth2Info); + downlinkMsg = DownlinkMsg.newBuilder() + .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) + .addOAuth2UpdateMsg(oAuth2UpdateMsg) + .build(); + } + return downlinkMsg; + } + + public ListenableFuture processOAuth2Notification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { + OAuth2Info oAuth2Info = JacksonUtil.fromString(edgeNotificationMsg.getBody(), OAuth2Info.class); + if (oAuth2Info == null) { + return Futures.immediateFuture(null); + } + EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); + EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()); + return processActionForAllEdges(tenantId, type, actionType, null, JacksonUtil.toJsonNode(edgeNotificationMsg.getBody()), null); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java index e69c479d93..4e9a0b82d8 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java @@ -16,12 +16,18 @@ package org.thingsboard.server.service.edge.rpc.processor.telemetry; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.notification.rule.trigger.EdgeCommunicationFailureTrigger; +import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EntityDataProto; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -31,18 +37,28 @@ import org.thingsboard.server.queue.util.TbCoreComponent; @TbCoreComponent public class TelemetryEdgeProcessor extends BaseTelemetryProcessor { + @Value("${edges.rpc.max_telemetry_message_size:0}") + private int maxTelemetryMessageSize; + + @Lazy + @Autowired + private NotificationRuleProcessor notificationRuleProcessor; + @Override protected String getMsgSourceKey() { return DataConstants.EDGE_MSG_SOURCE; } - public DownlinkMsg convertTelemetryEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertTelemetryEventToDownlink(Edge edge, EdgeEvent edgeEvent) { if (edgeEvent.getBody() != null) { String bodyStr = edgeEvent.getBody().toString(); - if (bodyStr.length() > 1000) { - log.debug("[{}][{}][{}] Conversion to a DownlinkMsg telemetry event failed due to a size limit violation. " + - "Current size is {}, but the limit is 1000. {}", edgeEvent.getTenantId(), edgeEvent.getEdgeId(), - edgeEvent.getEntityId(), bodyStr.length(), StringUtils.truncate(bodyStr, 100)); + if (maxTelemetryMessageSize > 0 && bodyStr.length() > maxTelemetryMessageSize) { + String error = "Conversion to a DownlinkMsg telemetry event failed due to a size limit violation."; + String message = String.format("%s Current size is %s, but the limit is %s", error, bodyStr.length(), maxTelemetryMessageSize); + log.debug("[{}][{}][{}] {}. {}", edgeEvent.getTenantId(), edgeEvent.getEdgeId(), + edgeEvent.getEntityId(), message, StringUtils.truncate(bodyStr, 100)); + notificationRuleProcessor.process(EdgeCommunicationFailureTrigger.builder().tenantId(edgeEvent.getTenantId()) + .edgeId(edgeEvent.getEdgeId()).customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(message).error(error).build()); return null; } } @@ -55,4 +71,5 @@ public class TelemetryEdgeProcessor extends BaseTelemetryProcessor { .addEntityData(entityDataProto) .build(); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java index 2c192fab1d..515dccb987 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java @@ -60,4 +60,5 @@ public class TenantEdgeProcessor extends BaseEdgeProcessor { } return downlinkMsg; } + } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java index 15d7fabe60..671d58a789 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java @@ -22,10 +22,8 @@ import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; @@ -33,30 +31,18 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; -import org.thingsboard.server.service.profile.TbAssetProfileCache; - -import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; @Service @AllArgsConstructor public class DefaultTbAssetService extends AbstractTbEntityService implements TbAssetService { private final AssetService assetService; - private final TbAssetProfileCache assetProfileCache; @Override public Asset save(Asset asset, User user) throws Exception { ActionType actionType = asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED; TenantId tenantId = asset.getTenantId(); try { - if (TB_SERVICE_QUEUE.equals(asset.getType())) { - throw new ThingsboardException("Unable to save asset with type " + TB_SERVICE_QUEUE, ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } else if (asset.getAssetProfileId() != null) { - AssetProfile assetProfile = assetProfileCache.get(tenantId, asset.getAssetProfileId()); - if (assetProfile != null && TB_SERVICE_QUEUE.equals(assetProfile.getName())) { - throw new ThingsboardException("Unable to save asset with profile " + TB_SERVICE_QUEUE, ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - } Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); autoCommit(user, savedAsset.getId()); logEntityActionService.logEntityAction(tenantId, savedAsset.getId(), savedAsset, asset.getCustomerId(), diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java index cfda5f8814..03850880ae 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java @@ -22,7 +22,6 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.TenantId; @@ -30,8 +29,6 @@ import org.thingsboard.server.dao.asset.AssetProfileService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; -import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; - @Service @TbCoreComponent @AllArgsConstructor @@ -45,14 +42,6 @@ public class DefaultTbAssetProfileService extends AbstractTbEntityService implem ActionType actionType = assetProfile.getId() == null ? ActionType.ADDED : ActionType.UPDATED; TenantId tenantId = assetProfile.getTenantId(); try { - if (TB_SERVICE_QUEUE.equals(assetProfile.getName())) { - throw new ThingsboardException("Unable to save asset profile with name " + TB_SERVICE_QUEUE, ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } else if (assetProfile.getId() != null) { - AssetProfile foundAssetProfile = assetProfileService.findAssetProfileById(tenantId, assetProfile.getId()); - if (foundAssetProfile != null && TB_SERVICE_QUEUE.equals(foundAssetProfile.getName())) { - throw new ThingsboardException("Updating asset profile with name " + TB_SERVICE_QUEUE + " is prohibited!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - } AssetProfile savedAssetProfile = checkNotNull(assetProfileService.saveAssetProfile(assetProfile)); autoCommit(user, savedAssetProfile.getId()); logEntityActionService.logEntityAction(tenantId, savedAssetProfile.getId(), savedAssetProfile, diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java index eea0651873..42e77d9c8e 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java @@ -34,7 +34,7 @@ public class AlarmsDeletionTaskProcessor extends HousekeeperTaskProcessor { Queue config = queueService.findQueueByTenantIdAndName(queueKey.getTenantId(), queueKey.getQueueName()); + if (config == null) { + if (!partitions.isEmpty()) { + log.error("[{}] Queue configuration is missing", queueKey, new RuntimeException("stacktrace")); + } + return null; + } return createConsumer(queueKey, config); }); - consumer.update(partitions); + if (consumer != null) { + consumer.update(partitions); + } } }); consumers.keySet().stream() diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerStats.java b/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerStats.java index c860a00237..5479677f39 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerStats.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerStats.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.queue; import io.micrometer.core.instrument.Timer; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.stats.StatsCounter; import org.thingsboard.server.common.stats.StatsFactory; @@ -45,6 +44,7 @@ public class TbRuleEngineConsumerStats { public static final String FAILED_MSGS = "failedMsgs"; public static final String SUCCESSFUL_ITERATIONS = "successfulIterations"; public static final String FAILED_ITERATIONS = "failedIterations"; + public static final String TENANT_ID_TAG = "tenantId"; private final StatsFactory statsFactory; @@ -73,14 +73,15 @@ public class TbRuleEngineConsumerStats { this.statsFactory = statsFactory; String statsKey = StatsType.RULE_ENGINE.getName() + "." + queueName; - this.totalMsgCounter = statsFactory.createStatsCounter(statsKey, TOTAL_MSGS); - this.successMsgCounter = statsFactory.createStatsCounter(statsKey, SUCCESSFUL_MSGS); - this.timeoutMsgCounter = statsFactory.createStatsCounter(statsKey, TIMEOUT_MSGS); - this.failedMsgCounter = statsFactory.createStatsCounter(statsKey, FAILED_MSGS); - this.tmpTimeoutMsgCounter = statsFactory.createStatsCounter(statsKey, TMP_TIMEOUT); - this.tmpFailedMsgCounter = statsFactory.createStatsCounter(statsKey, TMP_FAILED); - this.successIterationsCounter = statsFactory.createStatsCounter(statsKey, SUCCESSFUL_ITERATIONS); - this.failedIterationsCounter = statsFactory.createStatsCounter(statsKey, FAILED_ITERATIONS); + String tenant = tenantId == null || tenantId.isSysTenantId() ? "system" : tenantId.toString(); + this.totalMsgCounter = statsFactory.createStatsCounter(statsKey, TOTAL_MSGS, TENANT_ID_TAG, tenant); + this.successMsgCounter = statsFactory.createStatsCounter(statsKey, SUCCESSFUL_MSGS, TENANT_ID_TAG, tenant); + this.timeoutMsgCounter = statsFactory.createStatsCounter(statsKey, TIMEOUT_MSGS, TENANT_ID_TAG, tenant); + this.failedMsgCounter = statsFactory.createStatsCounter(statsKey, FAILED_MSGS, TENANT_ID_TAG, tenant); + this.tmpTimeoutMsgCounter = statsFactory.createStatsCounter(statsKey, TMP_TIMEOUT, TENANT_ID_TAG, tenant); + this.tmpFailedMsgCounter = statsFactory.createStatsCounter(statsKey, TMP_FAILED, TENANT_ID_TAG, tenant); + this.successIterationsCounter = statsFactory.createStatsCounter(statsKey, SUCCESSFUL_ITERATIONS, TENANT_ID_TAG, tenant); + this.failedIterationsCounter = statsFactory.createStatsCounter(statsKey, FAILED_ITERATIONS, TENANT_ID_TAG, tenant); counters.add(totalMsgCounter); counters.add(successMsgCounter); @@ -93,7 +94,7 @@ public class TbRuleEngineConsumerStats { counters.add(failedIterationsCounter); } - public Timer getTimer(TenantId tenantId, String status){ + public Timer getTimer(TenantId tenantId, String status) { return tenantMsgProcessTimers.computeIfAbsent(tenantId, id -> statsFactory.createTimer(StatsType.RULE_ENGINE.getName() + "." + queueName, "tenantId", tenantId.getId().toString(), diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index 2f5f2abd38..b42f57116c 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -95,7 +95,7 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements @Override public List findLwM2mObject(TenantId tenantId, String sortOrder, String sortProperty, String[] objectIds) { log.trace("Executing findByTenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); List resources = resourceService.findTenantResourcesByResourceTypeAndObjectIds(tenantId, ResourceType.LWM2M_MODEL, objectIds); return resources.stream() @@ -107,7 +107,7 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements @Override public List findLwM2mObjectPage(TenantId tenantId, String sortProperty, String sortOrder, PageLink pageLink) { log.trace("Executing findByTenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); PageData resourcePageData = resourceService.findTenantResourcesByResourceTypeAndPageLink(tenantId, ResourceType.LWM2M_MODEL, pageLink); return resourcePageData.getData().stream() .flatMap(s -> Stream.ofNullable(toLwM2mObject(s, false))) diff --git a/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java b/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java index 64ca544495..03747d3062 100644 --- a/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java +++ b/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java @@ -21,15 +21,15 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.QueueStatsId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.JsonDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.queue.QueueStats; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; -import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.usagerecord.ApiLimitService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.util.TbRuleEngineComponent; @@ -52,7 +52,6 @@ import java.util.stream.Collectors; @RequiredArgsConstructor public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsService { - public static final String TB_SERVICE_QUEUE = "TbServiceQueue"; public static final String RULE_ENGINE_EXCEPTION = "ruleEngineException"; public static final FutureCallback CALLBACK = new FutureCallback() { @Override @@ -68,10 +67,10 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS private final TbServiceInfoProvider serviceInfoProvider; private final TelemetrySubscriptionService tsService; - private final AssetService assetService; + private final QueueStatsService queueStatsService; private final ApiLimitService apiLimitService; private final Lock lock = new ReentrantLock(); - private final ConcurrentMap tenantQueueAssets = new ConcurrentHashMap<>(); + private final ConcurrentMap tenantQueueStats = new ConcurrentHashMap<>(); @Value("${queue.rule-engine.stats.max-error-message-length:4096}") private int maxErrorMessageLength; @@ -82,7 +81,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS ruleEngineStats.getTenantStats().forEach((id, stats) -> { try { TenantId tenantId = TenantId.fromUUID(id); - AssetId serviceAssetId = getServiceAssetId(tenantId, queueName); + QueueStatsId queueStatsId = getQueueStatsId(tenantId, queueName); if (stats.getTotalMsgCounter().get() > 0) { List tsList = stats.getCounters().entrySet().stream() .map(kv -> new BasicTsKvEntry(ts, new LongDataEntry(kv.getKey(), (long) kv.getValue().get()))) @@ -90,7 +89,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS if (!tsList.isEmpty()) { long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getQueueStatsTtlDays); ttl = TimeUnit.DAYS.toSeconds(ttl); - tsService.saveAndNotifyInternal(tenantId, serviceAssetId, tsList, ttl, CALLBACK); + tsService.saveAndNotifyInternal(tenantId, queueStatsId, tsList, ttl, CALLBACK); } } } catch (Exception e) { @@ -104,7 +103,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS TsKvEntry tsKv = new BasicTsKvEntry(e.getTs(), new JsonDataEntry(RULE_ENGINE_EXCEPTION, e.toJsonString(maxErrorMessageLength))); long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getRuleEngineExceptionsTtlDays); ttl = TimeUnit.DAYS.toSeconds(ttl); - tsService.saveAndNotifyInternal(tenantId, getServiceAssetId(tenantId, queueName), Collections.singletonList(tsKv), ttl, CALLBACK); + tsService.saveAndNotifyInternal(tenantId, getQueueStatsId(tenantId, queueName), Collections.singletonList(tsKv), ttl, CALLBACK); } catch (Exception e2) { if (!"Asset is referencing to non-existent tenant!".equalsIgnoreCase(e2.getMessage())) { log.debug("[{}] Failed to store the statistics", tenantId, e2); @@ -113,30 +112,30 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS }); } - private AssetId getServiceAssetId(TenantId tenantId, String queueName) { + private QueueStatsId getQueueStatsId(TenantId tenantId, String queueName) { TenantQueueKey key = new TenantQueueKey(tenantId, queueName); - AssetId assetId = tenantQueueAssets.get(key); - if (assetId == null) { + QueueStatsId queueStatsId = tenantQueueStats.get(key); + if (queueStatsId == null) { lock.lock(); try { - assetId = tenantQueueAssets.get(key); - if (assetId == null) { - Asset asset = assetService.findAssetByTenantIdAndName(tenantId, queueName + "_" + serviceInfoProvider.getServiceId()); - if (asset == null) { - asset = new Asset(); - asset.setTenantId(tenantId); - asset.setName(queueName + "_" + serviceInfoProvider.getServiceId()); - asset.setType(TB_SERVICE_QUEUE); - asset = assetService.saveAsset(asset); + queueStatsId = tenantQueueStats.get(key); + if (queueStatsId == null) { + QueueStats queueStats = queueStatsService.findByTenantIdAndNameAndServiceId(tenantId, queueName , serviceInfoProvider.getServiceId()); + if (queueStats == null) { + queueStats = new QueueStats(); + queueStats.setTenantId(tenantId); + queueStats.setQueueName(queueName); + queueStats.setServiceId(serviceInfoProvider.getServiceId()); + queueStats = queueStatsService.save(tenantId, queueStats); } - assetId = asset.getId(); - tenantQueueAssets.put(key, assetId); + queueStatsId = queueStats.getId(); + tenantQueueStats.put(key, queueStatsId); } } finally { lock.unlock(); } } - return assetId; + return queueStatsId; } @Data diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java index ff8b12a32f..2d8440fa20 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java @@ -23,20 +23,9 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; -import org.thingsboard.server.common.data.kv.BasicTsKvEntry; -import org.thingsboard.server.common.data.kv.BooleanDataEntry; -import org.thingsboard.server.common.data.kv.DataType; -import org.thingsboard.server.common.data.kv.DoubleDataEntry; -import org.thingsboard.server.common.data.kv.JsonDataEntry; -import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.data.kv.LongDataEntry; -import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; -import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionMgrMsgProto; import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmDeleteProto; import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmUpdateProto; @@ -47,7 +36,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesDeletePr import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; @@ -60,6 +48,10 @@ import java.util.Map; import java.util.TreeMap; import java.util.UUID; +import static org.thingsboard.server.common.util.KvProtoUtil.fromTsValueProtoList; +import static org.thingsboard.server.common.util.KvProtoUtil.toTsKvProtoBuilder; +import static org.thingsboard.server.common.util.KvProtoUtil.toTsValueProto; + public class TbSubscriptionUtils { public static ToCoreMsg toSubEventProto(String serviceId, TbEntitySubEvent event) { @@ -181,7 +173,7 @@ public class TbSubscriptionUtils { builder.setEntityIdLSB(entityId.getId().getLeastSignificantBits()); builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits()); builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits()); - ts.forEach(v -> builder.addData(toKeyValueProto(v.getTs(), v).build())); + ts.forEach(v -> builder.addData(toTsKvProtoBuilder(v.getTs(), v).build())); SubscriptionMgrMsgProto.Builder msgBuilder = SubscriptionMgrMsgProto.newBuilder(); msgBuilder.setTsUpdate(builder); return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build(); @@ -208,7 +200,7 @@ public class TbSubscriptionUtils { builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits()); builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits()); builder.setScope(scope); - attributes.forEach(v -> builder.addData(toKeyValueProto(v.getLastUpdateTs(), v).build())); + attributes.forEach(v -> builder.addData(toTsKvProtoBuilder(v.getLastUpdateTs(), v).build())); SubscriptionMgrMsgProto.Builder msgBuilder = SubscriptionMgrMsgProto.newBuilder(); msgBuilder.setAttrUpdate(builder); @@ -231,123 +223,10 @@ public class TbSubscriptionUtils { return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build(); } - private static TsKvProto.Builder toKeyValueProto(long ts, KvEntry attr) { - KeyValueProto.Builder dataBuilder = KeyValueProto.newBuilder(); - dataBuilder.setKey(attr.getKey()); - dataBuilder.setType(KeyValueType.forNumber(attr.getDataType().ordinal())); - switch (attr.getDataType()) { - case BOOLEAN: - attr.getBooleanValue().ifPresent(dataBuilder::setBoolV); - break; - case LONG: - attr.getLongValue().ifPresent(dataBuilder::setLongV); - break; - case DOUBLE: - attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV); - break; - case JSON: - attr.getJsonValue().ifPresent(dataBuilder::setJsonV); - break; - case STRING: - attr.getStrValue().ifPresent(dataBuilder::setStringV); - break; - } - return TsKvProto.newBuilder().setTs(ts).setKv(dataBuilder); - } - - private static TransportProtos.TsValueProto toTsValueProto(long ts, KvEntry attr) { - TransportProtos.TsValueProto.Builder dataBuilder = TransportProtos.TsValueProto.newBuilder(); - dataBuilder.setTs(ts); - dataBuilder.setType(KeyValueType.forNumber(attr.getDataType().ordinal())); - switch (attr.getDataType()) { - case BOOLEAN: - attr.getBooleanValue().ifPresent(dataBuilder::setBoolV); - break; - case LONG: - attr.getLongValue().ifPresent(dataBuilder::setLongV); - break; - case DOUBLE: - attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV); - break; - case JSON: - attr.getJsonValue().ifPresent(dataBuilder::setJsonV); - break; - case STRING: - attr.getStrValue().ifPresent(dataBuilder::setStringV); - break; - } - return dataBuilder.build(); - } - - public static EntityId toEntityId(String entityType, long entityIdMSB, long entityIdLSB) { return EntityIdFactory.getByTypeAndUuid(entityType, new UUID(entityIdMSB, entityIdLSB)); } - public static List toTsKvEntityList(List dataList) { - List result = new ArrayList<>(dataList.size()); - dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), getKvEntry(proto.getKv())))); - return result; - } - - public static List toAttributeKvList(List dataList) { - List result = new ArrayList<>(dataList.size()); - dataList.forEach(proto -> result.add(new BaseAttributeKvEntry(getKvEntry(proto.getKv()), proto.getTs()))); - return result; - } - - private static KvEntry getKvEntry(KeyValueProto proto) { - KvEntry entry = null; - DataType type = DataType.values()[proto.getType().getNumber()]; - switch (type) { - case BOOLEAN: - entry = new BooleanDataEntry(proto.getKey(), proto.getBoolV()); - break; - case LONG: - entry = new LongDataEntry(proto.getKey(), proto.getLongV()); - break; - case DOUBLE: - entry = new DoubleDataEntry(proto.getKey(), proto.getDoubleV()); - break; - case STRING: - entry = new StringDataEntry(proto.getKey(), proto.getStringV()); - break; - case JSON: - entry = new JsonDataEntry(proto.getKey(), proto.getJsonV()); - break; - } - return entry; - } - - public static List toTsKvEntityList(String key, List dataList) { - List result = new ArrayList<>(dataList.size()); - dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), getKvEntry(key, proto)))); - return result; - } - - private static KvEntry getKvEntry(String key, TransportProtos.TsValueProto proto) { - KvEntry entry = null; - DataType type = DataType.values()[proto.getType().getNumber()]; - switch (type) { - case BOOLEAN: - entry = new BooleanDataEntry(key, proto.getBoolV()); - break; - case LONG: - entry = new LongDataEntry(key, proto.getLongV()); - break; - case DOUBLE: - entry = new DoubleDataEntry(key, proto.getDoubleV()); - break; - case STRING: - entry = new StringDataEntry(key, proto.getStringV()); - break; - case JSON: - entry = new JsonDataEntry(key, proto.getJsonV()); - break; - } - return entry; - } - public static ToCoreMsg toAlarmUpdateProto(TenantId tenantId, EntityId entityId, AlarmInfo alarm) { TbAlarmUpdateProto.Builder builder = TbAlarmUpdateProto.newBuilder(); builder.setEntityType(entityId.getEntityType().name()); @@ -405,7 +284,7 @@ public class TbSubscriptionUtils { public static List fromProto(TransportProtos.TbSubUpdateProto proto) { List result = new ArrayList<>(); for (var p : proto.getDataList()) { - result.addAll(toTsKvEntityList(p.getKey(), p.getTsValueList())); + result.addAll(fromTsValueProtoList(p.getKey(), p.getTsValueList())); } return result; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java index 0aa71917d4..6df897c179 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java @@ -101,7 +101,11 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS ctx.putInternalId(exportData.getExternalId(), importResult.getSavedEntity().getId()); ctx.addReferenceCallback(exportData.getExternalId(), importResult.getSaveReferencesCallback()); - ctx.addEventCallback(importResult.getSendEventsCallback()); + if (ctx.isRollbackOnError()) { + ctx.addEventCallback(importResult.getSendEventsCallback()); + } else { + importResult.getSendEventsCallback().run(); + } return importResult; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java index d4c80f730a..909300a4b5 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java @@ -236,14 +236,14 @@ public abstract class AbstractBulkImportService future = gitServiceQueue.getEntity(user.getTenantId(), request.getVersionId(), versionLoadRequest.getExternalEntityId()); DonAsynchron.withCallback(future, - entityData -> doInTemplate(ctx, request, c -> loadSingleEntity(c, config, entityData)), + entityData -> load(ctx, request, c -> loadSingleEntity(c, config, entityData)), e -> processLoadError(ctx, e), executor); break; } case ENTITY_TYPE: { EntityTypeVersionLoadRequest versionLoadRequest = (EntityTypeVersionLoadRequest) request; - executor.submit(() -> doInTemplate(ctx, request, c -> loadMultipleEntities(c, versionLoadRequest))); + ctx.setRollbackOnError(versionLoadRequest.isRollbackOnError()); + executor.submit(() -> load(ctx, request, c -> loadMultipleEntities(c, versionLoadRequest))); break; } default: @@ -265,19 +269,24 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont return ctx.getRequestId(); } - private VersionLoadResult doInTemplate(EntitiesImportCtx ctx, VersionLoadRequest request, Function function) { + private VersionLoadResult load(EntitiesImportCtx ctx, VersionLoadRequest request, Function loadFunction) { try { - VersionLoadResult result = transactionTemplate.execute(status -> { - try { - return function.apply(ctx); - } catch (RuntimeException e) { - throw e; - } catch (Exception e) { - throw new RuntimeException(e); // to prevent UndeclaredThrowableException + VersionLoadResult result; + if (ctx.isRollbackOnError()) { + result = transactionTemplate.execute(status -> { + try { + return loadFunction.apply(ctx); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); // to prevent UndeclaredThrowableException + } + }); + for (ThrowingRunnable eventCallback : ctx.getEventCallbacks()) { + eventCallback.run(); } - }); - for (ThrowingRunnable throwingRunnable : ctx.getEventCallbacks()) { - throwingRunnable.run(); + } else { + result = loadFunction.apply(ctx); } result.setDone(true); return cachePut(ctx.getRequestId(), result); @@ -324,7 +333,6 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont sw.startNew("Entities " + entityType.name()); ctx.setSettings(getEntityImportSettings(request, entityType)); importEntities(ctx, entityType); - persistToCache(ctx); } sw.startNew("Reimport"); @@ -336,7 +344,6 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont .filter(entityType -> request.getEntityTypes().get(entityType).isRemoveOtherEntities()) .sorted(exportImportService.getEntityTypeComparatorForImport().reversed()) .forEach(entityType -> removeOtherEntities(ctx, entityType)); - persistToCache(ctx); sw.startNew("References and Relations"); exportImportService.saveReferencesAndRelations(ctx); @@ -389,6 +396,8 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont ctx.getImportedEntities().computeIfAbsent(entityType, t -> new HashSet<>()) .add(importResult.getSavedEntity().getId()); } + + persistToCache(ctx); log.debug("Imported {} pack ({}) for tenant {}", entityType, entityDataList.size(), ctx.getTenantId()); offset += limit; } while (entityDataList.size() == limit); @@ -413,17 +422,34 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } private void removeOtherEntities(EntitiesImportCtx ctx, EntityType entityType) { - DaoUtil.processInBatches(pageLink -> { - return exportableEntitiesService.findEntitiesByTenantId(ctx.getTenantId(), entityType, pageLink); - }, 100, entity -> { - if (ctx.getImportedEntities().get(entityType) == null || !ctx.getImportedEntities().get(entityType).contains(entity.getId())) { - exportableEntitiesService.removeById(ctx.getTenantId(), entity.getId()); - - ctx.addEventCallback(() -> logEntityActionService.logEntityAction(ctx.getTenantId(), entity.getId(), entity, null, - ActionType.DELETED, ctx.getUser())); - ctx.registerDeleted(entityType); + var entities = new PageDataIterable<>(link -> exportableEntitiesService.findEntitiesIdsByTenantId(ctx.getTenantId(), entityType, link), 100); + Set toRemove = new HashSet<>(); + for (EntityId entityId : entities) { + if (ctx.getImportedEntities().get(entityType) == null || !ctx.getImportedEntities().get(entityType).contains(entityId)) { + toRemove.add(entityId); } - }); + } + + for (EntityId entityId : toRemove) { + ExportableEntity entity = exportableEntitiesService.findEntityById(entityId); + exportableEntitiesService.removeById(ctx.getTenantId(), entityId); + + ThrowingRunnable callback = () -> { + logEntityActionService.logEntityAction(ctx.getTenantId(), entity.getId(), entity, null, + ActionType.DELETED, ctx.getUser()); + }; + if (ctx.isRollbackOnError()) { + ctx.addEventCallback(callback); + } else { + try { + callback.run(); + } catch (ThingsboardException e) { + throw new RuntimeException(e); + } + } + ctx.registerDeleted(entityType); + } + persistToCache(ctx); } private VersionLoadResult onError(EntityId externalId, Throwable e) { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesImportCtx.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesImportCtx.java index 0f0e3313b8..19999ea080 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesImportCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesImportCtx.java @@ -58,6 +58,7 @@ public class EntitiesImportCtx { private boolean finalImportAttempt = false; private EntityImportSettings settings; private EntityImportResult currentImportResult; + private boolean rollbackOnError; public EntitiesImportCtx(UUID requestId, User user, String versionId) { this(requestId, user, versionId, null); diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java index c8683972d9..57ba35628c 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java @@ -68,7 +68,7 @@ public class RpcCleanUpService { long ttl = TimeUnit.DAYS.toMillis(tenantProfileConfiguration.get().getRpcTtlDays()); long expirationTime = System.currentTimeMillis() - ttl; - long totalRemoved = rpcDao.deleteOutdatedRpcByTenantId(tenantId, expirationTime); + int totalRemoved = rpcDao.deleteOutdatedRpcByTenantId(tenantId, expirationTime); if (totalRemoved > 0) { log.info("Removed {} outdated rpc(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime)); diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 99b173399e..938ef7986e 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -866,6 +866,8 @@ js: monitor_thread_pool_size: "${LOCAL_JS_SANDBOX_MONITOR_THREAD_POOL_SIZE:4}" # Maximum CPU time in milliseconds allowed for script execution max_cpu_time: "${LOCAL_JS_SANDBOX_MAX_CPU_TIME:8000}" + # Maximum memory in Bytes which JS executor thread can allocate (approximate calculation). A zero memory limit in combination with a non-zero CPU limit is not recommended due to the implementation of Nashorn 0.4.2. 100MiB is effectively unlimited for most cases + max_memory: "${LOCAL_JS_SANDBOX_MAX_MEMORY:104857600}" # Maximum allowed JavaScript execution errors before JavaScript will be blacklisted max_errors: "${LOCAL_JS_SANDBOX_MAX_ERRORS:3}" # JS Eval max request timeout. 0 - no timeout @@ -1304,6 +1306,8 @@ edges: private_key: "${EDGES_RPC_SSL_PRIVATE_KEY:privateKeyFile.pem}" # Maximum size (in bytes) of inbound messages the cloud can handle from the edge. By default, it can handle messages up to 4 Megabytes max_inbound_message_size: "${EDGES_RPC_MAX_INBOUND_MESSAGE_SIZE:4194304}" + # Maximum length of telemetry (time-series and attributes) message the cloud sends to the edge. By default, there is no limitation. + max_telemetry_message_size: "${EDGES_RPC_MAX_TELEMETRY_MESSAGE_SIZE:0}" storage: # Max records of edge event to read from DB and sent to the edge max_read_records_count: "${EDGES_STORAGE_MAX_READ_RECORDS_COUNT:50}" @@ -1332,6 +1336,8 @@ springdoc: swagger: # General swagger match pattern of swagger UI links api_path: "${SWAGGER_API_PATH:/api/**}" + # General swagger match pattern path of swagger UI links + security_path_regex: "${SWAGGER_SECURITY_PATH_REGEX:/api/.*}" # Nonsecurity API path match pattern of swagger UI links non_security_path_regex: "${SWAGGER_NON_SECURITY_PATH_REGEX:/api/(?:noauth|v1)/.*}" # The title on the API doc UI page diff --git a/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java index b41c8c3e78..06d15ddb44 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java @@ -29,6 +29,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.test.context.ContextConfiguration; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.StringUtils; @@ -45,16 +46,17 @@ import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.asset.AssetDao; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.service.stats.DefaultRuleEngineStatisticsService; import java.util.ArrayList; import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @@ -327,6 +329,53 @@ public class AssetControllerTest extends AbstractControllerTest { .andExpect(statusReason(containsString(msgErrorNoFound("Alarm", alarm.getId().getId().toString())))); } + @Test + public void testDeleteAssetWithPropagatedAlarm() throws Exception { + Device device = new Device(); + device.setTenantId(savedTenant.getTenantId()); + device.setName("Test device"); + device.setLabel("Label"); + device.setType("default"); + device = doPost("/api/device", device, Device.class); + + Asset asset = new Asset(); + asset.setName("My asset"); + asset.setType("default"); + asset = doPost("/api/asset", asset, Asset.class); + + EntityRelation entityRelation = new EntityRelation(asset.getId(), device.getId(), "CONTAINS"); + doPost("/api/relation", entityRelation); + + //create alarm + Alarm alarm = Alarm.builder() + .tenantId(savedTenant.getTenantId()) + .originator(device.getId()) + .severity(AlarmSeverity.CRITICAL) + .type("test_type") + .propagate(true) + .build(); + + alarm = doPost("/api/alarm", alarm, Alarm.class); + Assert.assertNotNull(alarm); + + PageData deviceAlarms = doGetTyped("/api/alarm/DEVICE/" + device.getUuidId() + "?page=0&pageSize=10", new TypeReference<>() { + }); + assertThat(deviceAlarms.getData()).hasSize(1); + + PageData assetAlarms = doGetTyped("/api/alarm/ASSET/" + asset.getUuidId() + "?page=0&pageSize=10", new TypeReference<>() { + }); + assertThat(assetAlarms.getData()).hasSize(1); + + //delete asset + doDelete("/api/asset/" + asset.getId().getId().toString()) + .andExpect(status().isOk()); + + //check device alarms + PageData deviceAlarmsAfterAssetDeletion = doGetTyped("/api/alarm/DEVICE/" + device.getUuidId() + "?page=0&pageSize=10", new TypeReference>() { + }); + assertThat(deviceAlarmsAfterAssetDeletion.getData()).hasSize(1); + } + @Test public void testDeleteAssetAssignedToEntityView() throws Exception { Asset asset1 = new Asset(); @@ -567,8 +616,6 @@ public class AssetControllerTest extends AbstractControllerTest { savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, cntEntity, cntEntity, cntEntity); - loadedAssets.removeIf(asset -> asset.getType().equals(DefaultRuleEngineStatisticsService.TB_SERVICE_QUEUE)); - assets.sort(idComparator); loadedAssets.sort(idComparator); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java index 772a3c5ecb..b0a6db944c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java @@ -25,7 +25,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.TsKvEntry; @@ -34,12 +33,13 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.ProcessingStrategyType; import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.common.data.queue.QueueStats; import org.thingsboard.server.common.data.queue.SubmitStrategy; import org.thingsboard.server.common.data.queue.SubmitStrategyType; import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.stats.StatsFactory; -import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.timeseries.TimeseriesDao; import org.thingsboard.server.gen.transport.TransportProtos; @@ -50,6 +50,7 @@ import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingRes import org.thingsboard.server.service.stats.DefaultRuleEngineStatisticsService; import org.thingsboard.server.service.stats.RuleEngineStatisticsService; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -66,7 +67,6 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; @DaoSqlTest @TestPropertySource(properties = { @@ -81,7 +81,7 @@ public class BaseQueueControllerTest extends AbstractControllerTest { @SpyBean private TimeseriesDao timeseriesDao; @Autowired - private AssetService assetService; + private QueueStatsService queueStatsService; @Test public void testQueueWithServiceTypeRE() throws Exception { @@ -176,16 +176,17 @@ public class BaseQueueControllerTest extends AbstractControllerTest { }); ruleEngineStatisticsService.reportQueueStats(System.currentTimeMillis(), testStats); - Asset serviceAsset = assetService.findAssetsByTenantIdAndType(tenantId, TB_SERVICE_QUEUE, new PageLink(100)).getData() - .stream().filter(asset -> asset.getName().startsWith(queue.getName())) - .findFirst().get(); + List queueStatsList = queueStatsService.findByTenantId(tenantId); + assertThat(queueStatsList).hasSize(1); + QueueStats queueStats = queueStatsList.get(0); + assertThat(queueStats.getQueueName()).isEqualTo(queue.getName()); ArgumentCaptor ttlCaptor = ArgumentCaptor.forClass(Long.class); - verify(timeseriesDao).save(eq(tenantId), eq(serviceAsset.getId()), argThat(tsKvEntry -> { + verify(timeseriesDao).save(eq(tenantId), eq(queueStats.getId()), argThat(tsKvEntry -> { return tsKvEntry.getKey().equals(TbRuleEngineConsumerStats.SUCCESSFUL_MSGS) && tsKvEntry.getLongValue().get().equals(5L); }), ttlCaptor.capture()); - verify(timeseriesDao).save(eq(tenantId), eq(serviceAsset.getId()), argThat(tsKvEntry -> { + verify(timeseriesDao).save(eq(tenantId), eq(queueStats.getId()), argThat(tsKvEntry -> { return tsKvEntry.getKey().equals(TbRuleEngineConsumerStats.FAILED_MSGS) && tsKvEntry.getLongValue().get().equals(5L); }), ttlCaptor.capture()); @@ -193,7 +194,7 @@ public class BaseQueueControllerTest extends AbstractControllerTest { assertThat(usedTtl).isEqualTo(TimeUnit.DAYS.toSeconds(queueStatsTtlDays)); }); - verify(timeseriesDao).save(eq(tenantId), eq(serviceAsset.getId()), argThat(tsKvEntry -> { + verify(timeseriesDao).save(eq(tenantId), eq(queueStats.getId()), argThat(tsKvEntry -> { return tsKvEntry.getKey().equals(DefaultRuleEngineStatisticsService.RULE_ENGINE_EXCEPTION) && tsKvEntry.getJsonValue().get().equals(ruleEngineException.toJsonString(0)); }), ttlCaptor.capture()); diff --git a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java index 61d86994eb..73a3a2e25e 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java @@ -76,6 +76,7 @@ import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg; import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; import org.thingsboard.server.gen.edge.v1.SyncCompletedMsg; @@ -631,7 +632,7 @@ public class EdgeControllerTest extends AbstractControllerTest { List loadedEdges = new ArrayList<>(); PageLink pageLink = new PageLink(23); - PageData pageData = null; + PageData pageData; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", new TypeReference<>() { @@ -885,6 +886,7 @@ public class EdgeControllerTest extends AbstractControllerTest { EdgeImitator edgeImitator = new EdgeImitator(EDGE_HOST, EDGE_PORT, edge.getRoutingKey(), edge.getSecret()); edgeImitator.ignoreType(UserCredentialsUpdateMsg.class); + edgeImitator.ignoreType(OAuth2UpdateMsg.class); edgeImitator.expectMessageAmount(24); edgeImitator.connect(); @@ -973,8 +975,7 @@ public class EdgeControllerTest extends AbstractControllerTest { private boolean popQueueMsg(List messages, UpdateMsgType msgType, String name) { for (AbstractMessage message : messages) { - if (message instanceof QueueUpdateMsg) { - QueueUpdateMsg queueUpdateMsg = (QueueUpdateMsg) message; + if (message instanceof QueueUpdateMsg queueUpdateMsg) { Queue queue = JacksonUtil.fromString(queueUpdateMsg.getEntity(), Queue.class, true); Assert.assertNotNull(queue); if (msgType.equals(queueUpdateMsg.getMsgType()) && name.equals(queue.getName())) { @@ -988,8 +989,7 @@ public class EdgeControllerTest extends AbstractControllerTest { private boolean popRuleChainMsg(List messages, UpdateMsgType msgType, String name) { for (AbstractMessage message : messages) { - if (message instanceof RuleChainUpdateMsg) { - RuleChainUpdateMsg ruleChainUpdateMsg = (RuleChainUpdateMsg) message; + if (message instanceof RuleChainUpdateMsg ruleChainUpdateMsg) { RuleChain ruleChain = JacksonUtil.fromString(ruleChainUpdateMsg.getEntity(), RuleChain.class, true); Assert.assertNotNull(ruleChain); if (msgType.equals(ruleChainUpdateMsg.getMsgType()) @@ -1005,8 +1005,7 @@ public class EdgeControllerTest extends AbstractControllerTest { private boolean popAdminSettingsMsg(List messages, String key) { for (AbstractMessage message : messages) { - if (message instanceof AdminSettingsUpdateMsg) { - AdminSettingsUpdateMsg adminSettingsUpdateMsg = (AdminSettingsUpdateMsg) message; + if (message instanceof AdminSettingsUpdateMsg adminSettingsUpdateMsg) { AdminSettings adminSettings = JacksonUtil.fromString(adminSettingsUpdateMsg.getEntity(), AdminSettings.class, true); Assert.assertNotNull(adminSettings); if (key.equals(adminSettings.getKey())) { @@ -1020,8 +1019,7 @@ public class EdgeControllerTest extends AbstractControllerTest { private boolean popDeviceProfileMsg(List messages, UpdateMsgType msgType, String name) { for (AbstractMessage message : messages) { - if (message instanceof DeviceProfileUpdateMsg) { - DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) message; + if (message instanceof DeviceProfileUpdateMsg deviceProfileUpdateMsg) { DeviceProfile deviceProfile = JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true); Assert.assertNotNull(deviceProfile); if (msgType.equals(deviceProfileUpdateMsg.getMsgType()) @@ -1036,8 +1034,7 @@ public class EdgeControllerTest extends AbstractControllerTest { private boolean popDeviceMsg(List messages, UpdateMsgType msgType, String name) { for (AbstractMessage message : messages) { - if (message instanceof DeviceUpdateMsg) { - DeviceUpdateMsg deviceUpdateMsg = (DeviceUpdateMsg) message; + if (message instanceof DeviceUpdateMsg deviceUpdateMsg) { Device device = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true); Assert.assertNotNull(device); if (msgType.equals(deviceUpdateMsg.getMsgType()) @@ -1052,8 +1049,7 @@ public class EdgeControllerTest extends AbstractControllerTest { private boolean popAssetProfileMsg(List messages, UpdateMsgType msgType, String name) { for (AbstractMessage message : messages) { - if (message instanceof AssetProfileUpdateMsg) { - AssetProfileUpdateMsg assetProfileUpdateMsg = (AssetProfileUpdateMsg) message; + if (message instanceof AssetProfileUpdateMsg assetProfileUpdateMsg) { AssetProfile assetProfile = JacksonUtil.fromString(assetProfileUpdateMsg.getEntity(), AssetProfile.class, true); Assert.assertNotNull(assetProfile); if (msgType.equals(assetProfileUpdateMsg.getMsgType()) @@ -1068,8 +1064,7 @@ public class EdgeControllerTest extends AbstractControllerTest { private boolean popAssetMsg(List messages, UpdateMsgType msgType, String name) { for (AbstractMessage message : messages) { - if (message instanceof AssetUpdateMsg) { - AssetUpdateMsg assetUpdateMsg = (AssetUpdateMsg) message; + if (message instanceof AssetUpdateMsg assetUpdateMsg) { Asset asset = JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true); Assert.assertNotNull(asset); if (msgType.equals(assetUpdateMsg.getMsgType()) @@ -1084,8 +1079,7 @@ public class EdgeControllerTest extends AbstractControllerTest { private boolean popUserMsg(List messages, UpdateMsgType msgType, String email, Authority authority) { for (AbstractMessage message : messages) { - if (message instanceof UserUpdateMsg) { - UserUpdateMsg userUpdateMsg = (UserUpdateMsg) message; + if (message instanceof UserUpdateMsg userUpdateMsg) { User user = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); Assert.assertNotNull(user); if (msgType.equals(userUpdateMsg.getMsgType()) @@ -1101,8 +1095,7 @@ public class EdgeControllerTest extends AbstractControllerTest { private boolean popCustomerMsg(List messages, UpdateMsgType msgType, String title) { for (AbstractMessage message : messages) { - if (message instanceof CustomerUpdateMsg) { - CustomerUpdateMsg customerUpdateMsg = (CustomerUpdateMsg) message; + if (message instanceof CustomerUpdateMsg customerUpdateMsg) { Customer customer = JacksonUtil.fromString(customerUpdateMsg.getEntity(), Customer.class, true); Assert.assertNotNull(customer); if (msgType.equals(customerUpdateMsg.getMsgType()) @@ -1117,8 +1110,7 @@ public class EdgeControllerTest extends AbstractControllerTest { private boolean popTenantMsg(List messages, TenantId tenantId1) { for (AbstractMessage message : messages) { - if (message instanceof TenantUpdateMsg) { - TenantUpdateMsg tenantUpdateMsg = (TenantUpdateMsg) message; + if (message instanceof TenantUpdateMsg tenantUpdateMsg) { Tenant tenant = JacksonUtil.fromString(tenantUpdateMsg.getEntity(), Tenant.class, true); Assert.assertNotNull(tenant); if (UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(tenantUpdateMsg.getMsgType()) @@ -1133,8 +1125,7 @@ public class EdgeControllerTest extends AbstractControllerTest { private boolean popTenantProfileMsg(List messages, TenantProfileId tenantProfileId) { for (AbstractMessage message : messages) { - if (message instanceof TenantProfileUpdateMsg) { - TenantProfileUpdateMsg tenantProfileUpdateMsg = (TenantProfileUpdateMsg) message; + if (message instanceof TenantProfileUpdateMsg tenantProfileUpdateMsg) { TenantProfile tenantProfile = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true); Assert.assertNotNull(tenantProfile); if (UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(tenantProfileUpdateMsg.getMsgType()) @@ -1236,4 +1227,5 @@ public class EdgeControllerTest extends AbstractControllerTest { edgeUpgradeInstructionsService.setAppVersion("3.6.2.6"); Assert.assertTrue(edgeUpgradeInstructionsService.isUpgradeAvailable(savedEdge.getTenantId(), savedEdge.getId())); } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java index 67ad2cbe55..8de8514e5b 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java @@ -22,11 +22,13 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.web.servlet.ResultActions; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; @@ -51,10 +53,13 @@ import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.data.query.KeyFilter; import org.thingsboard.server.common.data.query.NumericFilterPredicate; import org.thingsboard.server.common.data.query.TsValue; +import org.thingsboard.server.common.data.queue.QueueStats; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -69,6 +74,9 @@ public class EntityQueryControllerTest extends AbstractControllerTest { private Tenant savedTenant; private User tenantAdmin; + @Autowired + private QueueStatsService queueStatsService; + @Before public void beforeTest() throws Exception { loginSysAdmin(); @@ -593,4 +601,47 @@ public class EntityQueryControllerTest extends AbstractControllerTest { assertThat(getErrorMessage(result)).contains("Invalid").contains("sort property"); } + @Test + public void testFindQueueStatsEntitiesByQuery() throws Exception { + List queueStatsList = new ArrayList<>(); + for (int i = 0; i < 97; i++) { + QueueStats queueStats = new QueueStats(); + queueStats.setQueueName(StringUtils.randomAlphabetic(5)); + queueStats.setServiceId(StringUtils.randomAlphabetic(5)); + queueStats.setTenantId(savedTenant.getTenantId()); + queueStatsList.add(queueStatsService.save(savedTenant.getId(), queueStats)); + Thread.sleep(1); + } + + EntityTypeFilter entityTypeFilter = new EntityTypeFilter(); + entityTypeFilter.setEntityType(EntityType.QUEUE_STATS); + + EntityDataSortOrder sortOrder = new EntityDataSortOrder( + new EntityKey(EntityKeyType.ENTITY_FIELD, "queueName"), EntityDataSortOrder.Direction.ASC + ); + EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder); + List entityFields = Arrays.asList(new EntityKey(EntityKeyType.ENTITY_FIELD, "queueName"), + new EntityKey(EntityKeyType.ENTITY_FIELD, "serviceId")); + + EntityDataQuery query = new EntityDataQuery(entityTypeFilter, pageLink, entityFields, null, null); + + PageData data = + doPostWithTypedResponse("/api/entitiesQuery/find", query, new TypeReference>() { + }); + + Assert.assertEquals(97, data.getTotalElements()); + Assert.assertEquals(10, data.getTotalPages()); + Assert.assertTrue(data.hasNext()); + Assert.assertEquals(10, data.getData().size()); + data.getData().forEach(entityData -> { + assertThat(entityData.getLatest().get(EntityKeyType.ENTITY_FIELD).get("queueName")).asString().isNotBlank(); + assertThat(entityData.getLatest().get(EntityKeyType.ENTITY_FIELD).get("serviceId")).asString().isNotBlank(); + }); + + EntityCountQuery countQuery = new EntityCountQuery(entityTypeFilter); + + Long count = doPostWithResponse("/api/entitiesQuery/count", countQuery, Long.class); + Assert.assertEquals(97, count.longValue()); + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java b/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java index 8b0854983a..717c17d437 100644 --- a/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java @@ -494,7 +494,7 @@ public class HomePageApiTest extends AbstractControllerTest { } private OAuth2Info createDefaultOAuth2Info() { - return new OAuth2Info(true, Lists.newArrayList( + return new OAuth2Info(true, false, Lists.newArrayList( OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( OAuth2DomainInfo.builder().name("domain").scheme(SchemeType.MIXED).build() diff --git a/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java index d9c35826d4..72930e0327 100644 --- a/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java @@ -1109,13 +1109,14 @@ public class UserControllerTest extends AbstractControllerTest { private List getUsersInfo(PageLink pageLink) throws Exception { List loadedCustomerUsers = new ArrayList<>(); - PageData pageData = null; + PageData pageData; do { pageData = doGetTypedWithPageLink("/api/users/info?", new TypeReference<>() { }, pageLink); loadedCustomerUsers.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); + Assert.assertEquals(pageLink.getPageSize(), pageData.getData().size()); } } while (pageData.hasNext()); return loadedCustomerUsers; diff --git a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java index 0787d08fa7..e7f664477c 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java @@ -63,6 +63,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; @@ -85,6 +86,7 @@ import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.v1.EdgeConfiguration; +import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg; import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; @@ -140,6 +142,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { installation(); edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret()); + edgeImitator.ignoreType(OAuth2UpdateMsg.class); edgeImitator.expectMessageAmount(21); edgeImitator.connect(); @@ -538,6 +541,18 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { Assert.assertTrue(customer.isPublic()); } + private void validateOAuth2() throws Exception { + Optional oAuth2UpdateMsgOpt = edgeImitator.findMessageByType(OAuth2UpdateMsg.class); + Assert.assertTrue(oAuth2UpdateMsgOpt.isPresent()); + OAuth2UpdateMsg oAuth2UpdateMsg = oAuth2UpdateMsgOpt.get(); + OAuth2Info oAuth2Info = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true); + Assert.assertNotNull(oAuth2Info); + OAuth2Info auth2Info = doGet("/api/oauth2/config", OAuth2Info.class); + Assert.assertNotNull(auth2Info); + Assert.assertEquals(oAuth2Info, auth2Info); + testAutoGeneratedCodeByProtobuf(oAuth2UpdateMsg); + } + private void validateSyncCompleted() { Optional syncCompletedMsgOpt = edgeImitator.findMessageByType(SyncCompletedMsg.class); Assert.assertTrue(syncCompletedMsgOpt.isPresent()); diff --git a/application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java new file mode 100644 index 0000000000..c25410b562 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java @@ -0,0 +1,113 @@ +/** + * 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.edge; + +import com.google.common.collect.Lists; +import com.google.protobuf.AbstractMessage; +import org.junit.Assert; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.oauth2.MapperType; +import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2DomainInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2ParamsInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; +import org.thingsboard.server.common.data.oauth2.SchemeType; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg; + +import java.util.Arrays; +import java.util.Collections; +import java.util.UUID; + +@DaoSqlTest +public class OAuth2EdgeTest extends AbstractEdgeTest { + + @Test + public void testOAuth2Support() throws Exception { + loginSysAdmin(); + + // enable oauth + edgeImitator.allowIgnoredTypes(); + edgeImitator.expectMessageAmount(1); + OAuth2Info oAuth2Info = createDefaultOAuth2Info(); + oAuth2Info = doPost("/api/oauth2/config", oAuth2Info, OAuth2Info.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof OAuth2UpdateMsg); + OAuth2UpdateMsg oAuth2UpdateMsg = (OAuth2UpdateMsg) latestMessage; + OAuth2Info result = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true); + Assert.assertEquals(oAuth2Info, result); + + // disable oauth support + edgeImitator.expectMessageAmount(1); + oAuth2Info.setEnabled(false); + oAuth2Info.setEdgeEnabled(false); + doPost("/api/oauth2/config", oAuth2Info, OAuth2Info.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof OAuth2UpdateMsg); + oAuth2UpdateMsg = (OAuth2UpdateMsg) latestMessage; + result = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true); + Assert.assertEquals(oAuth2Info, result); + + edgeImitator.ignoreType(OAuth2UpdateMsg.class); + loginTenantAdmin(); + } + + private OAuth2Info createDefaultOAuth2Info() { + return new OAuth2Info(true, true, Lists.newArrayList( + OAuth2ParamsInfo.builder() + .domainInfos(Lists.newArrayList( + OAuth2DomainInfo.builder().name("domain").scheme(SchemeType.MIXED).build() + )) + .mobileInfos(Collections.emptyList()) + .clientRegistrations(Lists.newArrayList( + validRegistrationInfo() + )) + .build() + )); + } + + private OAuth2RegistrationInfo validRegistrationInfo() { + return OAuth2RegistrationInfo.builder() + .clientId(UUID.randomUUID().toString()) + .clientSecret(UUID.randomUUID().toString()) + .authorizationUri(UUID.randomUUID().toString()) + .accessTokenUri(UUID.randomUUID().toString()) + .scope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString())) + .platforms(Collections.emptyList()) + .userInfoUri(UUID.randomUUID().toString()) + .userNameAttributeName(UUID.randomUUID().toString()) + .jwkSetUri(UUID.randomUUID().toString()) + .clientAuthenticationMethod(UUID.randomUUID().toString()) + .loginButtonLabel(UUID.randomUUID().toString()) + .mapperConfig( + OAuth2MapperConfig.builder() + .type(MapperType.CUSTOM) + .custom( + OAuth2CustomMapperConfig.builder() + .url(UUID.randomUUID().toString()) + .build() + ) + .build() + ) + .build(); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java index 916e5790e9..12405a7d5f 100644 --- a/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java +++ b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java @@ -44,6 +44,7 @@ import org.thingsboard.server.gen.edge.v1.DownlinkResponseMsg; import org.thingsboard.server.gen.edge.v1.EdgeConfiguration; import org.thingsboard.server.gen.edge.v1.EntityDataProto; import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; +import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg; import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; @@ -314,6 +315,11 @@ public class EdgeImitator { result.add(saveDownlinkMsg(resourceUpdateMsg)); } } + if (downlinkMsg.getOAuth2UpdateMsgCount() > 0) { + for (OAuth2UpdateMsg oAuth2UpdateMsg : downlinkMsg.getOAuth2UpdateMsgList()) { + result.add(saveDownlinkMsg(oAuth2UpdateMsg)); + } + } if (downlinkMsg.hasEdgeConfiguration()) { result.add(saveDownlinkMsg(downlinkMsg.getEdgeConfiguration())); } diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java index ba8eda3879..f3ed8e12e6 100644 --- a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java @@ -46,6 +46,7 @@ import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.edge.EdgeSynchronizationManager; import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; @@ -81,6 +82,7 @@ import org.thingsboard.server.service.edge.rpc.constructor.edge.EdgeMsgConstruct import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorFactory; import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorV1; import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.oauth2.OAuth2MsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorFactory; import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorV1; import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorV2; @@ -128,6 +130,7 @@ import org.thingsboard.server.service.edge.rpc.processor.device.profile.DevicePr import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorFactory; import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorV1; import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorV2; +import org.thingsboard.server.service.edge.rpc.processor.oauth2.OAuth2EdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessorFactory; import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessorV1; import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessorV2; @@ -239,6 +242,9 @@ public abstract class BaseEdgeProcessorTest { @MockBean protected ResourceService resourceService; + @MockBean + protected OAuth2Service oAuth2Service; + @MockBean @Lazy protected TbQueueProducerProvider producerProvider; @@ -360,6 +366,9 @@ public abstract class BaseEdgeProcessorTest { @MockBean protected WidgetMsgConstructorV2 widgetMsgConstructorV2; + @MockBean + protected OAuth2MsgConstructor oAuth2MsgConstructor; + @MockBean protected AlarmEdgeProcessorV1 alarmProcessorV1; @@ -417,6 +426,9 @@ public abstract class BaseEdgeProcessorTest { @MockBean protected RelationEdgeProcessorV2 relationEdgeProcessorV2; + @MockBean + protected OAuth2EdgeProcessor oAuth2EdgeProcessor; + @SpyBean protected RuleChainMsgConstructorFactory ruleChainMsgConstructorFactory; @@ -514,17 +526,18 @@ public abstract class BaseEdgeProcessorTest { } protected static Stream provideParameters() { - UUID dashoboardUUID = UUID.randomUUID(); - UUID ruleChaindUUID = UUID.randomUUID(); + UUID dashboardUUID = UUID.randomUUID(); + UUID ruleChainUUID = UUID.randomUUID(); return Stream.of( Arguments.of(EdgeVersion.V_3_3_0, 0, 0, 0, 0), Arguments.of(EdgeVersion.V_3_3_3, 0, 0, 0, 0), Arguments.of(EdgeVersion.V_3_4_0, 0, 0, 0, 0), Arguments.of(EdgeVersion.V_3_6_0, - dashoboardUUID.getMostSignificantBits(), - dashoboardUUID.getLeastSignificantBits(), - ruleChaindUUID.getMostSignificantBits(), - ruleChaindUUID.getLeastSignificantBits()) + dashboardUUID.getMostSignificantBits(), + dashboardUUID.getLeastSignificantBits(), + ruleChainUUID.getMostSignificantBits(), + ruleChainUUID.getLeastSignificantBits()) ); } + } diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java index a6e28e7968..94714af463 100644 --- a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java @@ -16,27 +16,51 @@ package org.thingsboard.server.service.edge.rpc.processor.telemetry; import com.fasterxml.jackson.databind.node.ObjectNode; -import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessorTest; -@Slf4j -@RunWith(MockitoJUnitRunner.class) -public class TelemetryEdgeProcessorTest { +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; + +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = {TelemetryEdgeProcessor.class}) +@TestPropertySource(properties = { + "edges.rpc.max_telemetry_message_size=1000" +}) +public class TelemetryEdgeProcessorTest extends BaseEdgeProcessorTest { + + @SpyBean + private TelemetryEdgeProcessor telemetryEdgeProcessor; + + @MockBean + private NotificationRuleProcessor notificationRuleProcessor; @Test - public void testConvert_maxSizeLimit() throws Exception { + public void testConvert_maxSizeLimit() { + Edge edge = new Edge(); EdgeEvent edgeEvent = new EdgeEvent(); ObjectNode body = JacksonUtil.newObjectNode(); - body.put("value", StringUtils.randomAlphanumeric(10000)); + body.put("value", StringUtils.randomAlphanumeric(1000)); edgeEvent.setBody(body); - DownlinkMsg downlinkMsg = new TelemetryEdgeProcessor().convertTelemetryEventToDownlink(edgeEvent); + + DownlinkMsg downlinkMsg = telemetryEdgeProcessor.convertTelemetryEventToDownlink(edge, edgeEvent); Assert.assertNull(downlinkMsg); + + verify(notificationRuleProcessor, Mockito.times(1)).process(any()); } + } diff --git a/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java b/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java index 55c521c309..28834a0ab7 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java @@ -15,23 +15,29 @@ */ package org.thingsboard.server.service.script; -import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.test.context.TestPropertySource; -import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.TbStopWatch; import org.thingsboard.script.api.ScriptType; import org.thingsboard.script.api.js.NashornJsInvokeService; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.service.DaoSqlTest; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST; @@ -41,8 +47,9 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_RE "js.max_script_body_size=50", "js.max_total_args_size=50", "js.max_result_size=50", - "js.local.max_errors=2" + "js.local.max_errors=2", }) +@Slf4j class NashornJsInvokeServiceTest extends AbstractControllerTest { @Autowired @@ -56,23 +63,64 @@ class NashornJsInvokeServiceTest extends AbstractControllerTest { int iterations = 1000; UUID scriptId = evalScript("return msg.temperature > 20"); // warmup - ObjectNode msg = JacksonUtil.newObjectNode(); - for (int i = 0; i < 100; i++) { - msg.put("temperature", i); + log.info("Warming up 1000 times..."); + var warmupWatch = TbStopWatch.create(); + for (int i = 0; i < 1000; i++) { boolean expected = i > 20; - boolean result = Boolean.valueOf(invokeScript(scriptId, JacksonUtil.toString(msg))); + boolean result = Boolean.parseBoolean(invokeScript(scriptId, "{\"temperature\":" + i + "}")); Assert.assertEquals(expected, result); } - long startTs = System.currentTimeMillis(); + log.info("Warming up finished in {} ms", warmupWatch.stopAndGetTotalTimeMillis()); + log.info("Starting performance test..."); + var watch = TbStopWatch.create(); for (int i = 0; i < iterations; i++) { - msg.put("temperature", i); boolean expected = i > 20; - boolean result = Boolean.valueOf(invokeScript(scriptId, JacksonUtil.toString(msg))); + boolean result = Boolean.parseBoolean(invokeScript(scriptId, "{\"temperature\":" + i + "}")); + log.debug("asserting result"); Assert.assertEquals(expected, result); } - long duration = System.currentTimeMillis() - startTs; - System.out.println(iterations + " invocations took: " + duration + "ms"); - Assert.assertTrue(duration < TimeUnit.MINUTES.toMillis(4)); + long duration = watch.stopAndGetTotalTimeMillis(); + log.info("Performance test with {} invocations took: {} ms", iterations, duration); + assertThat(duration).as("duration ms") + .isLessThan(TimeUnit.MINUTES.toMillis(1)); // effective exec time is about 500ms + } + + @Test + void givenSimpleScriptMultiThreadTestPerformance() throws ExecutionException, InterruptedException, TimeoutException { + int iterations = 1000*4; + List> futures = new ArrayList<>(iterations); + UUID scriptId = evalScript("return msg.temperature > 20 ;"); + // warmup + log.info("Warming up 1000 times..."); + + var warmupWatch = TbStopWatch.create(); + for (int i = 0; i < 1000; i++) { + futures.add(invokeScriptAsync(scriptId, "{\"temperature\":" + i + "}")); + } + List results = Futures.allAsList(futures).get(1, TimeUnit.MINUTES); + for (int i = 0; i < 1000; i++) { + boolean expected = i > 20; + boolean result = Boolean.parseBoolean(results.get(i).toString()); + Assert.assertEquals(expected, result); + } + log.info("Warming up finished in {} ms", warmupWatch.stopAndGetTotalTimeMillis()); + futures.clear(); + + log.info("Starting performance test..."); + var watch = TbStopWatch.create(); + for (int i = 0; i < iterations; i++) { + futures.add(invokeScriptAsync(scriptId, "{\"temperature\":" + i + "}")); + } + results = Futures.allAsList(futures).get(1, TimeUnit.MINUTES); + for (int i = 0; i < iterations; i++) { + boolean expected = i > 20; + boolean result = Boolean.parseBoolean(results.get(i).toString()); + Assert.assertEquals(expected, result); + } + long duration = watch.stopAndGetTotalTimeMillis(); + log.info("Performance test with {} invocations took: {} ms", iterations, duration); + assertThat(duration).as("duration ms") + .isLessThan(TimeUnit.MINUTES.toMillis(1)); // effective exec time is about 500ms } @Test @@ -122,7 +170,11 @@ class NashornJsInvokeServiceTest extends AbstractControllerTest { } private String invokeScript(UUID scriptId, String msg) throws ExecutionException, InterruptedException { - return invokeService.invokeScript(TenantId.SYS_TENANT_ID, null, scriptId, msg, "{}", POST_TELEMETRY_REQUEST.name()).get().toString(); + return invokeScriptAsync(scriptId, msg).get().toString(); + } + + private ListenableFuture invokeScriptAsync(UUID scriptId, String msg) { + return invokeService.invokeScript(TenantId.SYS_TENANT_ID, null, scriptId, msg, "{}", POST_TELEMETRY_REQUEST.name()); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesIntegrationTest.java index a7de7eb571..9ac2d5fc11 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesIntegrationTest.java @@ -19,7 +19,6 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.server.resources.Resource; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.server.coapserver.DefaultCoapServerService; @@ -60,15 +59,11 @@ public class CoapAttributesUpdatesIntegrationTest extends AbstractCoapAttributes processAfterTest(); } - - - @Ignore // Uncomment when Californium 3.11 is released with https://github.com/eclipse-californium/californium/pull/2215 @Test public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { processJsonTestSubscribeToAttributesUpdates(false); } - @Ignore // Uncomment when Californium 3.11 is released with https://github.com/eclipse-californium/californium/pull/2215 @Test public void testSubscribeToAttributesUpdatesFromTheServerWithEmptyCurrentStateNotification() throws Exception { processJsonTestSubscribeToAttributesUpdates(true); diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesJsonIntegrationTest.java index 4755ba134d..3fa625796c 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesJsonIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesJsonIntegrationTest.java @@ -18,7 +18,6 @@ package org.thingsboard.server.transport.coap.attributes.updates; import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.common.data.CoapDeviceType; import org.thingsboard.server.common.data.TransportPayloadType; @@ -45,12 +44,11 @@ public class CoapAttributesUpdatesJsonIntegrationTest extends AbstractCoapAttrib processAfterTest(); } - @Ignore // Uncomment when Californium 3.11 is released with https://github.com/eclipse-californium/californium/pull/2215 @Test public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { processJsonTestSubscribeToAttributesUpdates(false); } - @Ignore // Uncomment when Californium 3.11 is released with https://github.com/eclipse-californium/californium/pull/2215 + @Test public void testSubscribeToAttributesUpdatesFromTheServerWithEmptyCurrentStateNotification() throws Exception { processJsonTestSubscribeToAttributesUpdates(true); diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesProtoIntegrationTest.java index 1c9589bba9..4b07c8dcc2 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesProtoIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesProtoIntegrationTest.java @@ -18,7 +18,6 @@ package org.thingsboard.server.transport.coap.attributes.updates; import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.common.data.CoapDeviceType; import org.thingsboard.server.common.data.TransportPayloadType; @@ -44,12 +43,10 @@ public class CoapAttributesUpdatesProtoIntegrationTest extends AbstractCoapAttri public void afterTest() throws Exception { processAfterTest(); } - @Ignore // Uncomment when Californium 3.11 is released with https://github.com/eclipse-californium/californium/pull/2215 @Test public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { processProtoTestSubscribeToAttributesUpdates(false); } - @Ignore // Uncomment when Californium 3.11 is released with https://github.com/eclipse-californium/californium/pull/2215 @Test public void testSubscribeToAttributesUpdatesFromTheServerWithEmptyCurrentStateNotification() throws Exception { processProtoTestSubscribeToAttributesUpdates(true); diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java index e7a7485ef5..112d3e6aa5 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java @@ -26,7 +26,6 @@ import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.MediaTypeRegistry; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.DeviceId; @@ -69,7 +68,6 @@ public class CoapClientIntegrationTest extends AbstractCoapIntegrationTest { private static final List EXPECTED_KEYS = Arrays.asList("key1", "key2", "key3", "key4", "key5"); private static final String DEVICE_RESPONSE = "{\"value1\":\"A\",\"value2\":\"B\"}"; - @Before public void beforeTest() throws Exception { CoapTestConfigProperties configProperties = CoapTestConfigProperties.builder() @@ -83,7 +81,6 @@ public class CoapClientIntegrationTest extends AbstractCoapIntegrationTest { processAfterTest(); } - @Ignore // Uncomment when Californium 3.11 is released with https://github.com/eclipse-californium/californium/pull/2215 @Test public void testConfirmableRequests() throws Exception { boolean confirmable = true; @@ -92,7 +89,6 @@ public class CoapClientIntegrationTest extends AbstractCoapIntegrationTest { processTestRequestAttributesValuesFromTheServer(confirmable); } - @Ignore // Uncomment when Californium 3.11 is released with https://github.com/eclipse-californium/californium/pull/2215 @Test public void testNonConfirmableRequests() throws Exception { boolean confirmable = false; diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcDefaultIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcDefaultIntegrationTest.java index 8f9d3379ef..df1159b128 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcDefaultIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcDefaultIntegrationTest.java @@ -20,7 +20,6 @@ import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.service.security.AccessValidator; @@ -83,13 +82,11 @@ public class CoapServerSideRpcDefaultIntegrationTest extends AbstractCoapServerS Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); } - @Ignore // Uncomment when Californium 3.11 is released with https://github.com/eclipse-californium/californium/pull/2215 @Test public void testServerCoapOneWayRpc() throws Exception { processOneWayRpcTest(false); } - @Ignore // Uncomment when Californium 3.11 is released with https://github.com/eclipse-californium/californium/pull/2215 @Test public void testServerCoapTwoWayRpc() throws Exception { processTwoWayRpcTest("{\"value1\":\"A\",\"value2\":\"B\"}", false); diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcJsonIntegrationTest.java index 34d678b5b5..5c94e91b72 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcJsonIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcJsonIntegrationTest.java @@ -18,7 +18,6 @@ package org.thingsboard.server.transport.coap.rpc; import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.common.data.CoapDeviceType; import org.thingsboard.server.common.data.TransportPayloadType; @@ -44,13 +43,11 @@ public class CoapServerSideRpcJsonIntegrationTest extends AbstractCoapServerSide processAfterTest(); } - @Ignore // Uncomment when Californium 3.11 is released with https://github.com/eclipse-californium/californium/pull/2215 @Test public void testServerCoapOneWayRpc() throws Exception { processOneWayRpcTest(false); } - @Ignore // Uncomment when Californium 3.11 is released with https://github.com/eclipse-californium/californium/pull/2215 @Test public void testServerCoapTwoWayRpc() throws Exception { processTwoWayRpcTest("{\"value1\":\"A\",\"value2\":\"B\"}", false); diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcProtoIntegrationTest.java index 138f87e964..2f7d46a390 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcProtoIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcProtoIntegrationTest.java @@ -18,7 +18,6 @@ package org.thingsboard.server.transport.coap.rpc; import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.common.data.CoapDeviceType; import org.thingsboard.server.common.data.TransportPayloadType; @@ -45,13 +44,11 @@ public class CoapServerSideRpcProtoIntegrationTest extends AbstractCoapServerSid processAfterTest(); } - @Ignore // Uncomment when Californium 3.11 is released with https://github.com/eclipse-californium/californium/pull/2215 @Test public void testServerCoapOneWayRpc() throws Exception { processOneWayRpcTest(true); } - @Ignore // Uncomment when Californium 3.11 is released with https://github.com/eclipse-californium/californium/pull/2215 @Test public void testServerCoapTwoWayRpc() throws Exception { processTwoWayRpcTest("{\"payload\":\"{\\\"value1\\\":\\\"A\\\",\\\"value2\\\":\\\"B\\\"}\"}", true); diff --git a/application/src/test/resources/logback-test.xml b/application/src/test/resources/logback-test.xml index 981bcab132..d72bccb7a6 100644 --- a/application/src/test/resources/logback-test.xml +++ b/application/src/test/resources/logback-test.xml @@ -16,7 +16,7 @@ - + diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java index 5607e4ff50..57bee7ba1a 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java @@ -81,7 +81,7 @@ public interface AlarmService extends EntityDaoService { void delAlarmTypes(TenantId tenantId, Set types); - int deleteAlarmsByEntityId(TenantId tenantId, EntityId entityId); + int deleteAlarmsByOriginatorId(TenantId tenantId, EntityId entityId); // Other API Alarm findAlarmById(TenantId tenantId, AlarmId alarmId); @@ -108,6 +108,8 @@ public interface AlarmService extends EntityDaoService { PageData findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, PageLink pageLink); + PageData findAlarmIdsByOriginatorId(TenantId tenantId, EntityId originatorId, PageLink pageLink); + int deleteEntityAlarmRecords(TenantId tenantId, EntityId entityId); void deleteEntityAlarmRecordsByTenantId(TenantId tenantId); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index 941432b879..11129002e7 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.UUID; public interface OAuth2Service { + List getOAuth2Clients(String domainScheme, String domainName, String pkgName, PlatformType platformType); void saveOAuth2Info(OAuth2Info oauth2Info); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/queue/QueueStatsService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/queue/QueueStatsService.java new file mode 100644 index 0000000000..c9e3f605c6 --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/queue/QueueStatsService.java @@ -0,0 +1,35 @@ +/** + * 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.dao.queue; + +import org.thingsboard.server.common.data.id.QueueStatsId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.queue.QueueStats; +import org.thingsboard.server.dao.entity.EntityDaoService; + +import java.util.List; + +public interface QueueStatsService extends EntityDaoService { + + QueueStats save(TenantId tenantId, QueueStats queueStats); + + QueueStats findQueueStatsById(TenantId tenantId, QueueStatsId queueStatsId); + + QueueStats findByTenantIdAndNameAndServiceId(TenantId tenantId, String queueName, String serviceId); + + List findByTenantId(TenantId tenantId); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index fb4fe1011e..bb43bf4d11 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -58,7 +58,8 @@ public enum EntityType { NOTIFICATION_TEMPLATE (30), NOTIFICATION_REQUEST (31), NOTIFICATION (32), - NOTIFICATION_RULE (33); + NOTIFICATION_RULE (33), + QUEUE_STATS(34); @Getter private final int protoNumber; // Corresponds to EntityTypeProto diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java index 23b6c9b76d..0bd8e3a687 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java @@ -25,6 +25,7 @@ import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.id.AlarmCommentId; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.validation.Length; @@ -36,21 +37,21 @@ import org.thingsboard.server.common.data.validation.NoXss; @AllArgsConstructor public class AlarmComment extends BaseData implements HasName { @Schema(description = "JSON object with Alarm id.", accessMode = Schema.AccessMode.READ_ONLY) - private EntityId alarmId; + private AlarmId alarmId; @Schema(description = "JSON object with User id.", accessMode = Schema.AccessMode.READ_ONLY) private UserId userId; @Schema(description = "Defines origination of comment. System type means comment was created by TB. OTHER type means comment was created by user.", example = "SYSTEM/OTHER", accessMode = Schema.AccessMode.READ_ONLY) private AlarmCommentType type; - @Schema(description = "JSON object with text of comment.",implementation = com.fasterxml.jackson.databind.JsonNode.class) + @Schema(description = "JSON object with text of comment.") @NoXss @Length(fieldName = "comment", max = 10000) @EqualsAndHashCode.Include - private transient JsonNode comment; + private JsonNode comment; @Schema(description = "JSON object with the alarm comment Id. " + "Specify this field to update the alarm comment. " + "Referencing non-existing alarm Id will cause error. " + - "Omit this field to create new alarm." ) + "Omit this field to create new alarm.", accessMode = Schema.AccessMode.READ_ONLY) @Override public AlarmCommentId getId() { return super.getId(); @@ -72,7 +73,7 @@ public class AlarmComment extends BaseData implements HasName { @Override @JsonProperty(access = JsonProperty.Access.READ_ONLY) - @Schema(required = true, description = "representing comment text", example = "Please take a look") + @Schema(accessMode = Schema.AccessMode.READ_ONLY, description = "representing comment text", example = "Please take a look") public String getName() { return comment.toString(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java index 6fc255501f..783bb3ffd5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java @@ -41,7 +41,8 @@ public enum EdgeEventType { ADMIN_SETTINGS(true, null), OTA_PACKAGE(true, EntityType.OTA_PACKAGE), QUEUE(true, EntityType.QUEUE), - TB_RESOURCE(true, EntityType.TB_RESOURCE); + TB_RESOURCE(true, EntityType.TB_RESOURCE), + OAUTH2(true, null); private final boolean allEdgesRelated; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityId.java index 8b4b1f156d..64c6b03427 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityId.java @@ -35,10 +35,10 @@ public interface EntityId extends HasUUID, Serializable { //NOSONAR, the constan UUID NULL_UUID = UUID.fromString("13814000-1dd2-11b2-8080-808080808080"); - @Schema(required = true, description = "ID of the entity, time-based UUID v1", example = "784f394c-42b6-435a-983c-b7beff2784f9") + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "ID of the entity, time-based UUID v1", example = "784f394c-42b6-435a-983c-b7beff2784f9") UUID getId(); - @Schema(required = true, example = "DEVICE") + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, example = "DEVICE") EntityType getEntityType(); @JsonIgnore diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index eb8b337c7d..7a9e4388f7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -103,6 +103,8 @@ public class EntityIdFactory { return new NotificationTemplateId(uuid); case NOTIFICATION: return new NotificationId(uuid); + case QUEUE_STATS: + return new QueueStatsId(uuid); } throw new IllegalArgumentException("EntityType " + type + " is not supported!"); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/QueueStatsId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/QueueStatsId.java new file mode 100644 index 0000000000..ae5a4843ed --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/QueueStatsId.java @@ -0,0 +1,43 @@ +/** + * 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.common.data.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import org.thingsboard.server.common.data.EntityType; + +import java.util.UUID; + +public class QueueStatsId extends UUIDBased implements EntityId { + + private static final long serialVersionUID = 1L; + + @JsonCreator + public QueueStatsId(@JsonProperty("id") UUID id) { + super(id); + } + + public static QueueStatsId fromString(String queueId) { + return new QueueStatsId(UUID.fromString(queueId)); + } + + @Schema(required = true, description = "string", example = "QUEUE_STATS", allowableValues = "QUEUE_STATS") + @Override + public EntityType getEntityType() { + return EntityType.QUEUE_STATS; + } +} \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/UUIDBased.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/UUIDBased.java index ac56ccef12..b9fd9d30ee 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/UUIDBased.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/UUIDBased.java @@ -68,7 +68,7 @@ public abstract class UUIDBased implements HasUUID, Serializable { @Override public String toString() { - return id.toString(); + return String.valueOf(id); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/UserId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/UserId.java index 1f18f5a78e..65658246d2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/UserId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/UserId.java @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.EntityType; import java.util.UUID; +@Schema public class UserId extends UUIDBased implements EntityId { @JsonCreator @@ -33,7 +34,7 @@ public class UserId extends UUIDBased implements EntityId { return new UserId(UUID.fromString(userId)); } - @Schema(required = true, description = "string", example = "USER", allowableValues = "USER") + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "string", example = "USER", allowableValues = "USER") @Override public EntityType getEntityType() { return EntityType.USER; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java index a9d857ca9e..a8ad9b42c1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java @@ -15,8 +15,21 @@ */ package org.thingsboard.server.common.data.kv; +import lombok.Getter; + public enum DataType { - STRING, LONG, BOOLEAN, DOUBLE, JSON; + BOOLEAN(0), + LONG(1), + DOUBLE(2), + STRING(3), + JSON(4); + + @Getter + private final int protoNumber; // Corresponds to KeyValueType + + DataType(int protoNumber) { + this.protoNumber = protoNumber; + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java index c163647d49..034070f57c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java @@ -35,6 +35,8 @@ import java.util.List; public class OAuth2Info { @Schema(description = "Whether OAuth2 settings are enabled or not") private boolean enabled; + @Schema(description = "Whether OAuth2 settings are enabled on Edge or not") + private boolean edgeEnabled; @Schema(description = "List of configured OAuth2 clients. Cannot contain null values", required = true) private List oauth2ParamsInfos; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java index 3f847c2f9e..70020fc7cd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java @@ -30,11 +30,13 @@ import org.thingsboard.server.common.data.id.TenantId; public class OAuth2Params extends BaseData { private boolean enabled; + private boolean edgeEnabled; private TenantId tenantId; public OAuth2Params(OAuth2Params oauth2Params) { super(oauth2Params); this.enabled = oauth2Params.enabled; + this.edgeEnabled = oauth2Params.edgeEnabled; this.tenantId = oauth2Params.tenantId; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageLink.java b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageLink.java index 14f98ca97d..042a9b1896 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageLink.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageLink.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import org.springframework.data.domain.Sort; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -65,19 +66,19 @@ public class PageLink { return new PageLink(this.pageSize, this.page+1, this.textSearch, this.sortOrder); } - public Sort toSort(SortOrder sortOrder, Map columnMap) { + public Sort toSort(SortOrder sortOrder, Map columnMap, boolean addDefaultSorting) { if (sortOrder == null) { return DEFAULT_SORT; } else { - String property = sortOrder.getProperty(); - if (columnMap.containsKey(property)) { - property = columnMap.get(property); - } - return Sort.by(Sort.Direction.fromString(sortOrder.getDirection().name()), property); + return toSort(List.of(sortOrder), columnMap, addDefaultSorting); } } - public Sort toSort(List sortOrders, Map columnMap) { + public Sort toSort(List sortOrders, Map columnMap, boolean addDefaultSorting) { + if (addDefaultSorting && !isDefaultSortOrderAvailable(sortOrders)) { + sortOrders = new ArrayList<>(sortOrders); + sortOrders.add(new SortOrder(DEFAULT_SORT_PROPERTY, SortOrder.Direction.ASC)); + } return Sort.by(sortOrders.stream().map(s -> toSortOrder(s, columnMap)).collect(Collectors.toList())); } @@ -86,7 +87,16 @@ public class PageLink { if (columnMap.containsKey(property)) { property = columnMap.get(property); } - return new Sort.Order(Sort.Direction.fromString(sortOrder.getDirection().name()), property, Sort.NullHandling.NULLS_LAST); + return new Sort.Order(Sort.Direction.fromString(sortOrder.getDirection().name()), property); + } + + public boolean isDefaultSortOrderAvailable(List sortOrders) { + for (SortOrder sortOrder : sortOrders) { + if (DEFAULT_SORT_PROPERTY.equals(sortOrder.getProperty())) { + return true; + } + } + return false; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/page/TimePageLink.java b/common/data/src/main/java/org/thingsboard/server/common/data/page/TimePageLink.java index c9dacbff40..365d4f7e35 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/page/TimePageLink.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/page/TimePageLink.java @@ -19,11 +19,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import org.springframework.data.domain.Sort; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; @Data @ToString(callSuper = true) @@ -67,29 +62,4 @@ public class TimePageLink extends PageLink { this.startTime, this.endTime); } - @Override - public Sort toSort(SortOrder sortOrder, Map columnMap) { - if (sortOrder == null) { - return super.toSort(sortOrder, columnMap); - } else { - return toSort(new ArrayList<>(List.of(sortOrder)), columnMap); - } - } - - @Override - public Sort toSort(List sortOrders, Map columnMap) { - if (!isDefaultSortOrderAvailable(sortOrders)) { - sortOrders.add(new SortOrder(DEFAULT_SORT_PROPERTY, SortOrder.Direction.ASC)); - } - return super.toSort(sortOrders, columnMap); - } - - private boolean isDefaultSortOrderAvailable(List sortOrders) { - for (SortOrder sortOrder : sortOrders) { - if (DEFAULT_SORT_PROPERTY.equals(sortOrder.getProperty())) { - return true; - } - } - return false; - } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java b/common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java new file mode 100644 index 0000000000..04d50dfe6a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java @@ -0,0 +1,39 @@ +/** + * 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.common.data.queue; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.QueueStatsId; + +@EqualsAndHashCode(callSuper = true) +@Data +public class QueueStats extends BaseData implements HasTenantId { + private TenantId tenantId; + private String queueName; + private String serviceId; + + public QueueStats() { + } + + public QueueStats(QueueStatsId id) { + super(id); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/EntityTypeVersionLoadRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/EntityTypeVersionLoadRequest.java index bcc6fc9c5b..7eca2c04de 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/EntityTypeVersionLoadRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/EntityTypeVersionLoadRequest.java @@ -26,6 +26,7 @@ import java.util.Map; public class EntityTypeVersionLoadRequest extends VersionLoadRequest { private Map entityTypes; + private boolean rollbackOnError; @Override public VersionLoadRequestType getType() { diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 34a8c6f093..58959e4d19 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -37,6 +37,7 @@ enum EdgeVersion { V_3_6_0 = 3; V_3_6_1 = 4; V_3_6_2 = 5; + V_3_7_0 = 6; } /** @@ -467,6 +468,10 @@ message ResourceUpdateMsg { string entity = 11; } +message OAuth2UpdateMsg { + string entity = 1; +} + message RuleChainMetadataRequestMsg { int64 ruleChainIdMSB = 1; int64 ruleChainIdLSB = 2; @@ -668,5 +673,6 @@ message DownlinkMsg { repeated TenantProfileUpdateMsg tenantProfileUpdateMsg = 27; repeated ResourceUpdateMsg resourceUpdateMsg = 28; repeated AlarmCommentUpdateMsg alarmCommentUpdateMsg = 29; + repeated OAuth2UpdateMsg oAuth2UpdateMsg = 30; } diff --git a/common/proto/src/main/java/org/thingsboard/server/common/util/KvProtoUtil.java b/common/proto/src/main/java/org/thingsboard/server/common/util/KvProtoUtil.java index 504ffe368d..74674e1e45 100644 --- a/common/proto/src/main/java/org/thingsboard/server/common/util/KvProtoUtil.java +++ b/common/proto/src/main/java/org/thingsboard/server/common/util/KvProtoUtil.java @@ -29,11 +29,26 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.gen.transport.TransportProtos; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; public class KvProtoUtil { + private static final DataType[] dataTypeByProtoNumber; + + static { + int arraySize = Arrays.stream(DataType.values()).mapToInt(DataType::getProtoNumber).max().orElse(0); + dataTypeByProtoNumber = new DataType[arraySize + 1]; + Arrays.stream(DataType.values()).forEach(dataType -> dataTypeByProtoNumber[dataType.getProtoNumber()] = dataType); + } + + public static List toAttributeKvList(List dataList) { + List result = new ArrayList<>(dataList.size()); + dataList.forEach(proto -> result.add(new BaseAttributeKvEntry(fromTsKvProto(proto.getKv()), proto.getTs()))); + return result; + } + public static List attrToTsKvProtos(List result) { List clientAttributes; if (result == null || result.isEmpty()) { @@ -47,8 +62,7 @@ public class KvProtoUtil { return clientAttributes; } - - public static List tsToTsKvProtos(List result) { + public static List toTsKvProtoList(List result) { List ts; if (result == null || result.isEmpty()) { ts = Collections.emptyList(); @@ -61,95 +75,85 @@ public class KvProtoUtil { return ts; } + public static List fromTsKvProtoList(List dataList) { + List result = new ArrayList<>(dataList.size()); + dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), fromTsKvProto(proto.getKv())))); + return result; + } + public static TransportProtos.TsKvProto toTsKvProto(long ts, KvEntry kvEntry) { return TransportProtos.TsKvProto.newBuilder().setTs(ts) - .setKv(KvProtoUtil.toKeyValueProto(kvEntry)).build(); + .setKv(KvProtoUtil.toKeyValueTypeProto(kvEntry)).build(); + } + + public static TsKvEntry fromTsKvProto(TransportProtos.TsKvProto proto) { + return new BasicTsKvEntry(proto.getTs(), fromTsKvProto(proto.getKv())); } - public static TransportProtos.KeyValueProto toKeyValueProto(KvEntry kvEntry) { + public static TransportProtos.KeyValueProto toKeyValueTypeProto(KvEntry kvEntry) { TransportProtos.KeyValueProto.Builder builder = TransportProtos.KeyValueProto.newBuilder(); builder.setKey(kvEntry.getKey()); + builder.setType(toKeyValueTypeProto(kvEntry.getDataType())); switch (kvEntry.getDataType()) { - case BOOLEAN: - builder.setType(TransportProtos.KeyValueType.BOOLEAN_V); - builder.setBoolV(kvEntry.getBooleanValue().get()); - break; - case DOUBLE: - builder.setType(TransportProtos.KeyValueType.DOUBLE_V); - builder.setDoubleV(kvEntry.getDoubleValue().get()); - break; - case LONG: - builder.setType(TransportProtos.KeyValueType.LONG_V); - builder.setLongV(kvEntry.getLongValue().get()); - break; - case STRING: - builder.setType(TransportProtos.KeyValueType.STRING_V); - builder.setStringV(kvEntry.getStrValue().get()); - break; - case JSON: - builder.setType(TransportProtos.KeyValueType.JSON_V); - builder.setJsonV(kvEntry.getJsonValue().get()); - break; + case BOOLEAN -> kvEntry.getBooleanValue().ifPresent(builder::setBoolV); + case LONG -> kvEntry.getLongValue().ifPresent(builder::setLongV); + case DOUBLE -> kvEntry.getDoubleValue().ifPresent(builder::setDoubleV); + case JSON -> kvEntry.getJsonValue().ifPresent(builder::setJsonV); + case STRING -> kvEntry.getStrValue().ifPresent(builder::setStringV); } return builder.build(); } - public static TransportProtos.TsKvProto.Builder toKeyValueProto(long ts, KvEntry attr) { - TransportProtos.KeyValueProto.Builder dataBuilder = TransportProtos.KeyValueProto.newBuilder(); - dataBuilder.setKey(attr.getKey()); - dataBuilder.setType(TransportProtos.KeyValueType.forNumber(attr.getDataType().ordinal())); - switch (attr.getDataType()) { - case BOOLEAN: - attr.getBooleanValue().ifPresent(dataBuilder::setBoolV); - break; - case LONG: - attr.getLongValue().ifPresent(dataBuilder::setLongV); - break; - case DOUBLE: - attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV); - break; - case JSON: - attr.getJsonValue().ifPresent(dataBuilder::setJsonV); - break; - case STRING: - attr.getStrValue().ifPresent(dataBuilder::setStringV); - break; - } - return TransportProtos.TsKvProto.newBuilder().setTs(ts).setKv(dataBuilder); + public static KvEntry fromTsKvProto(TransportProtos.KeyValueProto proto) { + return switch (fromKeyValueTypeProto(proto.getType())) { + case BOOLEAN -> new BooleanDataEntry(proto.getKey(), proto.getBoolV()); + case LONG -> new LongDataEntry(proto.getKey(), proto.getLongV()); + case DOUBLE -> new DoubleDataEntry(proto.getKey(), proto.getDoubleV()); + case STRING -> new StringDataEntry(proto.getKey(), proto.getStringV()); + case JSON -> new JsonDataEntry(proto.getKey(), proto.getJsonV()); + }; } - public static List toTsKvEntityList(List dataList) { - List result = new ArrayList<>(dataList.size()); - dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), getKvEntry(proto.getKv())))); - return result; + public static TransportProtos.TsKvProto.Builder toTsKvProtoBuilder(long ts, KvEntry kvEntry) { + return TransportProtos.TsKvProto.newBuilder().setTs(ts).setKv(KvProtoUtil.toKeyValueTypeProto(kvEntry)); } - public static List toAttributeKvList(List dataList) { - List result = new ArrayList<>(dataList.size()); - dataList.forEach(proto -> result.add(new BaseAttributeKvEntry(getKvEntry(proto.getKv()), proto.getTs()))); + public static List fromTsValueProtoList(String key, List dataList) { + List result = new ArrayList<>(dataList.size()); + dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), fromTsValueProto(key, proto)))); return result; } - private static KvEntry getKvEntry(TransportProtos.KeyValueProto proto) { - KvEntry entry = null; - DataType type = DataType.values()[proto.getType().getNumber()]; - switch (type) { - case BOOLEAN: - entry = new BooleanDataEntry(proto.getKey(), proto.getBoolV()); - break; - case LONG: - entry = new LongDataEntry(proto.getKey(), proto.getLongV()); - break; - case DOUBLE: - entry = new DoubleDataEntry(proto.getKey(), proto.getDoubleV()); - break; - case STRING: - entry = new StringDataEntry(proto.getKey(), proto.getStringV()); - break; - case JSON: - entry = new JsonDataEntry(proto.getKey(), proto.getJsonV()); - break; + public static TransportProtos.TsValueProto toTsValueProto(long ts, KvEntry attr) { + TransportProtos.TsValueProto.Builder dataBuilder = TransportProtos.TsValueProto.newBuilder(); + dataBuilder.setTs(ts); + dataBuilder.setType(toKeyValueTypeProto(attr.getDataType())); + switch (attr.getDataType()) { + case BOOLEAN -> attr.getBooleanValue().ifPresent(dataBuilder::setBoolV); + case LONG -> attr.getLongValue().ifPresent(dataBuilder::setLongV); + case DOUBLE -> attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV); + case JSON -> attr.getJsonValue().ifPresent(dataBuilder::setJsonV); + case STRING -> attr.getStrValue().ifPresent(dataBuilder::setStringV); } - return entry; + return dataBuilder.build(); } + + public static KvEntry fromTsValueProto(String key, TransportProtos.TsValueProto proto) { + return switch (fromKeyValueTypeProto(proto.getType())) { + case BOOLEAN -> new BooleanDataEntry(key, proto.getBoolV()); + case LONG -> new LongDataEntry(key, proto.getLongV()); + case DOUBLE -> new DoubleDataEntry(key, proto.getDoubleV()); + case STRING -> new StringDataEntry(key, proto.getStringV()); + case JSON -> new JsonDataEntry(key, proto.getJsonV()); + }; + } + + public static TransportProtos.KeyValueType toKeyValueTypeProto(DataType dataType) { + return TransportProtos.KeyValueType.forNumber(dataType.getProtoNumber()); + } + + public static DataType fromKeyValueTypeProto(TransportProtos.KeyValueType keyValueType) { + return dataTypeByProtoNumber[keyValueType.getNumber()]; + } + } diff --git a/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java b/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java index ca6e826cac..38f84bb4a1 100644 --- a/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java +++ b/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java @@ -434,36 +434,28 @@ public class ProtoUtils { } public static TransportProtos.ToDeviceActorNotificationMsgProto toProto(ToDeviceActorNotificationMsg msg) { - if (msg instanceof DeviceEdgeUpdateMsg) { - DeviceEdgeUpdateMsg updateMsg = (DeviceEdgeUpdateMsg) msg; + if (msg instanceof DeviceEdgeUpdateMsg updateMsg) { TransportProtos.DeviceEdgeUpdateMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceEdgeUpdateMsg(proto).build(); - } else if (msg instanceof DeviceNameOrTypeUpdateMsg) { - DeviceNameOrTypeUpdateMsg updateMsg = (DeviceNameOrTypeUpdateMsg) msg; + } else if (msg instanceof DeviceNameOrTypeUpdateMsg updateMsg) { TransportProtos.DeviceNameOrTypeUpdateMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceNameOrTypeMsg(proto).build(); - } else if (msg instanceof DeviceAttributesEventNotificationMsg) { - DeviceAttributesEventNotificationMsg updateMsg = (DeviceAttributesEventNotificationMsg) msg; + } else if (msg instanceof DeviceAttributesEventNotificationMsg updateMsg) { TransportProtos.DeviceAttributesEventMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceAttributesEventMsg(proto).build(); - } else if (msg instanceof DeviceCredentialsUpdateNotificationMsg) { - DeviceCredentialsUpdateNotificationMsg updateMsg = (DeviceCredentialsUpdateNotificationMsg) msg; + } else if (msg instanceof DeviceCredentialsUpdateNotificationMsg updateMsg) { TransportProtos.DeviceCredentialsUpdateMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceCredentialsUpdateMsg(proto).build(); - } else if (msg instanceof ToDeviceRpcRequestActorMsg) { - ToDeviceRpcRequestActorMsg updateMsg = (ToDeviceRpcRequestActorMsg) msg; + } else if (msg instanceof ToDeviceRpcRequestActorMsg updateMsg) { TransportProtos.ToDeviceRpcRequestActorMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setToDeviceRpcRequestMsg(proto).build(); - } else if (msg instanceof FromDeviceRpcResponseActorMsg) { - FromDeviceRpcResponseActorMsg updateMsg = (FromDeviceRpcResponseActorMsg) msg; + } else if (msg instanceof FromDeviceRpcResponseActorMsg updateMsg) { TransportProtos.FromDeviceRpcResponseActorMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setFromDeviceRpcResponseMsg(proto).build(); - } else if (msg instanceof RemoveRpcActorMsg) { - RemoveRpcActorMsg updateMsg = (RemoveRpcActorMsg) msg; + } else if (msg instanceof RemoveRpcActorMsg updateMsg) { TransportProtos.RemoveRpcActorMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setRemoveRpcActorMsg(proto).build(); - } else if (msg instanceof DeviceDeleteMsg) { - DeviceDeleteMsg updateMsg = (DeviceDeleteMsg) msg; + } else if (msg instanceof DeviceDeleteMsg updateMsg) { TransportProtos.DeviceDeleteMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceDeleteMsg(proto).build(); } @@ -507,24 +499,14 @@ public class ProtoUtils { List result = new ArrayList<>(); for (TransportProtos.AttributeValueProto kvEntry : valuesList) { boolean hasValue = kvEntry.getHasV(); - KvEntry entry = null; - switch (kvEntry.getType()) { - case BOOLEAN_V: - entry = new BooleanDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getBoolV() : null); - break; - case LONG_V: - entry = new LongDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getLongV() : null); - break; - case DOUBLE_V: - entry = new DoubleDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getDoubleV() : null); - break; - case STRING_V: - entry = new StringDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getStringV() : null); - break; - case JSON_V: - entry = new JsonDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getJsonV() : null); - break; - } + KvEntry entry = switch (kvEntry.getType()) { + case BOOLEAN_V -> new BooleanDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getBoolV() : null); + case LONG_V -> new LongDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getLongV() : null); + case DOUBLE_V -> new DoubleDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getDoubleV() : null); + case STRING_V -> new StringDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getStringV() : null); + case JSON_V -> new JsonDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getJsonV() : null); + default -> null; + }; result.add(new BaseAttributeKvEntry(kvEntry.getLastUpdateTs(), entry)); } return result; @@ -1029,15 +1011,11 @@ public class ProtoUtils { .setDeviceProfileIdLSB(device.getDeviceProfileId().getId().getLeastSignificantBits()) .setAdditionalInfo(JacksonUtil.toString(device.getAdditionalInfo())); - PowerSavingConfiguration psmConfiguration = null; - switch (device.getDeviceData().getTransportConfiguration().getType()) { - case LWM2M: - psmConfiguration = (Lwm2mDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration(); - break; - case COAP: - psmConfiguration = (CoapDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration(); - break; - } + PowerSavingConfiguration psmConfiguration = switch (device.getDeviceData().getTransportConfiguration().getType()) { + case LWM2M -> (Lwm2mDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration(); + case COAP -> (CoapDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration(); + default -> null; + }; if (psmConfiguration != null) { PowerMode powerMode = psmConfiguration.getPowerMode(); @@ -1079,4 +1057,5 @@ public class ProtoUtils { private static Long checkLong(Long l) { return isNotNull(l) ? l : 0; } + } diff --git a/common/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto index 6023496f57..7b2aa2fe1b 100644 --- a/common/proto/src/main/proto/queue.proto +++ b/common/proto/src/main/proto/queue.proto @@ -54,6 +54,7 @@ enum EntityTypeProto { NOTIFICATION_REQUEST = 31; NOTIFICATION = 32; NOTIFICATION_RULE = 33; + QUEUE_STATS = 34; } /** diff --git a/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java b/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java new file mode 100644 index 0000000000..72d71c7c93 --- /dev/null +++ b/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java @@ -0,0 +1,118 @@ +/** + * 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.common.util; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.thingsboard.server.common.data.kv.AggTsKvEntry; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.BooleanDataEntry; +import org.thingsboard.server.common.data.kv.DataType; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class KvProtoUtilTest { + + private static final long TS = System.currentTimeMillis(); + + private static Stream kvEntryData() { + String key = "key"; + return Stream.of( + new BooleanDataEntry(key, true), + new LongDataEntry(key, 23L), + new DoubleDataEntry(key, 23.0), + new StringDataEntry(key, "stringValue"), + new JsonDataEntry(key, "jsonValue") + ); + } + + private static Stream basicTsKvEntryData() { + return kvEntryData().map(kvEntry -> new BasicTsKvEntry(TS, kvEntry)); + } + + private static Stream> attributeKvEntryData() { + return Stream.of(kvEntryData().map(kvEntry -> new BaseAttributeKvEntry(TS, kvEntry)).toList()); + } + + private static List createTsKvEntryList(boolean withAggregation) { + return kvEntryData().map(kvEntry -> { + if (withAggregation) { + return new AggTsKvEntry(TS, kvEntry, 0); + } else { + return new BasicTsKvEntry(TS, kvEntry); + } + }).collect(Collectors.toList()); + } + + @ParameterizedTest + @EnumSource(DataType.class) + void protoDataTypeSerialization(DataType dataType) { + assertThat(KvProtoUtil.fromKeyValueTypeProto(KvProtoUtil.toKeyValueTypeProto(dataType))) + .as(dataType.name()).isEqualTo(dataType); + } + + @ParameterizedTest + @MethodSource("kvEntryData") + void protoKeyValueProtoSerialization(KvEntry kvEntry) { + assertThat(KvProtoUtil.fromTsKvProto(KvProtoUtil.toKeyValueTypeProto(kvEntry))) + .as("deserialized").isEqualTo(kvEntry); + } + + @ParameterizedTest + @MethodSource("basicTsKvEntryData") + void protoTsKvEntrySerialization(KvEntry kvEntry) { + assertThat(KvProtoUtil.fromTsKvProto(KvProtoUtil.toTsKvProto(TS, kvEntry))) + .as("deserialized").isEqualTo(kvEntry); + } + + @ParameterizedTest + @MethodSource("kvEntryData") + void protoTsValueSerialization(KvEntry kvEntry) { + assertThat(KvProtoUtil.fromTsValueProto(kvEntry.getKey(), KvProtoUtil.toTsValueProto(TS, kvEntry))) + .as("deserialized").isEqualTo(kvEntry); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void protoListTsKvEntrySerialization(boolean withAggregation) { + List tsKvEntries = createTsKvEntryList(withAggregation); + assertThat(KvProtoUtil.fromTsKvProtoList(KvProtoUtil.toTsKvProtoList(tsKvEntries))) + .as("deserialized").isEqualTo(tsKvEntries); + } + + @ParameterizedTest + @MethodSource("attributeKvEntryData") + void protoListAttributeKvSerialization(List attributeKvEntries) { + assertThat(KvProtoUtil.toAttributeKvList(KvProtoUtil.attrToTsKvProtos(attributeKvEntries))) + .as("deserialized") + .isEqualTo(attributeKvEntries); + } + +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java index f5284b5a08..a3734daa91 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java @@ -110,40 +110,40 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, ruleEngineSettings.getTopic()); + return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, ruleEngineSettings.getTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbPubSubConsumerTemplate<>(vcAdmin, pubSubSettings, vcSettings.getTopic(), + return new TbPubSubConsumerTemplate<>(vcAdmin, pubSubSettings, topicService.buildTopicName(vcSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) ); } @Override public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbPubSubConsumerTemplate<>(ruleEngineAdmin, pubSubSettings, configuration.getTopic(), + return new TbPubSubConsumerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(configuration.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); } @@ -156,7 +156,7 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(), + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); } @@ -169,13 +169,13 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getRequestsTopic(), + return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createTransportApiResponseProducer() { - return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getResponsesTopic()); + return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); } @Override @@ -203,29 +203,29 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(), + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueProducer> createVersionControlMsgProducer() { - return new TbPubSubProducerTemplate<>(vcAdmin, pubSubSettings, vcSettings.getTopic()); + return new TbPubSubProducerTemplate<>(vcAdmin, pubSubSettings, topicService.buildTopicName(vcSettings.getTopic())); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java index 556a30b269..c48559467d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java @@ -101,32 +101,32 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, ruleEngineSettings.getTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(), + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); } @@ -139,13 +139,13 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getRequestsTopic(), + return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createTransportApiResponseProducer() { - return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getResponsesTopic()); + return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); } @Override @@ -173,24 +173,24 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(), + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java index f88625f7fd..32e209b2ea 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java @@ -145,14 +145,14 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService log.trace("[{}] InvokeScript uuid {} with timeout {}ms", tenantId, scriptId, getMaxInvokeRequestsTimeout()); var task = doInvokeFunction(scriptId, args); - var resultFuture = Futures.transformAsync(task.getResultFuture(), output -> { + var resultFuture = Futures.transform(task.getResultFuture(), output -> { String result = JacksonUtil.toString(output); if (resultSizeExceeded(result)) { throw new TbScriptException(scriptId, TbScriptException.ErrorCode.OTHER, null, new RuntimeException( format("Script invocation result exceeds maximum allowed size of %s symbols", getMaxResultSize()) )); } - return Futures.immediateFuture(output); + return output; }, MoreExecutors.directExecutor()); return withTimeoutAndStatsCallback(scriptId, task, resultFuture, invokeCallback, getMaxInvokeRequestsTimeout()); diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/js/NashornJsInvokeService.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/js/NashornJsInvokeService.java index 0e37bd89d6..dc83b08af4 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/js/NashornJsInvokeService.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/js/NashornJsInvokeService.java @@ -41,7 +41,6 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.locks.ReentrantLock; @Slf4j @@ -62,9 +61,12 @@ public class NashornJsInvokeService extends AbstractJsInvokeService { @Value("${js.local.monitor_thread_pool_size}") private int monitorThreadPoolSize; - @Value("${js.local.max_cpu_time}") + @Value("${js.local.max_cpu_time:8000}") // 8 seconds private long maxCpuTime; + @Value("${js.local.max_memory:104857600}") // 100 MiB + private long maxMemory; + @Getter @Value("${js.local.max_errors}") private int maxErrors; @@ -107,12 +109,13 @@ public class NashornJsInvokeService extends AbstractJsInvokeService { @Override public void init() { super.init(); - jsExecutor = MoreExecutors.listeningDecorator(Executors.newWorkStealingPool(jsExecutorThreadPoolSize)); + jsExecutor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(jsExecutorThreadPoolSize, "nashorn-js-executor")); if (useJsSandbox) { sandbox = NashornSandboxes.create(); monitorExecutorService = ThingsBoardExecutors.newWorkStealingPool(monitorThreadPoolSize, "nashorn-js-monitor"); sandbox.setExecutor(monitorExecutorService); sandbox.setMaxCPUTime(maxCpuTime); + sandbox.setMaxMemory(maxMemory); sandbox.allowNoBraces(false); sandbox.allowLoadFunctions(true); sandbox.setMaxPreparedStatements(30); diff --git a/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultStatsFactory.java b/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultStatsFactory.java index dbe3fbcc4e..be0c4fe19f 100644 --- a/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultStatsFactory.java +++ b/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultStatsFactory.java @@ -19,16 +19,13 @@ import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.Timer; -import jakarta.annotation.PostConstruct; +import org.apache.commons.lang3.ArrayUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.StringUtils; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import jakarta.annotation.PostConstruct; import java.util.concurrent.atomic.AtomicInteger; @Service @@ -65,26 +62,17 @@ public class DefaultStatsFactory implements StatsFactory { @Override - public StatsCounter createStatsCounter(String key, String statsName) { - return createStatsCounter(key, statsName, Collections.emptyMap()); - } - - @Override - public StatsCounter createStatsCounter(String key, String statsName, Map tags) { - String[] tagsArr = new String[]{STATS_NAME_TAG, statsName}; - if (!tags.isEmpty()) { - List tagsList = new ArrayList<>(List.of(tagsArr)); - tags.forEach((name, value) -> { - tagsList.add(name); - tagsList.add(value); - }); - tagsArr = tagsList.toArray(String[]::new); + public StatsCounter createStatsCounter(String key, String statsName, String... otherTags) { + String[] tags = new String[]{STATS_NAME_TAG, statsName}; + if (otherTags.length > 0) { + if (otherTags.length % 2 != 0) { + throw new IllegalArgumentException("Invalid tags array size"); + } + tags = ArrayUtils.addAll(tags, otherTags); } return new StatsCounter( new AtomicInteger(0), - metricsEnabled ? - meterRegistry.counter(key, tagsArr) - : STUB_COUNTER, + metricsEnabled ? meterRegistry.counter(key, tags) : STUB_COUNTER, statsName ); } diff --git a/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsFactory.java b/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsFactory.java index 90a4191ee2..b9bf0b97fb 100644 --- a/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsFactory.java +++ b/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsFactory.java @@ -17,13 +17,9 @@ package org.thingsboard.server.common.stats; import io.micrometer.core.instrument.Timer; -import java.util.Map; - public interface StatsFactory { - StatsCounter createStatsCounter(String key, String statsName); - - StatsCounter createStatsCounter(String key, String statsName, Map tags); + StatsCounter createStatsCounter(String key, String statsName, String... otherTags); DefaultCounter createDefaultCounter(String key, String... tags); @@ -32,4 +28,5 @@ public interface StatsFactory { MessagesStats createMessagesStats(String key); Timer createTimer(String key, String... tags); + } 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 5e2d916397..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; @@ -137,8 +138,7 @@ public class DeviceApiController implements TbTransportService { + MARKDOWN_CODE_BLOCK_START + ATTRIBUTE_PAYLOAD_EXAMPLE + MARKDOWN_CODE_BLOCK_END - + REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/attributes", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public DeferredResult getDeviceAttributes( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @@ -174,13 +174,12 @@ public class DeviceApiController implements TbTransportService { + MARKDOWN_CODE_BLOCK_START + ATTRIBUTE_PAYLOAD_EXAMPLE + MARKDOWN_CODE_BLOCK_END - + REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/attributes", method = RequestMethod.POST) public DeferredResult postDeviceAttributes( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @PathVariable("deviceToken") String deviceToken, - @Parameter(description = "JSON with attribute key-value pairs. See API call description for example.") + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "JSON with attribute key-value pairs. See API call description for example.") @RequestBody String json) { DeferredResult responseWriter = new DeferredResult<>(); transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), @@ -196,8 +195,7 @@ public class DeviceApiController implements TbTransportService { description = "Post time-series data on behalf of device. " + "\n Example of the request: " + TS_PAYLOAD - + REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/telemetry", method = RequestMethod.POST) public DeferredResult postTelemetry( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @@ -222,8 +220,7 @@ public class DeviceApiController implements TbTransportService { + MARKDOWN_CODE_BLOCK_END + "Note: both 'secretKey' and 'durationMs' is optional parameters. " + "In case the secretKey is not specified, the empty string as a default value is used. In case the durationMs is not specified, the system parameter device.claim.duration is used.\n\n" - + REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/claim", method = RequestMethod.POST) public DeferredResult claimDevice( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @@ -244,8 +241,7 @@ public class DeviceApiController implements TbTransportService { description = "Subscribes to RPC commands using http long polling. " + "Deprecated, since long polling is resource and network consuming. " + "Consider using MQTT or CoAP protocol for light-weight real-time updates. \n\n" + - REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/rpc", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public DeferredResult subscribeToCommands( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @@ -268,15 +264,14 @@ public class DeviceApiController implements TbTransportService { @Operation(summary = "Reply to RPC commands (replyToCommand)", description = "Replies to server originated RPC command identified by 'requestId' parameter. The response is arbitrary JSON.\n\n" + - REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/rpc/{requestId}", method = RequestMethod.POST) public DeferredResult replyToCommand( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @PathVariable("deviceToken") String deviceToken, @Parameter(description = "RPC request id from the incoming RPC request", required = true , schema = @Schema(defaultValue = "123")) @PathVariable("requestId") Integer requestId, - @Parameter(description = "Reply to the RPC request, JSON. For example: {\"status\":\"success\"}", required = true) + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Reply to the RPC request, JSON. For example: {\"status\":\"success\"}", required = true) @RequestBody String json) { DeferredResult responseWriter = new DeferredResult(); transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), @@ -296,13 +291,12 @@ public class DeviceApiController implements TbTransportService { MARKDOWN_CODE_BLOCK_START + "{\"result\": 4}" + MARKDOWN_CODE_BLOCK_END + - REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/rpc", method = RequestMethod.POST) public DeferredResult postRpcRequest( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @PathVariable("deviceToken") String deviceToken, - @Parameter(description = "The RPC request JSON", required = true) + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "The RPC request JSON", required = true) @RequestBody String json) { DeferredResult responseWriter = new DeferredResult(); transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), @@ -324,8 +318,7 @@ public class DeviceApiController implements TbTransportService { description = "Subscribes to client and shared scope attribute updates using http long polling. " + "Deprecated, since long polling is resource and network consuming. " + "Consider using MQTT or CoAP protocol for light-weight real-time updates. \n\n" + - REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/attributes/updates", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public DeferredResult subscribeToAttributes( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @@ -356,8 +349,7 @@ public class DeviceApiController implements TbTransportService { "Optional 'chunk' and 'size' parameters may be used to download the firmware in chunks. " + "For example, device may request first 16 KB of firmware using 'chunk'=0 and 'size'=16384. " + "Next 16KB using 'chunk'=1 and 'size'=16384. The last chunk should have less bytes then requested using 'size' parameter. \n\n" + - REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/firmware", method = RequestMethod.GET) public DeferredResult getFirmware( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @@ -383,8 +375,7 @@ public class DeviceApiController implements TbTransportService { "Optional 'chunk' and 'size' parameters may be used to download the software in chunks. " + "For example, device may request first 16 KB of software using 'chunk'=0 and 'size'=16384. " + "Next 16KB using 'chunk'=1 and 'size'=16384. The last chunk should have less bytes then requested using 'size' parameter. \n\n" + - REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/software", method = RequestMethod.GET) public DeferredResult getSoftware( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @@ -418,12 +409,10 @@ public class DeviceApiController implements TbTransportService { " \"credentialsType\":\"ACCESS_TOKEN\",\n" + " \"credentialsValue\":\"DEVICE_ACCESS_TOKEN\",\n" + " \"status\":\"SUCCESS\"\n" + - "}" + MARKDOWN_CODE_BLOCK_END - , - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "}" + MARKDOWN_CODE_BLOCK_END) @RequestMapping(value = "/provision", method = RequestMethod.POST) public DeferredResult provisionDevice( - @Parameter(description = "JSON with provision request. See API call description for example.") + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "JSON with provision request. See API call description for example.") @RequestBody String json) { DeferredResult responseWriter = new DeferredResult<>(); transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json), @@ -441,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()) { @@ -468,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)); @@ -487,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) { @@ -513,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()) @@ -528,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" /> + + + + + + diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 29df8f7f65..cb391b27e9 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -229,7 +229,7 @@ public class LwM2mClient { this.resources.get(pathRezIdVer).updateLwM2mResource(resource, mode); return true; } else { - LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRezIdVer)); + LwM2mPath pathIds = getLwM2mPathFromString(pathRezIdVer); ResourceModel resourceModel = modelProvider.getObjectModel(registration).getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()); if (resourceModel != null) { this.resources.put(pathRezIdVer, new ResourceValue(resource, resourceModel)); @@ -257,7 +257,7 @@ public class LwM2mClient { } public String getRezIdByResourceNameAndObjectInstanceId(String resourceName, String pathObjectInstanceIdVer, LwM2mModelProvider modelProvider) { - LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathObjectInstanceIdVer)); + LwM2mPath pathIds = getLwM2mPathFromString(pathObjectInstanceIdVer); if (pathIds.isObjectInstance()) { Set rezIds = modelProvider.getObjectModel(registration) .getObjectModel(pathIds.getObjectId()).resources.entrySet() @@ -271,7 +271,7 @@ public class LwM2mClient { } public ResourceModel getResourceModel(String pathIdVer, LwM2mModelProvider modelProvider) { - LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); + LwM2mPath pathIds = getLwM2mPathFromString(pathIdVer); String verSupportedObject = String.valueOf(registration.getSupportedObject().get(pathIds.getObjectId())); String verRez = getVerFromPathIdVerOrId(pathIdVer); return verRez != null && verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration) @@ -289,7 +289,7 @@ public class LwM2mClient { public ObjectModel getObjectModel(String pathIdVer, LwM2mModelProvider modelProvider) { try { - LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); + LwM2mPath pathIds = getLwM2mPathFromString(pathIdVer); String verSupportedObject = String.valueOf(registration.getSupportedObject().get(pathIds.getObjectId())); String verRez = getVerFromPathIdVerOrId(pathIdVer); return verRez != null && verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration) @@ -309,7 +309,7 @@ public class LwM2mClient { public Collection getNewResourceForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider, LwM2mValueConverter converter) { - LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRezIdVer)); + LwM2mPath pathIds = getLwM2mPathFromString(pathRezIdVer); Collection resources = ConcurrentHashMap.newKeySet(); Map resourceModels = modelProvider.getObjectModel(registration) .getObjectModel(pathIds.getObjectId()).resources; @@ -329,7 +329,7 @@ public class LwM2mClient { */ public Collection getNewResourcesForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider, LwM2mValueConverter converter) { - LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRezIdVer)); + LwM2mPath pathIds = getLwM2mPathFromString(pathRezIdVer); Collection resources = ConcurrentHashMap.newKeySet(); Map resourceModels = modelProvider.getObjectModel(registration) .getObjectModel(pathIds.getObjectId()).resources; @@ -370,7 +370,7 @@ public class LwM2mClient { } public String isValidObjectVersion(String path) { - LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(path)); + LwM2mPath pathIds = getLwM2mPathFromString(path); LwM2m.Version verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); if (verSupportedObject == null) { return String.format("Specified object id %s absent in the list supported objects of the client or is security object!", pathIds.getObjectId()); @@ -390,7 +390,7 @@ public class LwM2mClient { public void deleteResources(String pathIdVer, LwM2mModelProvider modelProvider) { Set key = getKeysEqualsIdVer(pathIdVer); key.forEach(pathRez -> { - LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRez)); + LwM2mPath pathIds = getLwM2mPathFromString(pathRez); ResourceModel resourceModel = modelProvider.getObjectModel(registration).getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()); if (resourceModel != null) { this.resources.get(pathRez).setResourceModel(resourceModel); @@ -410,7 +410,7 @@ public class LwM2mClient { } private void saveResourceModel(String pathRez, LwM2mModelProvider modelProvider) { - LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRez)); + LwM2mPath pathIds = getLwM2mPathFromString(pathRez); ResourceModel resourceModel = modelProvider.getObjectModel(registration).getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()); this.resources.get(pathRez).setResourceModel(resourceModel); } @@ -456,5 +456,9 @@ public class LwM2mClient { return result; } + public LwM2mPath getLwM2mPathFromString(String path) { + return new LwM2mPath(fromVersionedIdToObjectId(path)); + } + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java index 30800eb9ee..17fef8acb0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java @@ -15,8 +15,12 @@ */ package org.thingsboard.server.transport.lwm2m.server.store.util; -import com.fasterxml.jackson.annotation.JsonValue; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import com.google.protobuf.util.JsonFormat; import lombok.SneakyThrows; import org.eclipse.leshan.core.model.ResourceModel; @@ -25,23 +29,35 @@ import org.eclipse.leshan.core.node.LwM2mNodeException; import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.ObjectLink; +import org.eclipse.leshan.core.util.datatype.ULong; import org.eclipse.leshan.server.redis.serialization.RegistrationSerDes; +import org.thingsboard.server.common.data.device.data.PowerMode; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import java.lang.reflect.Field; +import java.time.Instant; import java.util.Base64; import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; + +import static org.thingsboard.common.util.JacksonUtil.toJsonNode; public class LwM2MClientSerDes { public static final String VALUE = "value"; + private static final RegistrationSerDes registrationSerDes = new RegistrationSerDes(); @SneakyThrows public static byte[] serialize(LwM2mClient client) { JsonObject o = new JsonObject(); - o.addProperty("nodeId", "client.getNodeId()"); + o.addProperty("nodeId", client.getNodeId()); o.addProperty("endpoint", client.getEndpoint()); JsonObject resources = new JsonObject(); @@ -92,8 +108,15 @@ public class LwM2MClientSerDes { o.addProperty("pagingTransmissionWindow", client.getPagingTransmissionWindow()); } if (client.getRegistration() != null) { - RegistrationSerDes regDez = new RegistrationSerDes(); - o.addProperty("registration", regDez.jSerialize(client.getRegistration()).toString()); + String registrationAddress = client.getRegistration().getAddress().toString(); + JsonNode registrationNode = registrationSerDes.jSerialize(client.getRegistration()); + if (!registrationAddress.equals(registrationNode.get("transportdata").get("address").asText())){ + ObjectNode actualRegAddress = (ObjectNode)registrationNode.get("transportdata"); + actualRegAddress.put("address", registrationAddress); + ObjectNode actualIdentity = (ObjectNode) actualRegAddress.get("identity"); + actualIdentity.put("address", registrationAddress); + } + o.addProperty("registration", registrationNode.toString()); } o.addProperty("asleep", client.isAsleep()); o.addProperty("lastUplinkTime", client.getLastUplinkTime()); @@ -138,33 +161,35 @@ public class LwM2MClientSerDes { ResourceModel.Type type = ResourceModel.Type.valueOf(o.get("type").getAsString()); if (multiInstances) { Map instances = new HashMap<>(); - o.get("instances").getAsJsonArray().forEach(entry -> { -// instances.put(Integer.valueOf(entry.getAsJsonObject().), parseValue(type, entry.getValue())); - }); + for (Entry entry : o.get("instances").getAsJsonObject().entrySet()) { + JsonObject instance = entry.getValue().getAsJsonObject(); + instances.put(Integer.valueOf(instance.get("id").getAsString()), parseValue(type, instance.get(VALUE))); + } return LwM2mMultipleResource.newResource(id, instances, type); } else { - return LwM2mSingleResource.newResource(id, parseValue(type, (JsonValue) o.get(VALUE))); + return LwM2mSingleResource.newResource(id, parseValue(type, o.get(VALUE))); } } - private static Object parseValue(ResourceModel.Type type, JsonValue value) { + + private static Object parseValue(ResourceModel.Type type, JsonElement value) { switch (type) { case INTEGER: - return value.value(); -// case FLOAT: -// return value.value(); -// case BOOLEAN: -// return value.asBoolean(); -// case OPAQUE: -// return Base64.getDecoder().decode(value.asString()); -// case STRING: -// return value.asString(); -// case TIME: -// return new Date(value.asLong()); -// case OBJLNK: -// return ObjectLink.decodeFromString(value.asString()); -// case UNSIGNED_INTEGER: -// return ULong.valueOf(value.asString()); + return value.getAsInt(); + case FLOAT: + return value.getAsDouble(); + case BOOLEAN: + return value.getAsBoolean(); + case OPAQUE: + return Base64.getDecoder().decode(value.getAsString()); + case STRING: + return value.getAsString(); + case TIME: + return Instant.ofEpochMilli(value.getAsLong()); + case OBJLNK: + return ObjectLink.decodeFromString(value.getAsString()); + case UNSIGNED_INTEGER: + return ULong.valueOf(value.getAsString()); default: throw new LwM2mNodeException(String.format("Type %s is not supported", type.name())); } @@ -212,7 +237,7 @@ public class LwM2MClientSerDes { o.addProperty(VALUE, Base64.getEncoder().encodeToString((byte[]) value)); break; case STRING: - o.addProperty(VALUE, (String) value); + o.addProperty(VALUE, String.valueOf(value)); break; case TIME: o.addProperty(VALUE, ((Date) value).getTime()); @@ -221,7 +246,7 @@ public class LwM2MClientSerDes { o.addProperty(VALUE, ((ObjectLink) value).encodeToString()); break; case UNSIGNED_INTEGER: - o.addProperty(VALUE, value.toString()); + o.addProperty(VALUE, Integer.toUnsignedString((int)value)); break; default: throw new LwM2mNodeException(String.format("Type %s is not supported", type.name())); @@ -230,111 +255,111 @@ public class LwM2MClientSerDes { @SneakyThrows public static LwM2mClient deserialize(byte[] data) { -// JsonObject o = new JsonObject(new String(data))); -// LwM2mClient lwM2mClient = new LwM2mClient(o.get("nodeId").getAsString(), o.get("endpoint").getAsString()); - LwM2mClient lwM2mClient = new LwM2mClient("nodeId", "endpoint"); -// o.get("resources").getAsJsonObject().forEach(entry -> { -// JsonObject resource = entry.getValue().asObject(); -// LwM2mResource lwM2mResource = parseLwM2mResource(resource.get("lwM2mResource").getAsJsonObject()); -// ResourceModel resourceModel = parseResourceModel(resource.get("resourceModel").asObject()); -// ResourceValue resourceValue = new ResourceValue(lwM2mResource, resourceModel); -// lwM2mClient.getResources().put(entry.getName(), resourceValue); -// }); -// -// for (JsonObject.Member entry : o.get("sharedAttributes").asObject()) { -// TransportProtos.TsKvProto.Builder builder = TransportProtos.TsKvProto.newBuilder(); -// JsonFormat.parser().merge(entry.getValue().getAsString(), builder); -// lwM2mClient.getSharedAttributes().put(entry.getName(), builder.build()); -// } -// -// o.get("keyTsLatestMap").asObject().forEach(entry -> { -// lwM2mClient.getKeyTsLatestMap().put(entry.getName(), new AtomicLong(entry.getValue().asLong())); -// }); -// -// lwM2mClient.setState(LwM2MClientState.valueOf(o.get("state").getAsString())); -// -// Class lwM2mClientClass = LwM2mClient.class; -// -// JsonValue session = o.get("session"); -// if (session != null) { -// TransportProtos.SessionInfoProto.Builder builder = TransportProtos.SessionInfoProto.newBuilder(); -// JsonFormat.parser().merge(session.asString(), builder); -// -// Field sessionField = lwM2mClientClass.getDeclaredField("session"); -// sessionField.setAccessible(true); -// sessionField.set(lwM2mClient, builder.build()); -// } -// -// JsonValue tenantId = o.get("tenantId"); -// if (tenantId != null) { -// Field tenantIdField = lwM2mClientClass.getDeclaredField("tenantId"); -// tenantIdField.setAccessible(true); -// tenantIdField.set(lwM2mClient, new TenantId(UUID.fromString(tenantId.asString()))); -// } -// -// JsonValue deviceId = o.get("deviceId"); -// if (tenantId != null) { -// Field deviceIdField = lwM2mClientClass.getDeclaredField("deviceId"); -// deviceIdField.setAccessible(true); -// deviceIdField.set(lwM2mClient, UUID.fromString(deviceId.asString())); -// } -// -// JsonValue profileId = o.get("profileId"); -// if (tenantId != null) { -// Field profileIdField = lwM2mClientClass.getDeclaredField("profileId"); -// profileIdField.setAccessible(true); -// profileIdField.set(lwM2mClient, UUID.fromString(profileId.asString())); -// } -// -// JsonValue powerMode = o.get("powerMode"); -// if (powerMode != null) { -// Field powerModeField = lwM2mClientClass.getDeclaredField("powerMode"); -// powerModeField.setAccessible(true); -// powerModeField.set(lwM2mClient, PowerMode.valueOf(powerMode.asString())); -// } -// -// JsonValue edrxCycle = o.get("edrxCycle"); -// if (edrxCycle != null) { -// Field edrxCycleField = lwM2mClientClass.getDeclaredField("edrxCycle"); -// edrxCycleField.setAccessible(true); -// edrxCycleField.set(lwM2mClient, edrxCycle.asLong()); -// } -// -// JsonValue psmActivityTimer = o.get("psmActivityTimer"); -// if (psmActivityTimer != null) { -// Field psmActivityTimerField = lwM2mClientClass.getDeclaredField("psmActivityTimer"); -// psmActivityTimerField.setAccessible(true); -// psmActivityTimerField.set(lwM2mClient, psmActivityTimer.asLong()); -// } -// -// JsonValue pagingTransmissionWindow = o.get("pagingTransmissionWindow"); -// if (pagingTransmissionWindow != null) { -// Field pagingTransmissionWindowField = lwM2mClientClass.getDeclaredField("pagingTransmissionWindow"); -// pagingTransmissionWindowField.setAccessible(true); -// pagingTransmissionWindowField.set(lwM2mClient, pagingTransmissionWindow.asLong()); -// } -// -// JsonValue registration = o.get("registration"); -// if (registration != null) { -// lwM2mClient.setRegistration(RegistrationSerDes.deserialize(registration.asObject())); -// } -// -// lwM2mClient.setAsleep(o.get("asleep").getAsBoolean()); -// -// Field lastUplinkTimeField = lwM2mClientClass.getDeclaredField("lastUplinkTime"); -// lastUplinkTimeField.setAccessible(true); -// lastUplinkTimeField.setLong(lwM2mClient, o.get("lastUplinkTime").asLong()); -// -// Field firstEdrxDownlinkField = lwM2mClientClass.getDeclaredField("firstEdrxDownlink"); -// firstEdrxDownlinkField.setAccessible(true); -// firstEdrxDownlinkField.setBoolean(lwM2mClient, o.get("firstEdrxDownlink").getAsBoolean()); -// -// lwM2mClient.getRetryAttempts().set(o.get("retryAttempts").asInt()); -// -// JsonValue lastSentRpcId = o.get("lastSentRpcId"); -// if (lastSentRpcId != null) { -// lwM2mClient.setLastSentRpcId(UUID.fromString(lastSentRpcId.asString())); -// } + JsonObject o = JsonParser.parseString(new String(data)).getAsJsonObject(); + LwM2mClient lwM2mClient = new LwM2mClient(o.get("nodeId").getAsString(), o.get("endpoint").getAsString()); + + o.get("resources").getAsJsonObject().entrySet().forEach(entry -> { + JsonObject resource = entry.getValue().getAsJsonObject(); + LwM2mResource lwM2mResource = parseLwM2mResource(resource.get("lwM2mResource").getAsJsonObject()); + ResourceModel resourceModel = parseResourceModel(resource.get("resourceModel").getAsJsonObject()); + ResourceValue resourceValue = new ResourceValue(lwM2mResource, resourceModel); + lwM2mClient.getResources().put(String.valueOf(lwM2mResource.getId()), resourceValue); + }); + + for (Entry entry : o.get("sharedAttributes").getAsJsonObject().entrySet()) { + TransportProtos.TsKvProto.Builder builder = TransportProtos.TsKvProto.newBuilder(); + JsonFormat.parser().merge(entry.getValue().getAsString(), builder); + lwM2mClient.getSharedAttributes().put(entry.getKey(), builder.build()); + } + + o.get("keyTsLatestMap").getAsJsonObject().entrySet().forEach(entry -> { + lwM2mClient.getKeyTsLatestMap().put(entry.getKey(), new AtomicLong(entry.getValue().getAsLong())); + }); + + lwM2mClient.setState(LwM2MClientState.valueOf(o.get("state").getAsString())); + + Class lwM2mClientClass = LwM2mClient.class; + + JsonElement session = o.get("session"); + if (session != null) { + TransportProtos.SessionInfoProto.Builder builder = TransportProtos.SessionInfoProto.newBuilder(); + JsonFormat.parser().merge(session.getAsString(), builder); + + Field sessionField = lwM2mClientClass.getDeclaredField("session"); + sessionField.setAccessible(true); + sessionField.set(lwM2mClient, builder.build()); + } + + JsonElement tenantId = o.get("tenantId"); + if (tenantId != null) { + Field tenantIdField = lwM2mClientClass.getDeclaredField("tenantId"); + tenantIdField.setAccessible(true); + tenantIdField.set(lwM2mClient, new TenantId(UUID.fromString(tenantId.getAsString()))); + } + + JsonElement deviceId = o.get("deviceId"); + if (tenantId != null) { + Field deviceIdField = lwM2mClientClass.getDeclaredField("deviceId"); + deviceIdField.setAccessible(true); + deviceIdField.set(lwM2mClient, UUID.fromString(deviceId.getAsString())); + } + + JsonElement profileId = o.get("profileId"); + if (tenantId != null) { + Field profileIdField = lwM2mClientClass.getDeclaredField("profileId"); + profileIdField.setAccessible(true); + profileIdField.set(lwM2mClient, UUID.fromString(profileId.getAsString())); + } + + JsonElement powerMode = o.get("powerMode"); + if (powerMode != null) { + Field powerModeField = lwM2mClientClass.getDeclaredField("powerMode"); + powerModeField.setAccessible(true); + powerModeField.set(lwM2mClient, PowerMode.valueOf(powerMode.getAsString())); + } + + JsonElement edrxCycle = o.get("edrxCycle"); + if (edrxCycle != null) { + Field edrxCycleField = lwM2mClientClass.getDeclaredField("edrxCycle"); + edrxCycleField.setAccessible(true); + edrxCycleField.set(lwM2mClient, edrxCycle.getAsLong()); + } + + JsonElement psmActivityTimer = o.get("psmActivityTimer"); + if (psmActivityTimer != null) { + Field psmActivityTimerField = lwM2mClientClass.getDeclaredField("psmActivityTimer"); + psmActivityTimerField.setAccessible(true); + psmActivityTimerField.set(lwM2mClient, psmActivityTimer.getAsLong()); + } + + JsonElement pagingTransmissionWindow = o.get("pagingTransmissionWindow"); + if (pagingTransmissionWindow != null) { + Field pagingTransmissionWindowField = lwM2mClientClass.getDeclaredField("pagingTransmissionWindow"); + pagingTransmissionWindowField.setAccessible(true); + pagingTransmissionWindowField.set(lwM2mClient, pagingTransmissionWindow.getAsLong()); + } + + JsonElement registration = o.get("registration"); + if (registration != null) { + lwM2mClient.setRegistration(registrationSerDes.deserialize(toJsonNode(registration.getAsString()))); + } + + lwM2mClient.setAsleep(o.get("asleep").getAsBoolean()); + + Field lastUplinkTimeField = lwM2mClientClass.getDeclaredField("lastUplinkTime"); + lastUplinkTimeField.setAccessible(true); + lastUplinkTimeField.setLong(lwM2mClient, o.get("lastUplinkTime").getAsLong()); + + Field firstEdrxDownlinkField = lwM2mClientClass.getDeclaredField("firstEdrxDownlink"); + firstEdrxDownlinkField.setAccessible(true); + firstEdrxDownlinkField.setBoolean(lwM2mClient, o.get("firstEdrxDownlink").getAsBoolean()); + + lwM2mClient.getRetryAttempts().set(o.get("retryAttempts").getAsInt()); + + JsonElement lastSentRpcId = o.get("lastSentRpcId"); + if (lastSentRpcId != null) { + lwM2mClient.setLastSentRpcId(UUID.fromString(lastSentRpcId.getAsString())); + } return lwM2mClient; } diff --git a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServiceTest.java b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServiceTest.java deleted file mode 100644 index 9f49f3b138..0000000000 --- a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServiceTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/** - * 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.lwm2m.bootstrap; - -import org.eclipse.californium.core.network.CoapEndpoint; -import org.eclipse.californium.scandium.config.DtlsConnectorConfig; -import org.junit.Ignore; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.test.util.ReflectionTestUtils; -import org.thingsboard.server.common.transport.TransportService; -import org.thingsboard.server.transport.lwm2m.bootstrap.secure.TbLwM2MDtlsBootstrapCertificateVerifier; -import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapSecurityStore; -import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MInMemoryBootstrapConfigStore; -import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig; -import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.when; - -@ExtendWith(MockitoExtension.class) -public class LwM2MTransportBootstrapServiceTest { - - @Mock - private LwM2MTransportServerConfig serverConfig; - @Mock - private LwM2MTransportBootstrapConfig bootstrapConfig; - @Mock - private LwM2MBootstrapSecurityStore lwM2MBootstrapSecurityStore; - @Mock - private LwM2MInMemoryBootstrapConfigStore lwM2MInMemoryBootstrapConfigStore; - @Mock - private TransportService transportService; - @Mock - private TbLwM2MDtlsBootstrapCertificateVerifier certificateVerifier; - - @Disabled // fixme: nick - @Test - public void getLHServer_creates_ConnectionIdGenerator_when_connection_id_length_not_null(){ - final Integer CONNECTION_ID_LENGTH = 6; - when(serverConfig.getDtlsCidLength()).thenReturn(CONNECTION_ID_LENGTH); - var lwM2MBootstrapService = createLwM2MBootstrapService(); - - var server = lwM2MBootstrapService.getLhBootstrapServer(); - var securedEndpoint = (CoapEndpoint) ReflectionTestUtils.getField(server, "securedEndpoint"); - assertThat(securedEndpoint).isNotNull(); - - var config = (DtlsConnectorConfig) ReflectionTestUtils.getField(securedEndpoint.getConnector(), "config"); - assertThat(config).isNotNull(); - assertThat(config.getConnectionIdGenerator()).isNotNull(); - assertThat((Integer) ReflectionTestUtils.getField(config.getConnectionIdGenerator(), "connectionIdLength")) - .isEqualTo(CONNECTION_ID_LENGTH); - } - - @Disabled // fixme: nick - @Test - public void getLHServer_creates_no_ConnectionIdGenerator_when_connection_id_length_is_null(){ - when(serverConfig.getDtlsCidLength()).thenReturn(null); - var lwM2MBootstrapService = createLwM2MBootstrapService(); - - var server = lwM2MBootstrapService.getLhBootstrapServer(); - var securedEndpoint = (CoapEndpoint) ReflectionTestUtils.getField(server, "securedEndpoint"); - assertThat(securedEndpoint).isNotNull(); - - var config = (DtlsConnectorConfig) ReflectionTestUtils.getField(securedEndpoint.getConnector(), "config"); - assertThat(config).isNotNull(); - assertThat(config.getConnectionIdGenerator()).isNull(); - } - - private LwM2MTransportBootstrapService createLwM2MBootstrapService() { - setDefaultConfigVariables(); - return new LwM2MTransportBootstrapService(serverConfig, bootstrapConfig, lwM2MBootstrapSecurityStore, - lwM2MInMemoryBootstrapConfigStore, transportService, certificateVerifier); - } - - private void setDefaultConfigVariables(){ - when(bootstrapConfig.getPort()).thenReturn(5683); - when(bootstrapConfig.getSecurePort()).thenReturn(5684); - when(serverConfig.isRecommendedCiphers()).thenReturn(false); - when(serverConfig.getDtlsRetransmissionTimeout()).thenReturn(9000); - } - - -} \ No newline at end of file diff --git a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientTest.java b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientTest.java index 5bc334756d..8fe58a5276 100644 --- a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientTest.java +++ b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientTest.java @@ -15,9 +15,10 @@ */ package org.thingsboard.server.transport.lwm2m.server.client; +import org.eclipse.leshan.core.endpoint.EndpointUriUtil; import org.eclipse.leshan.core.link.Link; +import org.eclipse.leshan.core.peer.IpPeer; import org.eclipse.leshan.server.registration.Registration; -import org.junit.Ignore; import org.junit.Test; import org.junit.jupiter.api.Assertions; @@ -25,14 +26,14 @@ import java.net.InetSocketAddress; public class LwM2mClientTest { - @Ignore @Test public void setRegistration() { LwM2mClient client = new LwM2mClient("nodeId", "testEndpoint"); - Registration registration = null; /*new Registration - .Builder("test", "testEndpoint", Identity.unsecure(new InetSocketAddress(1000))) // FIXME: nick + Registration registration = new Registration + .Builder("testId", "testEndpoint", new IpPeer(new InetSocketAddress(1000)), + EndpointUriUtil.createUri("coap://localhost:5685")) .objectLinks(new Link[0]) - .build();*/ + .build(); Assertions.assertDoesNotThrow(() -> client.setRegistration(registration)); } diff --git a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDesTest.java b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDesTest.java index 43187f2a36..b3473a67b2 100644 --- a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDesTest.java +++ b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDesTest.java @@ -15,13 +15,17 @@ */ package org.thingsboard.server.transport.lwm2m.server.store.util; +import org.eclipse.leshan.core.LwM2m.LwM2mVersion; +import org.eclipse.leshan.core.endpoint.EndpointUriUtil; +import org.eclipse.leshan.core.link.Link; import org.eclipse.leshan.core.node.LwM2mMultipleResource; +import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mSingleResource; +import org.eclipse.leshan.core.peer.IpPeer; import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.server.registration.Registration; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.device.data.PowerMode; @@ -41,9 +45,12 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; +import java.net.Inet4Address; +import java.net.InetSocketAddress; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import java.util.UUID; @@ -56,10 +63,9 @@ import static org.mockito.Mockito.when; public class LwM2MClientSerDesTest { - @Ignore @Test public void serializeDeserialize() throws Exception { - LwM2mClient client = new LwM2mClient("nodeId", "testEndpoint"); + LwM2mClient client = new LwM2mClient("nodeId", "endpoint"); TransportDeviceInfo tdi = new TransportDeviceInfo(); tdi.setPowerMode(PowerMode.PSM); @@ -78,13 +84,13 @@ public class LwM2MClientSerDesTest { client.init(credentialsResponse, UUID.randomUUID()); - Registration registration = null; // FIXME: nick -// new Registration.Builder("test", "testEndpoint", Identity -// .unsecure(new InetSocketAddress(1000))) -// .supportedContentFormats() -// .supportedObjects(Map.of(15, "1.0", 17, "1.0")) -// .objectLinks(new Link[]{new Link("/")}) -// .build(); + Registration registration = new Registration + .Builder("test", "endpoint", new IpPeer(new InetSocketAddress(Inet4Address.getLoopbackAddress(), 1000)), + EndpointUriUtil.createUri("coap://localhost:5685")) + .supportedContentFormats() + .supportedObjects(Map.of(15, LwM2mVersion.V1_0, 17, LwM2mVersion.V1_0)) + .objectLinks(new Link[] { new Link("/15"), new Link("/17") }) + .build(); client.setRegistration(registration); client.setState(LwM2MClientState.REGISTERED); @@ -130,7 +136,12 @@ public class LwM2MClientSerDesTest { assertEquals(client.getPsmActivityTimer(), desClient.getPsmActivityTimer()); assertEquals(client.getPagingTransmissionWindow(), desClient.getPagingTransmissionWindow()); assertEquals(client.getEdrxCycle(), desClient.getEdrxCycle()); - assertEquals(client.getRegistration(), desClient.getRegistration()); + if (((IpPeer)desClient.getRegistration().getClientTransportData()).getSocketAddress().isUnresolved()) { + String actualReg = desClient.getRegistration().toString().replaceAll("/", ""); + assertEquals(client.getRegistration().toString(), actualReg); + } else { + assertEquals(client.getRegistration(), desClient.getRegistration()); + } assertEquals(client.isAsleep(), desClient.isAsleep()); assertEquals(client.getLastUplinkTime(), desClient.getLastUplinkTime()); assertEquals(client.getSleepTask(), desClient.getSleepTask()); @@ -143,7 +154,11 @@ public class LwM2MClientSerDesTest { Map actualResources = desClient.getResources(); assertNotNull(actualResources); assertEquals(expectedResources.size(), actualResources.size()); - expectedResources.forEach((key, value) -> assertEquals(value.toString(), actualResources.get(key).toString())); + for (Entry entry : expectedResources.entrySet()) { + LwM2mPath expectedPathId = client.getLwM2mPathFromString(entry.getKey().toString()); + String actualOld = actualResources.get(String.valueOf(expectedPathId.getObjectId())).toString(); + String actual = actualOld.replaceAll("\"", ""); + assertEquals(entry.getValue().toString(), actual); + } } - } \ No newline at end of file diff --git a/common/util/src/main/java/org/thingsboard/common/util/SslUtil.java b/common/util/src/main/java/org/thingsboard/common/util/SslUtil.java index a62870ea9b..1767c745bc 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/SslUtil.java +++ b/common/util/src/main/java/org/thingsboard/common/util/SslUtil.java @@ -104,7 +104,7 @@ public class SslUtil { } private static PrivateKey readPrivateKey(Reader reader, String passStr) throws IOException, PKCSException { - char[] password = StringUtils.isEmpty(passStr) ? EMPTY_PASS : passStr.toCharArray(); + char[] password = getPassword(passStr); PrivateKey privateKey = null; JcaPEMKeyConverter keyConverter = new JcaPEMKeyConverter(); try (PEMParser pemParser = new PEMParser(reader)) { @@ -130,4 +130,8 @@ public class SslUtil { return privateKey; } + public static char[] getPassword(String passStr) { + return StringUtils.isEmpty(passStr) ? EMPTY_PASS : passStr.toCharArray(); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java index 0e7fe8f108..865306c73b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java @@ -56,11 +56,19 @@ public abstract class DaoUtil { } public static Pageable toPageable(PageLink pageLink) { - return toPageable(pageLink, Collections.emptyMap()); + return toPageable(pageLink, true); + } + + public static Pageable toPageable(PageLink pageLink, boolean addDefaultSorting) { + return toPageable(pageLink, Collections.emptyMap(), addDefaultSorting); } public static Pageable toPageable(PageLink pageLink, Map columnMap) { - return PageRequest.of(pageLink.getPage(), pageLink.getPageSize(), pageLink.toSort(pageLink.getSortOrder(), columnMap)); + return toPageable(pageLink, columnMap, true); + } + + public static Pageable toPageable(PageLink pageLink, Map columnMap, boolean addDefaultSorting) { + return PageRequest.of(pageLink.getPage(), pageLink.getPageSize(), pageLink.toSort(pageLink.getSortOrder(), columnMap, addDefaultSorting)); } public static Pageable toPageable(PageLink pageLink, List sortOrders) { @@ -68,7 +76,11 @@ public abstract class DaoUtil { } public static Pageable toPageable(PageLink pageLink, Map columnMap, List sortOrders) { - return PageRequest.of(pageLink.getPage(), pageLink.getPageSize(), pageLink.toSort(sortOrders, columnMap)); + return toPageable(pageLink, columnMap, sortOrders, true); + } + + public static Pageable toPageable(PageLink pageLink, Map columnMap, List sortOrders, boolean addDefaultSorting) { + return PageRequest.of(pageLink.getPage(), pageLink.getPageSize(), pageLink.toSort(sortOrders, columnMap, addDefaultSorting)); } public static List convertDataList(Collection> toDataList) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java index 339c8fd988..365a5ac2e0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java @@ -80,6 +80,8 @@ public interface AlarmDao extends Dao { PageData findAlarmIdsByAssigneeId(TenantId tenantId, UUID userId, PageLink pageLink); + PageData findAlarmIdsByOriginatorId(TenantId tenantId, EntityId originatorId, PageLink pageLink); + void createEntityAlarmRecord(EntityAlarm entityAlarm); List findEntityAlarmRecords(TenantId tenantId, AlarmId id); diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java index ac6f4955d8..2c111151b3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java @@ -82,14 +82,14 @@ public class BaseAlarmCommentService extends AbstractEntityService implements Al @Override public ListenableFuture findAlarmCommentByIdAsync(TenantId tenantId, AlarmCommentId alarmCommentId) { log.trace("Executing findAlarmCommentByIdAsync by alarmCommentId [{}]", alarmCommentId); - validateId(alarmCommentId, "Incorrect alarmCommentId " + alarmCommentId); + validateId(alarmCommentId, id -> "Incorrect alarmCommentId " + id); return alarmCommentDao.findAlarmCommentByIdAsync(tenantId, alarmCommentId.getId()); } @Override public AlarmComment findAlarmCommentById(TenantId tenantId, AlarmCommentId alarmCommentId) { log.trace("Executing findAlarmCommentByIdAsync by alarmCommentId [{}]", alarmCommentId); - validateId(alarmCommentId, "Incorrect alarmCommentId " + alarmCommentId); + validateId(alarmCommentId, id -> "Incorrect alarmCommentId " + id); return alarmCommentDao.findById(tenantId, alarmCommentId.getId()); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index 9b7004042d..f9ddc212c8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -170,7 +170,7 @@ public class BaseAlarmService extends AbstractCachedEntityService findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds) { - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validateEntityDataPageLink(query.getPageLink()); return alarmDao.findAlarmDataByQueryForEntities(tenantId, query, orderedEntityIds); } @@ -216,17 +216,14 @@ public class BaseAlarmService extends AbstractCachedEntityService alarms; + public int deleteAlarmsByOriginatorId(TenantId tenantId, EntityId entityId) { + TimePageLink pageLink = new TimePageLink(256); + PageData alarms; int count = 0; do { - alarms = findAlarms(tenantId, AlarmQuery.builder() - .affectedEntityId(entityId) - .pageLink(new TimePageLink(pageLink, null, null)) - .build()); - for (AlarmInfo alarm : alarms.getData()) { - deleteAlarm(tenantId, alarm, true); + alarms = alarmService.findAlarmIdsByOriginatorId(tenantId, entityId, pageLink); + for (AlarmId alarmId : alarms.getData()) { + delAlarm(tenantId, alarmId); count++; } } while (alarms.hasNext()); @@ -286,21 +283,21 @@ public class BaseAlarmService extends AbstractCachedEntityService "Incorrect alarmId " + id); return alarmDao.findAlarmById(tenantId, alarmId.getId()); } @Override public ListenableFuture findAlarmByIdAsync(TenantId tenantId, AlarmId alarmId) { log.trace("Executing findAlarmByIdAsync [{}]", alarmId); - validateId(alarmId, "Incorrect alarmId " + alarmId); + validateId(alarmId, id -> "Incorrect alarmId " + id); return alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId()); } @Override public AlarmInfo findAlarmInfoById(TenantId tenantId, AlarmId alarmId) { log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId); - validateId(alarmId, "Incorrect alarmId " + alarmId); + validateId(alarmId, id -> "Incorrect alarmId " + id); return alarmDao.findAlarmInfoById(tenantId, alarmId.getId()); } @@ -327,10 +324,16 @@ public class BaseAlarmService extends AbstractCachedEntityService findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, PageLink pageLink) { log.trace("[{}] Executing findAlarmIdsByAssigneeId [{}]", tenantId, userId); - validateId(userId, "Incorrect userId " + userId); + validateId(userId, id -> "Incorrect userId " + id); return alarmDao.findAlarmIdsByAssigneeId(tenantId, userId.getId(), pageLink); } + @Override + public PageData findAlarmIdsByOriginatorId(TenantId tenantId, EntityId originatorId, PageLink pageLink) { + log.trace("[{}] Executing findAlarmsByOriginatorId [{}]", tenantId, originatorId); + return alarmDao.findAlarmIdsByOriginatorId(tenantId, originatorId, pageLink); + } + @Override public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus, String assigneeId) { @@ -361,14 +364,14 @@ public class BaseAlarmService extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return alarmDao.countAlarmsByQuery(tenantId, customerId, query); } @Override public PageData findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAlarmTypesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); if (DEFAULT_ALARM_TYPES_PAGE_LINK.equals(pageLink)) { return cache.getAndPutInTransaction(tenantId, () -> alarmDao.findTenantAlarmTypes(tenantId.getId(), pageLink), false); diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java index ab55a7950e..7946448666 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java @@ -103,7 +103,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_ASSET_PROFILE_ID + id); return cache.getOrFetchFromDB(AssetProfileCacheKey.fromId(assetProfileId), () -> assetProfileDao.findById(tenantId, assetProfileId.getId()), true, putInCache); } @@ -116,7 +116,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_ASSET_PROFILE_NAME + s); return cache.getOrFetchFromDB(AssetProfileCacheKey.fromName(tenantId, profileName), () -> assetProfileDao.findByName(tenantId, profileName), false, putInCache); } @@ -124,7 +124,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_ASSET_PROFILE_ID + id); return toAssetProfileInfo(findAssetProfileById(tenantId, assetProfileId)); } @@ -179,7 +179,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_ASSET_PROFILE_ID + id); AssetProfile assetProfile = assetProfileDao.findById(tenantId, assetProfileId.getId()); if (assetProfile != null && assetProfile.isDefault()) { throw new DataValidationException("Deletion of Default Asset Profile is prohibited!"); @@ -207,7 +207,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService findAssetProfiles(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAssetProfiles tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return assetProfileDao.findAssetProfiles(tenantId, pageLink); } @@ -215,7 +215,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService findAssetProfileInfos(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAssetProfileInfos tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return assetProfileDao.findAssetProfileInfos(tenantId, pageLink); } @@ -245,7 +245,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); AssetProfile assetProfile = new AssetProfile(); assetProfile.setTenantId(tenantId); assetProfile.setDefault(defaultProfile); @@ -257,7 +257,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return cache.getAndPutInTransaction(AssetProfileCacheKey.defaultProfile(tenantId), () -> assetProfileDao.findDefaultAssetProfile(tenantId), true); } @@ -265,14 +265,14 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return toAssetProfileInfo(findDefaultAssetProfile(tenantId)); } @Override public boolean setDefaultAssetProfile(TenantId tenantId, AssetProfileId assetProfileId) { log.trace("Executing setDefaultAssetProfile [{}]", assetProfileId); - Validator.validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId); + Validator.validateId(assetProfileId, id -> INCORRECT_ASSET_PROFILE_ID + id); AssetProfile assetProfile = assetProfileDao.findById(tenantId, assetProfileId.getId()); if (!assetProfile.isDefault()) { assetProfile.setDefault(true); @@ -298,7 +298,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); tenantAssetProfilesRemover.removeEntities(tenantId, tenantId); } @@ -326,7 +326,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService findAssetProfileNamesByTenantId(TenantId tenantId, boolean activeOnly) { log.trace("Executing findAssetProfileNamesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return assetProfileDao.findTenantAssetProfileNames(tenantId.getId(), activeOnly) .stream().sorted(Comparator.comparing(EntityInfo::getName)) .collect(Collectors.toList()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index 79357ac119..c03dd74d6c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -75,7 +75,6 @@ public class BaseAssetService extends AbstractCachedEntityService INCORRECT_ASSET_ID + id); return assetDao.findAssetInfoById(tenantId, assetId.getId()); } @Override public Asset findAssetById(TenantId tenantId, AssetId assetId) { log.trace("Executing findAssetById [{}]", assetId); - validateId(assetId, INCORRECT_ASSET_ID + assetId); + validateId(assetId, id -> INCORRECT_ASSET_ID + id); return assetDao.findById(tenantId, assetId.getId()); } @Override public ListenableFuture findAssetByIdAsync(TenantId tenantId, AssetId assetId) { log.trace("Executing findAssetById [{}]", assetId); - validateId(assetId, INCORRECT_ASSET_ID + assetId); + validateId(assetId, id -> INCORRECT_ASSET_ID + id); return assetDao.findByIdAsync(tenantId, assetId.getId()); } @Override public Asset findAssetByTenantIdAndName(TenantId tenantId, String name) { log.trace("Executing findAssetByTenantIdAndName [{}][{}]", tenantId, name); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return cache.getAndPutInTransaction(new AssetCacheKey(tenantId, name), () -> assetDao.findAssetsByTenantIdAndName(tenantId.getId(), name) .orElse(null), true); @@ -199,7 +198,7 @@ public class BaseAssetService extends AbstractCachedEntityService INCORRECT_ASSET_ID + id); if (entityViewService.existsByTenantIdAndEntityId(tenantId, assetId)) { throw new DataValidationException("Can't delete asset that has entity views!"); } @@ -220,7 +219,7 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetsByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAssetsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return assetDao.findAssetsByTenantId(tenantId.getId(), pageLink); } @@ -228,7 +227,7 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetInfosByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAssetInfosByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return assetDao.findAssetInfosByTenantId(tenantId.getId(), pageLink); } @@ -236,8 +235,8 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetsByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { log.trace("Executing findAssetsByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return assetDao.findAssetsByTenantIdAndType(tenantId.getId(), type, pageLink); } @@ -245,8 +244,8 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { log.trace("Executing findAssetInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return assetDao.findAssetInfosByTenantIdAndType(tenantId.getId(), type, pageLink); } @@ -254,8 +253,8 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetInfosByTenantIdAndAssetProfileId(TenantId tenantId, AssetProfileId assetProfileId, PageLink pageLink) { log.trace("Executing findAssetInfosByTenantIdAndAssetProfileId, tenantId [{}], assetProfileId [{}], pageLink [{}]", tenantId, assetProfileId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(assetProfileId, id -> INCORRECT_ASSET_PROFILE_ID + id); validatePageLink(pageLink); return assetDao.findAssetInfosByTenantIdAndAssetProfileId(tenantId.getId(), assetProfileId.getId(), pageLink); } @@ -263,15 +262,15 @@ public class BaseAssetService extends AbstractCachedEntityService> findAssetsByTenantIdAndIdsAsync(TenantId tenantId, List assetIds) { log.trace("Executing findAssetsByTenantIdAndIdsAsync, tenantId [{}], assetIds [{}]", tenantId, assetIds); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateIds(assetIds, "Incorrect assetIds " + assetIds); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateIds(assetIds, ids -> "Incorrect assetIds " + ids); return assetDao.findAssetsByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(assetIds)); } @Override public void deleteAssetsByTenantId(TenantId tenantId) { log.trace("Executing deleteAssetsByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); tenantAssetsRemover.removeEntities(tenantId, tenantId); } @@ -283,8 +282,8 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findAssetsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); return assetDao.findAssetsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); } @@ -292,8 +291,8 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetInfosByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findAssetInfosByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); return assetDao.findAssetInfosByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); } @@ -301,9 +300,9 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) { log.trace("Executing findAssetsByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return assetDao.findAssetsByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); } @@ -311,9 +310,9 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetInfosByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) { log.trace("Executing findAssetInfosByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return assetDao.findAssetInfosByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); } @@ -321,9 +320,9 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetInfosByTenantIdAndCustomerIdAndAssetProfileId(TenantId tenantId, CustomerId customerId, AssetProfileId assetProfileId, PageLink pageLink) { log.trace("Executing findAssetInfosByTenantIdAndCustomerIdAndAssetProfileId, tenantId [{}], customerId [{}], assetProfileId [{}], pageLink [{}]", tenantId, customerId, assetProfileId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); + validateId(assetProfileId, id -> INCORRECT_ASSET_PROFILE_ID + id); validatePageLink(pageLink); return assetDao.findAssetInfosByTenantIdAndCustomerIdAndAssetProfileId(tenantId.getId(), customerId.getId(), assetProfileId.getId(), pageLink); } @@ -331,17 +330,17 @@ public class BaseAssetService extends AbstractCachedEntityService> findAssetsByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List assetIds) { log.trace("Executing findAssetsByTenantIdAndCustomerIdAndIdsAsync, tenantId [{}], customerId [{}], assetIds [{}]", tenantId, customerId, assetIds); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - validateIds(assetIds, "Incorrect assetIds " + assetIds); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); + validateIds(assetIds, ids -> "Incorrect assetIds " + ids); return assetDao.findAssetsByTenantIdAndCustomerIdAndIdsAsync(tenantId.getId(), customerId.getId(), toUUIDs(assetIds)); } @Override public void unassignCustomerAssets(TenantId tenantId, CustomerId customerId) { log.trace("Executing unassignCustomerAssets, tenantId [{}], customerId [{}]", tenantId, customerId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); customerAssetsUnasigner.removeEntities(tenantId, customerId); } @@ -373,7 +372,7 @@ public class BaseAssetService extends AbstractCachedEntityService> findAssetTypesByTenantId(TenantId tenantId) { log.trace("Executing findAssetTypesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return assetDao.findTenantAssetTypesAsync(tenantId.getId()); } @@ -422,8 +421,8 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { log.trace("Executing findAssetsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); validatePageLink(pageLink); return assetDao.findAssetsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); } @@ -431,9 +430,9 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink) { log.trace("Executing findAssetsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}] pageLink [{}]", tenantId, edgeId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return assetDao.findAssetsByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java index 65e52e06d2..7e705c2278 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java @@ -28,12 +28,12 @@ public class AttributeUtils { @Deprecated(since = "3.7.0") public static void validate(EntityId id, String scope) { - Validator.validateId(id.getId(), "Incorrect id " + id); - Validator.validateString(scope, "Incorrect scope " + scope); + Validator.validateId(id.getId(), uuid -> "Incorrect id " + uuid); + Validator.validateString(scope, sc -> "Incorrect scope " + sc); } public static void validate(EntityId id, AttributeScope scope) { - Validator.validateId(id.getId(), "Incorrect id " + id); + Validator.validateId(id.getId(), uuid -> "Incorrect id " + uuid); Validator.checkNotNull(scope, "Incorrect scope " + scope); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java index 60856d361b..1b1f2d005d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java @@ -59,28 +59,28 @@ public class BaseAttributesService implements AttributesService { @Override public ListenableFuture> find(TenantId tenantId, EntityId entityId, String scope, String attributeKey) { validate(entityId, scope); - Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey); + Validator.validateString(attributeKey, k -> "Incorrect attribute key " + k); return Futures.immediateFuture(attributesDao.find(tenantId, entityId, AttributeScope.valueOf(scope), attributeKey)); } @Override public ListenableFuture> find(TenantId tenantId, EntityId entityId, AttributeScope scope, String attributeKey) { validate(entityId, scope); - Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey); + Validator.validateString(attributeKey, k -> "Incorrect attribute key " + k); return Futures.immediateFuture(attributesDao.find(tenantId, entityId, scope, attributeKey)); } @Override public ListenableFuture> find(TenantId tenantId, EntityId entityId, String scope, Collection attributeKeys) { validate(entityId, scope); - attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey)); + attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, k -> "Incorrect attribute key " + k)); return Futures.immediateFuture(attributesDao.find(tenantId, entityId, AttributeScope.valueOf(scope), attributeKeys)); } @Override public ListenableFuture> find(TenantId tenantId, EntityId entityId, AttributeScope scope, Collection attributeKeys) { validate(entityId, scope); - attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey)); + attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, k -> "Incorrect attribute key " + k)); return Futures.immediateFuture(attributesDao.find(tenantId, entityId, scope, attributeKeys)); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java index 45e671a891..f51bc6f0b8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java @@ -118,7 +118,7 @@ public class CachedAttributesService implements AttributesService { @Override public ListenableFuture> find(TenantId tenantId, EntityId entityId, AttributeScope scope, String attributeKey) { validate(entityId, scope); - Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey); + Validator.validateString(attributeKey, k -> "Incorrect attribute key " + k); return cacheExecutor.submit(() -> { AttributeCacheKey attributeCacheKey = new AttributeCacheKey(scope, entityId, attributeKey); @@ -153,7 +153,7 @@ public class CachedAttributesService implements AttributesService { public ListenableFuture> find(TenantId tenantId, EntityId entityId, AttributeScope scope, final Collection attributeKeysNonUnique) { validate(entityId, scope); final var attributeKeys = new LinkedHashSet<>(attributeKeysNonUnique); // deduplicate the attributes - attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey)); + attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, k ->"Incorrect attribute key " + k)); //CacheExecutor for Redis or DirectExecutor for local Caffeine return Futures.transformAsync(cacheExecutor.submit(() -> findCachedAttributes(entityId, scope, attributeKeys)), diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java index ddaebef6e8..29b097bfab 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java @@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.StringUtils; @@ -85,31 +86,31 @@ public class AuditLogServiceImpl implements AuditLogService { @Override public PageData findAuditLogsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, List actionTypes, TimePageLink pageLink) { log.trace("Executing findAuditLogsByTenantIdAndCustomerId [{}], [{}], [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, "Incorrect customerId " + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> "Incorrect customerId " + id); return auditLogDao.findAuditLogsByTenantIdAndCustomerId(tenantId.getId(), customerId, actionTypes, pageLink); } @Override public PageData findAuditLogsByTenantIdAndUserId(TenantId tenantId, UserId userId, List actionTypes, TimePageLink pageLink) { log.trace("Executing findAuditLogsByTenantIdAndUserId [{}], [{}], [{}]", tenantId, userId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(userId, "Incorrect userId" + userId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(userId, id -> "Incorrect userId" + id); return auditLogDao.findAuditLogsByTenantIdAndUserId(tenantId.getId(), userId, actionTypes, pageLink); } @Override public PageData findAuditLogsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, List actionTypes, TimePageLink pageLink) { log.trace("Executing findAuditLogsByTenantIdAndEntityId [{}], [{}], [{}]", tenantId, entityId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateEntityId(entityId, INCORRECT_TENANT_ID + entityId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateEntityId(entityId, id -> "Incorrect entityId" + id); return auditLogDao.findAuditLogsByTenantIdAndEntityId(tenantId.getId(), entityId, actionTypes, pageLink); } @Override public PageData findAuditLogsByTenantId(TenantId tenantId, List actionTypes, TimePageLink pageLink) { log.trace("Executing findAuditLogs [{}]", pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return auditLogDao.findAuditLogsByTenantId(tenantId.getId(), actionTypes, pageLink); } @@ -200,10 +201,10 @@ public class AuditLogServiceImpl implements AuditLogService { break; case ATTRIBUTES_UPDATED: actionData.put("entityId", entityId.toString()); - String scope = extractParameter(String.class, 0, additionalInfo); + AttributeScope scope = extractParameter(AttributeScope.class, 0, additionalInfo); @SuppressWarnings("unchecked") List attributes = extractParameter(List.class, 1, additionalInfo); - actionData.put("scope", scope); + actionData.put("scope", scope.name()); ObjectNode attrsNode = JacksonUtil.newObjectNode(); if (attributes != null) { for (AttributeKvEntry attr : attributes) { @@ -215,8 +216,8 @@ public class AuditLogServiceImpl implements AuditLogService { case ATTRIBUTES_DELETED: case ATTRIBUTES_READ: actionData.put("entityId", entityId.toString()); - scope = extractParameter(String.class, 0, additionalInfo); - actionData.put("scope", scope); + scope = extractParameter(AttributeScope.class, 0, additionalInfo); + actionData.put("scope", scope.name()); @SuppressWarnings("unchecked") List keys = extractParameter(List.class, 1, additionalInfo); ArrayNode attrsArrayNode = actionData.putArray("attributes"); diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java index bd8ddb4059..3e85fb619d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java @@ -84,21 +84,21 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Override public Customer findCustomerById(TenantId tenantId, CustomerId customerId) { log.trace("Executing findCustomerById [{}]", customerId); - Validator.validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + Validator.validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); return customerDao.findById(tenantId, customerId.getId()); } @Override public Optional findCustomerByTenantIdAndTitle(TenantId tenantId, String title) { log.trace("Executing findCustomerByTenantIdAndTitle [{}] [{}]", tenantId, title); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return customerDao.findCustomersByTenantIdAndTitle(tenantId.getId(), title); } @Override public ListenableFuture findCustomerByIdAsync(TenantId tenantId, CustomerId customerId) { log.trace("Executing findCustomerByIdAsync [{}]", customerId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); return customerDao.findByIdAsync(tenantId, customerId.getId()); } @@ -126,7 +126,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Transactional public void deleteCustomer(TenantId tenantId, CustomerId customerId) { log.trace("Executing deleteCustomer [{}]", customerId); - Validator.validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + Validator.validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); Customer customer = findCustomerById(tenantId, customerId); if (customer == null) { throw new IncorrectParameterException("Unable to delete non-existent customer."); @@ -146,7 +146,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Override public Customer findOrCreatePublicCustomer(TenantId tenantId) { log.trace("Executing findOrCreatePublicCustomer, tenantId [{}]", tenantId); - Validator.validateId(tenantId, INCORRECT_CUSTOMER_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_CUSTOMER_ID + id); Optional publicCustomerOpt = customerDao.findCustomersByTenantIdAndTitle(tenantId.getId(), PUBLIC_CUSTOMER_TITLE); if (publicCustomerOpt.isPresent()) { return publicCustomerOpt.get(); @@ -168,7 +168,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Override public PageData findCustomersByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findCustomersByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); + Validator.validateId(tenantId, id -> "Incorrect tenantId " + id); Validator.validatePageLink(pageLink); return customerDao.findCustomersByTenantId(tenantId.getId(), pageLink); } @@ -176,7 +176,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Override public void deleteCustomersByTenantId(TenantId tenantId) { log.trace("Executing deleteCustomersByTenantId, tenantId [{}]", tenantId); - Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); + Validator.validateId(tenantId, id -> "Incorrect tenantId " + id); customersByTenantRemover.removeEntities(tenantId, tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index f3d2677dfa..8eef618382 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -110,21 +110,21 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId) { log.trace("Executing findDashboardById [{}]", dashboardId); - Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId); + Validator.validateId(dashboardId, id -> INCORRECT_DASHBOARD_ID + id); return dashboardDao.findById(tenantId, dashboardId.getId()); } @Override public ListenableFuture findDashboardByIdAsync(TenantId tenantId, DashboardId dashboardId) { log.trace("Executing findDashboardByIdAsync [{}]", dashboardId); - validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId); + validateId(dashboardId, id -> INCORRECT_DASHBOARD_ID + id); return dashboardDao.findByIdAsync(tenantId, dashboardId.getId()); } @Override public DashboardInfo findDashboardInfoById(TenantId tenantId, DashboardId dashboardId) { log.trace("Executing findDashboardInfoById [{}]", dashboardId); - Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId); + Validator.validateId(dashboardId, id -> INCORRECT_DASHBOARD_ID + id); return dashboardInfoDao.findById(tenantId, dashboardId.getId()); } @@ -137,7 +137,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public ListenableFuture findDashboardInfoByIdAsync(TenantId tenantId, DashboardId dashboardId) { log.trace("Executing findDashboardInfoByIdAsync [{}]", dashboardId); - validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId); + validateId(dashboardId, id -> INCORRECT_DASHBOARD_ID + id); return dashboardInfoDao.findByIdAsync(tenantId, dashboardId.getId()); } @@ -229,7 +229,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Transactional public void deleteDashboard(TenantId tenantId, DashboardId dashboardId) { log.trace("Executing deleteDashboard [{}]", dashboardId); - Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId); + Validator.validateId(dashboardId, id -> INCORRECT_DASHBOARD_ID + id); try { dashboardDao.removeById(tenantId, dashboardId.getId()); publishEvictEvent(new DashboardTitleEvictEvent(dashboardId)); @@ -248,7 +248,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public PageData findDashboardsByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findDashboardsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return dashboardInfoDao.findDashboardsByTenantId(tenantId.getId(), pageLink); } @@ -256,7 +256,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public PageData findMobileDashboardsByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findMobileDashboardsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return dashboardInfoDao.findMobileDashboardsByTenantId(tenantId.getId(), pageLink); } @@ -264,7 +264,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public void deleteDashboardsByTenantId(TenantId tenantId) { log.trace("Executing deleteDashboardsByTenantId, tenantId [{}]", tenantId); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); tenantDashboardsRemover.removeEntities(tenantId, tenantId); } @@ -276,8 +276,8 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public PageData findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findDashboardsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateId(customerId, "Incorrect customerId " + customerId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(customerId, id -> "Incorrect customerId " + id); Validator.validatePageLink(pageLink); return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); } @@ -285,8 +285,8 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public PageData findMobileDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findMobileDashboardsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateId(customerId, "Incorrect customerId " + customerId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(customerId, id -> "Incorrect customerId " + id); Validator.validatePageLink(pageLink); return dashboardInfoDao.findMobileDashboardsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); } @@ -294,7 +294,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId) { log.trace("Executing unassignCustomerDashboards, customerId [{}]", customerId); - Validator.validateId(customerId, "Incorrect customerId " + customerId); + Validator.validateId(customerId, id -> "Incorrect customerId " + id); Customer customer = customerDao.findById(tenantId, customerId.getId()); if (customer == null) { throw new DataValidationException("Can't unassign dashboards from non-existent customer!"); @@ -305,7 +305,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public void updateCustomerDashboards(TenantId tenantId, CustomerId customerId) { log.trace("Executing updateCustomerDashboards, customerId [{}]", customerId); - Validator.validateId(customerId, "Incorrect customerId " + customerId); + Validator.validateId(customerId, id -> "Incorrect customerId " + id); Customer customer = customerDao.findById(tenantId, customerId.getId()); if (customer == null) { throw new DataValidationException("Can't update dashboards for non-existent customer!"); @@ -355,8 +355,8 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public PageData findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { log.trace("Executing findDashboardsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateId(edgeId, INCORRECT_EDGE_ID + edgeId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(edgeId, id -> INCORRECT_EDGE_ID + id); Validator.validatePageLink(pageLink); return dashboardInfoDao.findDashboardsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java index 4367163ded..67af118d0b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java @@ -88,7 +88,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService public JsonNode findDevicePublishTelemetryCommands(String baseUrl, Device device) throws URISyntaxException { DeviceId deviceId = device.getId(); log.trace("Executing findDevicePublishTelemetryCommands [{}]", deviceId); - validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); + validateId(deviceId, id -> INCORRECT_DEVICE_ID + id); DeviceCredentials creds = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), deviceId); DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java index 60167f04f2..6025e7c820 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java @@ -72,14 +72,14 @@ public class DeviceCredentialsServiceImpl extends AbstractCachedEntityService "Incorrect deviceId " + id); return deviceCredentialsDao.findByDeviceId(tenantId, deviceId.getId()); } @Override public DeviceCredentials findDeviceCredentialsByCredentialsId(String credentialsId) { log.trace("Executing findDeviceCredentialsByCredentialsId [{}]", credentialsId); - validateString(credentialsId, "Incorrect credentialsId " + credentialsId); + validateString(credentialsId, id -> "Incorrect credentialsId " + id); return cache.getAndPutInTransaction(credentialsId, () -> deviceCredentialsDao.findByCredentialsId(TenantId.SYS_TENANT_ID, credentialsId), true); // caching null values is essential for permanently invalid requests diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index f2f893e4c3..700d8b52f0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -120,7 +120,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_DEVICE_PROFILE_ID + id); return cache.getOrFetchFromDB(DeviceProfileCacheKey.fromId(deviceProfileId), () -> deviceProfileDao.findById(tenantId, deviceProfileId.getId()), true, putInCache); } @@ -133,7 +133,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_DEVICE_PROFILE_NAME + pn); return cache.getOrFetchFromDB(DeviceProfileCacheKey.fromName(tenantId, profileName), () -> deviceProfileDao.findByName(tenantId, profileName), true, putInCache); } @@ -141,7 +141,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_PROVISION_DEVICE_KEY + dk); return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromProvisionDeviceKey(provisionDeviceKey), () -> deviceProfileDao.findByProvisionDeviceKey(provisionDeviceKey), false); } @@ -149,7 +149,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_DEVICE_PROFILE_ID + id); return toDeviceProfileInfo(findDeviceProfileById(tenantId, deviceProfileId)); } @@ -216,7 +216,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_DEVICE_PROFILE_ID + id); DeviceProfile deviceProfile = deviceProfileDao.findById(tenantId, deviceProfileId.getId()); if (deviceProfile != null && deviceProfile.isDefault()) { throw new DataValidationException("Deletion of Default Device Profile is prohibited!"); @@ -245,7 +245,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService findDeviceProfiles(TenantId tenantId, PageLink pageLink) { log.trace("Executing findDeviceProfiles tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return deviceProfileDao.findDeviceProfiles(tenantId, pageLink); } @@ -253,7 +253,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService findDeviceProfileInfos(TenantId tenantId, PageLink pageLink, String transportType) { log.trace("Executing findDeviceProfileInfos tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return deviceProfileDao.findDeviceProfileInfos(tenantId, pageLink, transportType); } @@ -283,7 +283,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); DeviceProfile deviceProfile = new DeviceProfile(); deviceProfile.setTenantId(tenantId); deviceProfile.setDefault(defaultProfile); @@ -306,7 +306,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return cache.getAndPutInTransaction(DeviceProfileCacheKey.defaultProfile(tenantId), () -> deviceProfileDao.findDefaultDeviceProfile(tenantId), true); } @@ -314,14 +314,14 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return toDeviceProfileInfo(findDefaultDeviceProfile(tenantId)); } @Override public boolean setDefaultDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) { log.trace("Executing setDefaultDeviceProfile [{}]", deviceProfileId); - validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId); + validateId(deviceProfileId, id -> INCORRECT_DEVICE_PROFILE_ID + id); DeviceProfile deviceProfile = deviceProfileDao.findById(tenantId, deviceProfileId.getId()); if (!deviceProfile.isDefault()) { deviceProfile.setDefault(true); @@ -347,7 +347,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); tenantDeviceProfilesRemover.removeEntities(tenantId, tenantId); } @@ -375,7 +375,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService findDeviceProfileNamesByTenantId(TenantId tenantId, boolean activeOnly) { log.trace("Executing findDeviceProfileNamesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return deviceProfileDao.findTenantDeviceProfileNames(tenantId.getId(), activeOnly) .stream().sorted(Comparator.comparing(EntityInfo::getName)) .collect(Collectors.toList()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 97b40ba7a8..71920c5743 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -126,14 +126,14 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_DEVICE_ID + id); return deviceDao.findDeviceInfoById(tenantId, deviceId.getId()); } @Override public Device findDeviceById(TenantId tenantId, DeviceId deviceId) { log.trace("Executing findDeviceById [{}]", deviceId); - validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); + validateId(deviceId, id -> INCORRECT_DEVICE_ID + id); if (TenantId.SYS_TENANT_ID.equals(tenantId)) { return cache.getAndPutInTransaction(new DeviceCacheKey(deviceId), () -> deviceDao.findById(tenantId, deviceId.getId()), true); @@ -146,7 +146,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId) { log.trace("Executing findDeviceById [{}]", deviceId); - validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); + validateId(deviceId, id -> INCORRECT_DEVICE_ID + id); if (TenantId.SYS_TENANT_ID.equals(tenantId)) { return deviceDao.findByIdAsync(tenantId, deviceId.getId()); } else { @@ -157,7 +157,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return cache.getAndPutInTransaction(new DeviceCacheKey(tenantId, name), () -> deviceDao.findDeviceByTenantIdAndName(tenantId.getId(), name).orElse(null), true); } @@ -321,7 +321,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_DEVICE_ID + id); if (entityViewService.existsByTenantIdAndEntityId(tenantId, deviceId)) { throw new DataValidationException("Can't delete device that has entity views!"); } @@ -344,7 +344,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService findDevicesByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findDevicesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id ->INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return deviceDao.findDevicesByTenantId(tenantId.getId(), pageLink); } @@ -356,7 +356,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return deviceDao.findDeviceInfosByFilter(filter, pageLink); @@ -372,8 +372,8 @@ public class DeviceServiceImpl extends AbstractCachedEntityService findDevicesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { log.trace("Executing findDevicesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return deviceDao.findDevicesByTenantIdAndType(tenantId.getId(), type, pageLink); } @@ -385,8 +385,8 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); + validateId(deviceProfileId, id -> INCORRECT_DEVICE_PROFILE_ID + id); validatePageLink(pageLink); return deviceDao.findDevicesByTenantIdAndTypeAndEmptyOtaPackage(tenantId.getId(), deviceProfileId.getId(), type, pageLink); } @@ -394,30 +394,30 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); + validateId(deviceProfileId, id -> INCORRECT_DEVICE_PROFILE_ID + id); return deviceDao.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(tenantId.getId(), deviceProfileId.getId(), type); } @Override public ListenableFuture> findDevicesByTenantIdAndIdsAsync(TenantId tenantId, List deviceIds) { log.trace("Executing findDevicesByTenantIdAndIdsAsync, tenantId [{}], deviceIds [{}]", tenantId, deviceIds); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateIds(deviceIds, "Incorrect deviceIds " + deviceIds); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateIds(deviceIds, ids -> "Incorrect deviceIds " + ids); return deviceDao.findDevicesByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(deviceIds)); } @Override public List findDevicesByIds(List deviceIds) { log.trace("Executing findDevicesByIdsAsync, deviceIds [{}]", deviceIds); - validateIds(deviceIds, "Incorrect deviceIds " + deviceIds); + validateIds(deviceIds, ids-> "Incorrect deviceIds " + ids); return deviceDao.findDevicesByIds(toUUIDs(deviceIds)); } @Override public ListenableFuture> findDevicesByIdsAsync(List deviceIds) { log.trace("Executing findDevicesByIdsAsync, deviceIds [{}]", deviceIds); - validateIds(deviceIds, "Incorrect deviceIds " + deviceIds); + validateIds(deviceIds, ids-> "Incorrect deviceIds " + ids); return deviceDao.findDevicesByIdsAsync(toUUIDs(deviceIds)); } @@ -425,7 +425,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); tenantDevicesRemover.removeEntities(tenantId, tenantId); } @@ -438,8 +438,8 @@ public class DeviceServiceImpl extends AbstractCachedEntityService findDevicesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findDevicesByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); return deviceDao.findDevicesByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); } @@ -447,9 +447,9 @@ public class DeviceServiceImpl extends AbstractCachedEntityService findDevicesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) { log.trace("Executing findDevicesByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return deviceDao.findDevicesByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); } @@ -457,9 +457,9 @@ public class DeviceServiceImpl extends AbstractCachedEntityService> findDevicesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List deviceIds) { log.trace("Executing findDevicesByTenantIdCustomerIdAndIdsAsync, tenantId [{}], customerId [{}], deviceIds [{}]", tenantId, customerId, deviceIds); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - validateIds(deviceIds, "Incorrect deviceIds " + deviceIds); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); + validateIds(deviceIds, ids -> "Incorrect deviceIds " + ids); return deviceDao.findDevicesByTenantIdCustomerIdAndIdsAsync(tenantId.getId(), customerId.getId(), toUUIDs(deviceIds)); } @@ -467,8 +467,8 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); customerDevicesRemover.removeEntities(tenantId, customerId); } @@ -494,7 +494,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService> findDeviceTypesByTenantId(TenantId tenantId) { log.trace("Executing findDeviceTypesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return deviceDao.findTenantDeviceTypesAsync(tenantId.getId()); } @@ -630,8 +630,8 @@ public class DeviceServiceImpl extends AbstractCachedEntityService findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { log.trace("Executing findDevicesByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); validatePageLink(pageLink); return deviceDao.findDevicesByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); } @@ -639,9 +639,9 @@ public class DeviceServiceImpl extends AbstractCachedEntityService findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink) { log.trace("Executing findDevicesByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}] pageLink [{}]", tenantId, edgeId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return deviceDao.findDevicesByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index e80d30f23a..1fd3cacb6d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -139,28 +139,28 @@ public class EdgeServiceImpl extends AbstractCachedEntityService INCORRECT_EDGE_ID + id); return edgeDao.findById(tenantId, edgeId.getId()); } @Override public EdgeInfo findEdgeInfoById(TenantId tenantId, EdgeId edgeId) { log.trace("Executing findEdgeInfoById [{}]", edgeId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); return edgeDao.findEdgeInfoById(tenantId, edgeId.getId()); } @Override public ListenableFuture findEdgeByIdAsync(TenantId tenantId, EdgeId edgeId) { log.trace("Executing findEdgeById [{}]", edgeId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); return edgeDao.findByIdAsync(tenantId, edgeId.getId()); } @Override public Edge findEdgeByTenantIdAndName(TenantId tenantId, String name) { log.trace("Executing findEdgeByTenantIdAndName [{}][{}]", tenantId, name); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return cache.getAndPutInTransaction(new EdgeCacheKey(tenantId, name), () -> edgeDao.findEdgeByTenantIdAndName(tenantId.getId(), name) .orElse(null), true); @@ -222,7 +222,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService INCORRECT_EDGE_ID + id); Edge edge = edgeDao.findById(tenantId, edgeId.getId()); edgeDao.removeById(tenantId, edgeId.getId()); @@ -235,7 +235,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgesByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findEdgesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return edgeDao.findEdgesByTenantId(tenantId.getId(), pageLink); } @@ -243,8 +243,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { log.trace("Executing findEdgesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return edgeDao.findEdgesByTenantIdAndType(tenantId.getId(), type, pageLink); } @@ -252,8 +252,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgeInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { log.trace("Executing findEdgeInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return edgeDao.findEdgeInfosByTenantIdAndType(tenantId.getId(), type, pageLink); } @@ -261,7 +261,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgeInfosByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findEdgeInfosByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return edgeDao.findEdgeInfosByTenantId(tenantId.getId(), pageLink); } @@ -269,15 +269,15 @@ public class EdgeServiceImpl extends AbstractCachedEntityService> findEdgesByTenantIdAndIdsAsync(TenantId tenantId, List edgeIds) { log.trace("Executing findEdgesByTenantIdAndIdsAsync, tenantId [{}], edgeIds [{}]", tenantId, edgeIds); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateIds(edgeIds, "Incorrect edgeIds " + edgeIds); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateIds(edgeIds, ids -> "Incorrect edgeIds " + ids); return edgeDao.findEdgesByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(edgeIds)); } @Override public void deleteEdgesByTenantId(TenantId tenantId) { log.trace("Executing deleteEdgesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); tenantEdgesRemover.removeEntities(tenantId, tenantId); } @@ -289,8 +289,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findEdgesByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); return edgeDao.findEdgesByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); } @@ -298,9 +298,9 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) { log.trace("Executing findEdgesByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return edgeDao.findEdgesByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); } @@ -308,8 +308,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgeInfosByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findEdgeInfosByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); return edgeDao.findEdgeInfosByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); } @@ -317,9 +317,9 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgeInfosByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) { log.trace("Executing findEdgeInfosByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return edgeDao.findEdgeInfosByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); } @@ -327,9 +327,9 @@ public class EdgeServiceImpl extends AbstractCachedEntityService> findEdgesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List edgeIds) { log.trace("Executing findEdgesByTenantIdCustomerIdAndIdsAsync, tenantId [{}], customerId [{}], edgeIds [{}]", tenantId, customerId, edgeIds); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - validateIds(edgeIds, "Incorrect edgeIds " + edgeIds); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); + validateIds(edgeIds, ids -> "Incorrect edgeIds " + ids); return edgeDao.findEdgesByTenantIdCustomerIdAndIdsAsync(tenantId.getId(), customerId.getId(), toUUIDs(edgeIds)); } @@ -337,8 +337,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); customerEdgeUnassigner.removeEntities(tenantId, customerId); } @@ -374,7 +374,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService> findEdgeTypesByTenantId(TenantId tenantId) { log.trace("Executing findEdgeTypesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); ListenableFuture> tenantEdgeTypes = edgeDao.findTenantEdgeTypesAsync(tenantId.getId()); return Futures.transform(tenantEdgeTypes, edgeTypes -> { @@ -404,7 +404,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgesByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink) { log.trace("Executing findEdgesByTenantIdAndEntityId, tenantId [{}], entityId [{}], pageLink [{}]", tenantId, entityId, pageLink); - Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return edgeDao.findEdgesByTenantIdAndEntityId(tenantId.getId(), entityId.getId(), entityId.getEntityType(), pageLink); } @@ -412,7 +412,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgesByTenantProfileId(TenantProfileId tenantProfileId, PageLink pageLink) { log.trace("Executing findEdgesByTenantProfileId, tenantProfileId [{}], pageLink [{}]", tenantProfileId, pageLink); - Validator.validateId(tenantProfileId, "Incorrect tenantProfileId " + tenantProfileId); + Validator.validateId(tenantProfileId, id -> "Incorrect tenantProfileId " + id); validatePageLink(pageLink); return edgeDao.findEdgesByTenantProfileId(tenantProfileId.getId(), pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index 4ee812a26b..5edb59f5c8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -17,8 +17,10 @@ package org.thingsboard.server.dao.entity; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasCustomerId; import org.thingsboard.server.common.data.HasEmail; import org.thingsboard.server.common.data.HasLabel; @@ -33,13 +35,18 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.common.data.query.EntityData; +import org.thingsboard.server.common.data.query.EntityDataPageLink; import org.thingsboard.server.common.data.query.EntityDataQuery; import org.thingsboard.server.common.data.query.EntityFilterType; +import org.thingsboard.server.common.data.query.EntityKey; +import org.thingsboard.server.common.data.query.EntityListFilter; import org.thingsboard.server.common.data.query.RelationsQueryFilter; import org.thingsboard.server.dao.exception.IncorrectParameterException; +import java.util.List; import java.util.Optional; import java.util.function.Function; +import java.util.stream.Collectors; import static org.thingsboard.server.common.data.id.EntityId.NULL_UUID; import static org.thingsboard.server.dao.service.Validator.validateEntityDataPageLink; @@ -60,13 +67,14 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe private EntityQueryDao entityQueryDao; @Autowired + @Lazy EntityServiceRegistry entityServiceRegistry; @Override public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { log.trace("Executing countEntitiesByQuery, tenantId [{}], customerId [{}], query [{}]", tenantId, customerId, query); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validateEntityCountQuery(query); return this.entityQueryDao.countEntitiesByQuery(tenantId, customerId, query); } @@ -74,10 +82,56 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe @Override public PageData findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { log.trace("Executing findEntityDataByQuery, tenantId [{}], customerId [{}], query [{}]", tenantId, customerId, query); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validateEntityDataQuery(query); - return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, query); + + if (EntityFilterType.RELATIONS_QUERY.equals(query.getEntityFilter().getType()) + || EntityFilterType.SINGLE_ENTITY.equals(query.getEntityFilter().getType()) + || StringUtils.isNotEmpty(query.getPageLink().getTextSearch())) { + return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, query); + } + + // 1 step - find entity data by filter and sort columns + PageData entityDataByQuery = findEntityIdsByFilterAndSorterColumns(tenantId, customerId, query); + if (entityDataByQuery == null || entityDataByQuery.getData().isEmpty()) { + return entityDataByQuery; + } + // 2 step - find entity data by entity ids from the 1st step + PageData result = findEntityDataByEntityIds(tenantId, customerId, query, entityDataByQuery.getData()); + return new PageData<>(result.getData(), entityDataByQuery.getTotalPages(), entityDataByQuery.getTotalElements(), entityDataByQuery.hasNext()); + } + + private PageData findEntityIdsByFilterAndSorterColumns(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { + List entityFields = null; + List latestValues = null; + if (query.getPageLink().getSortOrder() != null) { + if (query.getEntityFields() != null) { + entityFields = query.getEntityFields().stream() + .filter(entityKey -> entityKey.getKey().equals(query.getPageLink().getSortOrder().getKey().getKey())) + .collect(Collectors.toList()); + } + if (query.getLatestValues() != null) { + latestValues = query.getLatestValues().stream() + .filter(entityKey -> entityKey.getKey().equals(query.getPageLink().getSortOrder().getKey().getKey())) + .collect(Collectors.toList()); + } + } + EntityDataQuery entityQuery = new EntityDataQuery(query.getEntityFilter(), query.getPageLink(), entityFields, latestValues, query.getKeyFilters()); + return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, entityQuery); + } + + private PageData findEntityDataByEntityIds(TenantId tenantId, CustomerId customerId, EntityDataQuery query, List data) { + List entityIds = data.stream().map(d -> d.getEntityId().getId().toString()).toList(); + EntityType entityType = data.isEmpty() ? null : data.get(0).getEntityId().getEntityType(); + + EntityListFilter filter = new EntityListFilter(); + filter.setEntityType(entityType); + filter.setEntityList(entityIds); + + EntityDataPageLink pageLink = new EntityDataPageLink(query.getPageLink().getPageSize(), 0, null, query.getPageLink().getSortOrder()); + EntityDataQuery entityQuery = new EntityDataQuery(filter, pageLink, query.getEntityFields(), query.getLatestValues(), null); + return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, entityQuery); } @Override @@ -131,8 +185,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe } private CustomerId getCustomerId(HasId entity) { - if (entity instanceof HasCustomerId) { - HasCustomerId hasCustomerId = (HasCustomerId) entity; + if (entity instanceof HasCustomerId hasCustomerId) { CustomerId customerId = hasCustomerId.getCustomerId(); if (customerId == null) { customerId = NULL_CUSTOMER_ID; @@ -174,4 +227,5 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe throw new IncorrectParameterException("Relation query filter root entity should not be blank"); } } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java b/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java index 40064fbbde..364d3a12f3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java @@ -15,17 +15,14 @@ */ package org.thingsboard.server.dao.entity; +import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.context.ApplicationContext; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.context.event.EventListener; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -34,14 +31,13 @@ import java.util.Optional; @Slf4j public class DefaultEntityServiceRegistry implements EntityServiceRegistry { - private final ApplicationContext applicationContext; + private final List entityDaoServices; private final Map entityDaoServicesMap = new HashMap<>(); - @EventListener(ContextRefreshedEvent.class) - @Order(Ordered.HIGHEST_PRECEDENCE) + @PostConstruct public void init() { log.debug("Initializing EntityServiceRegistry on ContextRefreshedEvent"); - applicationContext.getBeansOfType(EntityDaoService.class).values().forEach(entityDaoService -> { + entityDaoServices.forEach(entityDaoService -> { EntityType entityType = entityDaoService.getEntityType(); entityDaoServicesMap.put(entityType, entityDaoService); if (EntityType.RULE_CHAIN.equals(entityType)) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java index a03c336232..faf70654cb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java @@ -145,15 +145,15 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); customerEntityViewsUnAssigner.removeEntities(tenantId, customerId); } @Override public EntityViewInfo findEntityViewInfoById(TenantId tenantId, EntityViewId entityViewId) { log.trace("Executing findEntityViewInfoById [{}]", entityViewId); - validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId); + validateId(entityViewId, id -> INCORRECT_ENTITY_VIEW_ID + id); return entityViewDao.findEntityViewInfoById(tenantId, entityViewId.getId()); } @@ -165,7 +165,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService INCORRECT_ENTITY_VIEW_ID + id); return cache.getOrFetchFromDB(EntityViewCacheKey.byId(entityViewId), () -> entityViewDao.findById(tenantId, entityViewId.getId()) , EntityViewCacheValue::getEntityView, v -> new EntityViewCacheValue(v, null), true, putInCache); @@ -174,7 +174,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return cache.getAndPutInTransaction(EntityViewCacheKey.byName(tenantId, name), () -> entityViewDao.findEntityViewByTenantIdAndName(tenantId.getId(), name).orElse(null) , EntityViewCacheValue::getEntityView, v -> new EntityViewCacheValue(v, null), true); @@ -184,7 +184,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findEntityViewsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return entityViewDao.findEntityViewsByTenantId(tenantId.getId(), pageLink); } @@ -192,7 +192,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewInfosByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findEntityViewInfosByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return entityViewDao.findEntityViewInfosByTenantId(tenantId.getId(), pageLink); } @@ -200,18 +200,18 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewByTenantIdAndType(TenantId tenantId, PageLink pageLink, String type) { log.trace("Executing findEntityViewByTenantIdAndType, tenantId [{}], pageLink [{}], type [{}]", tenantId, pageLink, type); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); - validateString(type, "Incorrect type " + type); + validateString(type, t -> "Incorrect type " + t); return entityViewDao.findEntityViewsByTenantIdAndType(tenantId.getId(), type, pageLink); } @Override public PageData findEntityViewInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { log.trace("Executing findEntityViewInfosByTenantIdAndType, tenantId [{}], pageLink [{}], type [{}]", tenantId, pageLink, type); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); - validateString(type, "Incorrect type " + type); + validateString(type, t -> "Incorrect type " + t); return entityViewDao.findEntityViewInfosByTenantIdAndType(tenantId.getId(), type, pageLink); } @@ -220,8 +220,8 @@ public class EntityViewServiceImpl extends AbstractCachedEntityServiceINCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); return entityViewDao.findEntityViewsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); @@ -231,8 +231,8 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewInfosByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findEntityViewInfosByTenantIdAndCustomerId, tenantId [{}], customerId [{}]," + " pageLink [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); return entityViewDao.findEntityViewInfosByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); @@ -242,10 +242,10 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, PageLink pageLink, String type) { log.trace("Executing findEntityViewsByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}]," + " pageLink [{}], type [{}]", tenantId, customerId, pageLink, type); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); - validateString(type, "Incorrect type " + type); + validateString(type, t -> "Incorrect type " + t); return entityViewDao.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); } @@ -254,10 +254,10 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewInfosByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) { log.trace("Executing findEntityViewInfosByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}]," + " pageLink [{}], type [{}]", tenantId, customerId, pageLink, type); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); - validateString(type, "Incorrect type " + type); + validateString(type, t -> "Incorrect type " + t); return entityViewDao.findEntityViewInfosByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); } @@ -291,15 +291,15 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewByIdAsync(TenantId tenantId, EntityViewId entityViewId) { log.trace("Executing findEntityViewById [{}]", entityViewId); - validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId); + validateId(entityViewId, id -> INCORRECT_ENTITY_VIEW_ID + id); return entityViewDao.findByIdAsync(tenantId, entityViewId.getId()); } @Override public ListenableFuture> findEntityViewsByTenantIdAndEntityIdAsync(TenantId tenantId, EntityId entityId) { log.trace("Executing findEntityViewsByTenantIdAndEntityIdAsync, tenantId [{}], entityId [{}]", tenantId, entityId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(entityId.getId(), "Incorrect entityId" + entityId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(entityId.getId(), id -> "Incorrect entityId" + id); return service.submit(() -> cache.getAndPutInTransaction(EntityViewCacheKey.byEntityId(tenantId, entityId), () -> entityViewDao.findEntityViewsByTenantIdAndEntityId(tenantId.getId(), entityId.getId()), @@ -309,8 +309,8 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId) { log.trace("Executing findEntityViewsByTenantIdAndEntityId, tenantId [{}], entityId [{}]", tenantId, entityId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(entityId.getId(), "Incorrect entityId" + entityId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(entityId.getId(), id -> "Incorrect entityId" + id); return cache.getAndPutInTransaction(EntityViewCacheKey.byEntityId(tenantId, entityId), () -> entityViewDao.findEntityViewsByTenantIdAndEntityId(tenantId.getId(), entityId.getId()), @@ -326,7 +326,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService INCORRECT_ENTITY_VIEW_ID + id); EntityView entityView = entityViewDao.findById(tenantId, entityViewId.getId()); entityViewDao.removeById(tenantId, entityViewId.getId()); publishEvictEvent(new EntityViewEvictEvent(entityView.getTenantId(), entityView.getId(), entityView.getEntityId(), null, entityView.getName(), null)); @@ -336,7 +336,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); tenantEntityViewRemover.removeEntities(tenantId, tenantId); } @@ -348,7 +348,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService> findEntityViewTypesByTenantId(TenantId tenantId) { log.trace("Executing findEntityViewTypesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); ListenableFuture> tenantEntityViewTypes = entityViewDao.findTenantEntityViewTypesAsync(tenantId.getId()); return Futures.transform(tenantEntityViewTypes, entityViewTypes -> { @@ -406,8 +406,8 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { log.trace("Executing findEntityViewsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); validatePageLink(pageLink); return entityViewDao.findEntityViewsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); } @@ -415,9 +415,9 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink) { log.trace("Executing findEntityViewsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}], pageLink [{}]", tenantId, edgeId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return entityViewDao.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 2fe56f5d1f..e1771c8a97 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -427,6 +427,7 @@ public class ModelConstants { */ public static final String OAUTH2_PARAMS_TABLE_NAME = "oauth2_params"; public static final String OAUTH2_PARAMS_ENABLED_PROPERTY = "enabled"; + public static final String OAUTH2_PARAMS_EDGE_ENABLED_PROPERTY = "edge_enabled"; public static final String OAUTH2_PARAMS_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; public static final String OAUTH2_REGISTRATION_TABLE_NAME = "oauth2_registration"; @@ -610,6 +611,14 @@ public class ModelConstants { public static final String QUEUE_TABLE_NAME = "queue"; public static final String QUEUE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; + /** + * Tenant queue stats constants. + */ + public static final String QUEUE_STATS_TABLE_NAME = "queue_stats"; + public static final String QUEUE_STATS_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; + public static final String QUEUE_STATS_QUEUE_NAME_PROPERTY = "queue_name"; + public static final String QUEUE_STATS_SERVICE_ID_PROPERTY = "service_id"; + /** * Notification constants */ diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java index adbbeb7fd8..4983172d3e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java @@ -39,6 +39,9 @@ public class OAuth2ParamsEntity extends BaseSqlEntity { @Column(name = ModelConstants.OAUTH2_PARAMS_ENABLED_PROPERTY) private Boolean enabled; + @Column(name = ModelConstants.OAUTH2_PARAMS_EDGE_ENABLED_PROPERTY) + private Boolean edgeEnabled; + @Column(name = ModelConstants.OAUTH2_PARAMS_TENANT_ID_PROPERTY) private UUID tenantId; @@ -48,6 +51,7 @@ public class OAuth2ParamsEntity extends BaseSqlEntity { } this.setCreatedTime(oauth2Params.getCreatedTime()); this.enabled = oauth2Params.isEnabled(); + this.edgeEnabled = oauth2Params.isEdgeEnabled(); if (oauth2Params.getTenantId() != null) { this.tenantId = oauth2Params.getTenantId().getId(); } @@ -60,6 +64,7 @@ public class OAuth2ParamsEntity extends BaseSqlEntity { oauth2Params.setCreatedTime(createdTime); oauth2Params.setTenantId(TenantId.fromUUID(tenantId)); oauth2Params.setEnabled(enabled); + oauth2Params.setEdgeEnabled(edgeEnabled); return oauth2Params; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java new file mode 100644 index 0000000000..5b9ec291a2 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java @@ -0,0 +1,69 @@ +/** + * 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.dao.model.sql; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.QueueStatsId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.queue.QueueStats; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import java.util.UUID; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = ModelConstants.QUEUE_STATS_TABLE_NAME) +public class QueueStatsEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.QUEUE_STATS_TENANT_ID_PROPERTY) + private UUID tenantId; + + @Column(name = ModelConstants.QUEUE_STATS_QUEUE_NAME_PROPERTY) + private String queueName; + + @Column(name = ModelConstants.QUEUE_STATS_SERVICE_ID_PROPERTY) + private String serviceId; + + public QueueStatsEntity() { + } + + public QueueStatsEntity(QueueStats queueStats) { + if (queueStats.getId() != null) { + this.setId(queueStats.getId().getId()); + } + this.setCreatedTime(queueStats.getCreatedTime()); + this.tenantId = DaoUtil.getId(queueStats.getTenantId()); + this.queueName = queueStats.getQueueName(); + this.serviceId = queueStats.getServiceId(); + } + + @Override + public QueueStats toData() { + QueueStats queueStats = new QueueStats(new QueueStatsId(getUuid())); + queueStats.setCreatedTime(createdTime); + queueStats.setTenantId(new TenantId(tenantId)); + queueStats.setQueueName(queueName); + queueStats.setServiceId(serviceId); + return queueStats; + } +} \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java index ed456a93cf..8d0faaca92 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java @@ -64,14 +64,14 @@ public class OAuth2ConfigTemplateServiceImpl extends AbstractEntityService imple @Override public Optional findClientRegistrationTemplateByProviderId(String providerId) { log.trace("Executing findClientRegistrationTemplateByProviderId [{}]", providerId); - validateString(providerId, INCORRECT_CLIENT_REGISTRATION_PROVIDER_ID + providerId); + validateString(providerId, id -> INCORRECT_CLIENT_REGISTRATION_PROVIDER_ID + id); return clientRegistrationTemplateDao.findByProviderId(providerId); } @Override public OAuth2ClientRegistrationTemplate findClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId) { log.trace("Executing findClientRegistrationTemplateById [{}]", templateId); - validateId(templateId, INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + templateId); + validateId(templateId, id -> INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + id); return clientRegistrationTemplateDao.findById(TenantId.SYS_TENANT_ID, templateId.getId()); } @@ -84,7 +84,7 @@ public class OAuth2ConfigTemplateServiceImpl extends AbstractEntityService imple @Override public void deleteClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId) { log.trace("Executing deleteClientRegistrationTemplateById [{}]", templateId); - validateId(templateId, INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + templateId); + validateId(templateId, id -> INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + id); clientRegistrationTemplateDao.removeById(TenantId.SYS_TENANT_ID, templateId.getId()); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 58be6a0770..6535b54a2c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.common.data.oauth2.TenantNameStrategyType; import org.thingsboard.server.dao.entity.AbstractEntityService; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; @@ -58,6 +59,7 @@ import static org.thingsboard.server.dao.service.Validator.validateString; @Slf4j @Service public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Service { + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; public static final String INCORRECT_CLIENT_REGISTRATION_ID = "Incorrect clientRegistrationId "; public static final String INCORRECT_DOMAIN_NAME = "Incorrect domainName "; @@ -84,7 +86,7 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se } catch (IllegalArgumentException e){ throw new IncorrectParameterException(INCORRECT_DOMAIN_SCHEME); } - validateString(domainName, INCORRECT_DOMAIN_NAME + domainName); + validateString(domainName, dn -> INCORRECT_DOMAIN_NAME + dn); return oauth2RegistrationDao.findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType( Arrays.asList(domainScheme, SchemeType.MIXED), domainName, pkgName, platformType) .stream() @@ -116,6 +118,7 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se }); } }); + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(TenantId.SYS_TENANT_ID).entity(oauth2Info).build()); } @Override @@ -123,7 +126,8 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se log.trace("Executing findOAuth2Info"); OAuth2Info oauth2Info = new OAuth2Info(); List oauth2ParamsList = oauth2ParamsDao.find(TenantId.SYS_TENANT_ID); - oauth2Info.setEnabled(oauth2ParamsList.stream().anyMatch(param -> param.isEnabled())); + oauth2Info.setEnabled(oauth2ParamsList.stream().anyMatch(OAuth2Params::isEnabled)); + oauth2Info.setEdgeEnabled(oauth2ParamsList.stream().anyMatch(OAuth2Params::isEdgeEnabled)); List oauth2ParamsInfos = new ArrayList<>(); oauth2Info.setOauth2ParamsInfos(oauth2ParamsInfos); oauth2ParamsList.stream().sorted(Comparator.comparing(BaseData::getUuidId)).forEach(oauth2Params -> { @@ -138,14 +142,14 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se @Override public OAuth2Registration findRegistration(UUID id) { log.trace("Executing findRegistration [{}]", id); - validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id); + validateId(id, uuid -> INCORRECT_CLIENT_REGISTRATION_ID + uuid); return oauth2RegistrationDao.findById(null, id); } @Override public String findAppSecret(UUID id, String pkgName) { log.trace("Executing findAppSecret [{}][{}]", id, pkgName); - validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id); + validateId(id, uuid -> INCORRECT_CLIENT_REGISTRATION_ID + uuid); validateString(pkgName, "Incorrect package name"); return oauth2RegistrationDao.findAppSecret(id, pkgName); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java index 4e22ea858b..8d30fc905d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java @@ -88,6 +88,7 @@ public class OAuth2Utils { public static OAuth2Params infoToOAuth2Params(OAuth2Info oauth2Info) { OAuth2Params oauth2Params = new OAuth2Params(); oauth2Params.setEnabled(oauth2Info.isEnabled()); + oauth2Params.setEdgeEnabled(oauth2Info.isEdgeEnabled()); oauth2Params.setTenantId(TenantId.SYS_TENANT_ID); return oauth2Params; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java index e4a4865f02..9a29845c66 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java @@ -159,14 +159,14 @@ public class BaseOtaPackageService extends AbstractCachedEntityService INCORRECT_OTA_PACKAGE_ID + id); return otaPackageDao.findById(tenantId, otaPackageId.getId()); } @Override public OtaPackageInfo findOtaPackageInfoById(TenantId tenantId, OtaPackageId otaPackageId) { log.trace("Executing findOtaPackageInfoById [{}]", otaPackageId); - validateId(otaPackageId, INCORRECT_OTA_PACKAGE_ID + otaPackageId); + validateId(otaPackageId, id -> INCORRECT_OTA_PACKAGE_ID + id); return cache.getAndPutInTransaction(new OtaPackageCacheKey(otaPackageId), () -> otaPackageInfoDao.findById(tenantId, otaPackageId.getId()), true); } @@ -174,14 +174,14 @@ public class BaseOtaPackageService extends AbstractCachedEntityService findOtaPackageInfoByIdAsync(TenantId tenantId, OtaPackageId otaPackageId) { log.trace("Executing findOtaPackageInfoByIdAsync [{}]", otaPackageId); - validateId(otaPackageId, INCORRECT_OTA_PACKAGE_ID + otaPackageId); + validateId(otaPackageId, id -> INCORRECT_OTA_PACKAGE_ID + id); return otaPackageInfoDao.findByIdAsync(tenantId, otaPackageId.getId()); } @Override public PageData findTenantOtaPackagesByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findTenantOtaPackagesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return otaPackageInfoDao.findOtaPackageInfoByTenantId(tenantId, pageLink); } @@ -189,7 +189,7 @@ public class BaseOtaPackageService extends AbstractCachedEntityService findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, PageLink pageLink) { log.trace("Executing findTenantOtaPackagesByTenantIdAndHasData, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return otaPackageInfoDao.findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, otaPackageType, pageLink); } @@ -197,7 +197,7 @@ public class BaseOtaPackageService extends AbstractCachedEntityService INCORRECT_OTA_PACKAGE_ID + id); try { otaPackageDao.removeById(tenantId, otaPackageId.getId()); publishEvictEvent(new OtaPackageCacheEvictEvent(otaPackageId)); @@ -226,7 +226,7 @@ public class BaseOtaPackageService extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); tenantOtaPackageRemover.removeEntities(tenantId, tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueStatsService.java b/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueStatsService.java new file mode 100644 index 0000000000..2274a782bb --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueStatsService.java @@ -0,0 +1,90 @@ +/** + * 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.dao.queue; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.QueueStatsId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.queue.QueueStats; +import org.thingsboard.server.dao.entity.AbstractEntityService; +import org.thingsboard.server.dao.service.DataValidator; + +import java.util.List; +import java.util.Optional; + +import static org.thingsboard.server.dao.service.Validator.validateId; + +@Service("QueueStatsDaoService") +@Slf4j +@RequiredArgsConstructor +public class BaseQueueStatsService extends AbstractEntityService implements QueueStatsService { + + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; + + private final QueueStatsDao queueStatsDao; + + private final DataValidator queueStatsValidator; + + @Override + public QueueStats save(TenantId tenantId, QueueStats queueStats) { + log.trace("Executing save [{}]", queueStats); + queueStatsValidator.validate(queueStats, QueueStats::getTenantId); + return queueStatsDao.save(tenantId, queueStats); + } + + @Override + public QueueStats findQueueStatsById(TenantId tenantId, QueueStatsId queueStatsId) { + log.trace("Executing findQueueStatsById [{}]", queueStatsId); + validateId(queueStatsId, id -> "Incorrect queueStatsId " + id); + return queueStatsDao.findById(tenantId, queueStatsId.getId()); + } + + @Override + public QueueStats findByTenantIdAndNameAndServiceId(TenantId tenantId, String queueName, String serviceId) { + log.trace("Executing findByTenantIdAndNameAndServiceId, tenantId: [{}], queueName: [{}], serviceId: [{}]", tenantId, queueName, serviceId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + return queueStatsDao.findByTenantIdQueueNameAndServiceId(tenantId, queueName, serviceId); + } + + @Override + public List findByTenantId(TenantId tenantId) { + log.trace("Executing findByTenantId, tenantId: [{}]", tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + return queueStatsDao.findByTenantId(tenantId); + } + + @Override + public void deleteByTenantId(TenantId tenantId) { + log.trace("Executing deleteByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + queueStatsDao.deleteByTenantId(tenantId); + } + + @Override + public Optional> findEntity(TenantId tenantId, EntityId entityId) { + return Optional.ofNullable(findQueueStatsById(tenantId, new QueueStatsId(entityId.getId()))); + } + + @Override + public EntityType getEntityType() { + return EntityType.QUEUE_STATS; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/queue/QueueStatsDao.java b/dao/src/main/java/org/thingsboard/server/dao/queue/QueueStatsDao.java new file mode 100644 index 0000000000..1c3db8bb54 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/queue/QueueStatsDao.java @@ -0,0 +1,32 @@ +/** + * 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.dao.queue; + +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.queue.QueueStats; +import org.thingsboard.server.dao.Dao; + +import java.util.List; + +public interface QueueStatsDao extends Dao { + + QueueStats findByTenantIdQueueNameAndServiceId(TenantId tenantId, String queueName, String serviceId); + + List findByTenantId(TenantId tenantId); + + void deleteByTenantId(TenantId tenantId); + +} \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java index 89cfff08d1..ec6af730ea 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java @@ -513,7 +513,7 @@ public class BaseRelationService implements RelationService { @Override public List findRuleNodeToRuleChainRelations(TenantId tenantId, RuleChainType ruleChainType, int limit) { log.trace("Executing findRuleNodeToRuleChainRelations, tenantId [{}], ruleChainType {} and limit {}", tenantId, ruleChainType, limit); - validateId(tenantId, "Invalid tenant id: " + tenantId); + validateId(tenantId, id -> "Invalid tenant id: " + id); return relationDao.findRuleNodeToRuleChainRelations(ruleChainType, limit); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java index ef05229124..953037a864 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -256,7 +256,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic var tenantId = imageInfo.getTenantId(); var imageId = imageInfo.getId(); log.trace("Executing deleteImage [{}] [{}]", tenantId, imageId); - Validator.validateId(imageId, INCORRECT_RESOURCE_ID + imageId); + Validator.validateId(imageId, id -> INCORRECT_RESOURCE_ID + id); TbImageDeleteResult.TbImageDeleteResultBuilder result = TbImageDeleteResult.builder(); boolean success = true; if (!force) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 8329e4f305..ad35ccd9b1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -116,14 +116,14 @@ public class BaseResourceService extends AbstractCachedEntityService INCORRECT_RESOURCE_ID + id); return resourceDao.findById(tenantId, resourceId.getId()); } @Override public TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId) { log.trace("Executing findResourceInfoById [{}] [{}]", tenantId, resourceId); - Validator.validateId(resourceId, INCORRECT_RESOURCE_ID + resourceId); + Validator.validateId(resourceId, id -> INCORRECT_RESOURCE_ID + id); return cache.getAndPutInTransaction(new ResourceInfoCacheKey(tenantId, resourceId), () -> resourceInfoDao.findById(tenantId, resourceId.getId()), true); @@ -138,7 +138,7 @@ public class BaseResourceService extends AbstractCachedEntityService findResourceInfoByIdAsync(TenantId tenantId, TbResourceId resourceId) { log.trace("Executing findResourceInfoById [{}] [{}]", tenantId, resourceId); - Validator.validateId(resourceId, INCORRECT_RESOURCE_ID + resourceId); + Validator.validateId(resourceId, id -> INCORRECT_RESOURCE_ID + id); return resourceInfoDao.findByIdAsync(tenantId, resourceId.getId()); } @@ -150,7 +150,7 @@ public class BaseResourceService extends AbstractCachedEntityService INCORRECT_RESOURCE_ID + id); if (!force) { resourceValidator.validateDelete(tenantId, resourceId); } @@ -163,7 +163,7 @@ public class BaseResourceService extends AbstractCachedEntityService findAllTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { TenantId tenantId = filter.getTenantId(); log.trace("Executing findAllTenantResourcesByTenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return resourceInfoDao.findAllTenantResourcesByTenantId(filter, pageLink); } @@ -171,35 +171,35 @@ public class BaseResourceService extends AbstractCachedEntityService findTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { TenantId tenantId = filter.getTenantId(); log.trace("Executing findTenantResourcesByTenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return resourceInfoDao.findTenantResourcesByTenantId(filter, pageLink); } @Override public List findTenantResourcesByResourceTypeAndObjectIds(TenantId tenantId, ResourceType resourceType, String[] objectIds) { log.trace("Executing findTenantResourcesByResourceTypeAndObjectIds [{}][{}][{}]", tenantId, resourceType, objectIds); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return resourceDao.findResourcesByTenantIdAndResourceType(tenantId, resourceType, objectIds, null); } @Override public PageData findAllTenantResources(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAllTenantResources [{}][{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return resourceDao.findAllByTenantId(tenantId, pageLink); } @Override public PageData findTenantResourcesByResourceTypeAndPageLink(TenantId tenantId, ResourceType resourceType, PageLink pageLink) { log.trace("Executing findTenantResourcesByResourceTypeAndPageLink [{}][{}][{}]", tenantId, resourceType, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return resourceDao.findResourcesByTenantIdAndResourceType(tenantId, resourceType, pageLink); } @Override public void deleteResourcesByTenantId(TenantId tenantId) { log.trace("Executing deleteResourcesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); tenantResourcesRemover.removeEntities(tenantId, tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java b/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java index 9c65d1efe9..312c0c998b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java @@ -54,15 +54,15 @@ public class BaseRpcService implements RpcService { @Override public void deleteRpc(TenantId tenantId, RpcId rpcId) { log.trace("Executing deleteRpc, tenantId [{}], rpcId [{}]", tenantId, rpcId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(rpcId, INCORRECT_RPC_ID + rpcId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(rpcId, id -> INCORRECT_RPC_ID + id); rpcDao.removeById(tenantId, rpcId.getId()); } @Override public void deleteAllRpcByTenantId(TenantId tenantId) { log.trace("Executing deleteAllRpcByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); tenantRpcRemover.removeEntities(tenantId, tenantId); } @@ -74,23 +74,23 @@ public class BaseRpcService implements RpcService { @Override public Rpc findById(TenantId tenantId, RpcId rpcId) { log.trace("Executing findById, tenantId [{}], rpcId [{}]", tenantId, rpcId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(rpcId, INCORRECT_RPC_ID + rpcId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(rpcId, id -> INCORRECT_RPC_ID + id); return rpcDao.findById(tenantId, rpcId.getId()); } @Override public ListenableFuture findRpcByIdAsync(TenantId tenantId, RpcId rpcId) { log.trace("Executing findRpcByIdAsync, tenantId [{}], rpcId: [{}]", tenantId, rpcId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(rpcId, INCORRECT_RPC_ID + rpcId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(rpcId, id -> INCORRECT_RPC_ID + id); return rpcDao.findByIdAsync(tenantId, rpcId.getId()); } @Override public PageData findAllByDeviceIdAndStatus(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) { log.trace("Executing findAllByDeviceIdAndStatus, tenantId [{}], deviceId [{}], rpcStatus [{}], pageLink [{}]", tenantId, deviceId, rpcStatus, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return rpcDao.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink); } @@ -98,7 +98,7 @@ public class BaseRpcService implements RpcService { @Override public PageData findAllByDeviceId(TenantId tenantId, DeviceId deviceId, PageLink pageLink) { log.trace("Executing findAllByDeviceIdAndStatus, tenantId [{}], deviceId [{}], pageLink [{}]", tenantId, deviceId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return rpcDao.findAllByDeviceId(tenantId, deviceId, pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java b/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java index 79b73c69aa..d2e867e763 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java @@ -30,5 +30,6 @@ public interface RpcDao extends Dao { PageData findAllRpcByTenantId(TenantId tenantId, PageLink pageLink); - Long deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime); + int deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 2b343a3df8..4f196545a1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -651,8 +651,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Override public PageData findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { log.trace("Executing findRuleChainsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); - Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); - Validator.validateId(edgeId, "Incorrect edgeId " + edgeId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(edgeId, id -> "Incorrect edgeId " + id); Validator.validatePageLink(pageLink); return ruleChainDao.findRuleChainsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); } @@ -711,14 +711,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Override public PageData findAutoAssignToEdgeRuleChainsByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAutoAssignToEdgeRuleChainsByTenantId, tenantId [{}], pageLink {}", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return ruleChainDao.findAutoAssignToEdgeRuleChainsByTenantId(tenantId.getId(), pageLink); } @Override public List findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String search) { log.trace("Executing findRuleNodes, tenantId [{}], type {}, search {}", tenantId, type, search); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validateString(type, "Incorrect type of the rule node"); validateString(search, "Incorrect search text"); return ruleNodeDao.findRuleNodesByTenantIdAndType(tenantId, type, search); @@ -727,7 +727,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Override public List findRuleNodesByTenantIdAndType(TenantId tenantId, String type) { log.trace("Executing findRuleNodes, tenantId [{}], type {}", tenantId, type); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validateString(type, "Incorrect type of the rule node"); return ruleNodeDao.findRuleNodesByTenantIdAndType(tenantId, type, ""); } @@ -761,7 +761,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Override public List findAllRuleNodesByIds(List ruleNodeIds) { log.trace("Executing findAllRuleNodesByIds, ruleNodeIds {}", ruleNodeIds); - validateIds(ruleNodeIds, "Incorrect ruleNodeIds " + ruleNodeIds); + validateIds(ruleNodeIds, ids -> "Incorrect ruleNodeIds " + ids); assert ruleNodeIds.size() <= 1024; return ruleNodeDao.findAllRuleNodeByIds(ruleNodeIds); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java index b3a121baf6..dab28e298b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java @@ -38,9 +38,10 @@ public class Validator { * This method validate EntityId entity id. If entity id is invalid than throw * IncorrectParameterException exception * - * @param entityId the entityId - * @param errorMessage the error message for exception + * @param entityId the entityId + * @param errorMessage the error message for exception */ + @Deprecated public static void validateEntityId(EntityId entityId, String errorMessage) { if (entityId == null || entityId.getId() == null) { throw new IncorrectParameterException(errorMessage); @@ -51,8 +52,8 @@ public class Validator { * This method validate EntityId entity id. If entity id is invalid than throw * IncorrectParameterException exception * - * @param entityId the entityId - * @param errorMessageFunction the error message for exception that apply entityId + * @param entityId the entityId + * @param errorMessageFunction the error message function for exception that applies entityId */ public static void validateEntityId(EntityId entityId, Function errorMessageFunction) { if (entityId == null || entityId.getId() == null) { @@ -64,8 +65,8 @@ public class Validator { * This method validate String string. If string is invalid than throw * IncorrectParameterException exception * - * @param val the val - * @param errorMessage the error message for exception + * @param val the val + * @param errorMessage the error message for exception */ public static void validateString(String val, String errorMessage) { if (val == null || val.isEmpty()) { @@ -73,12 +74,12 @@ public class Validator { } } - /* + /** * This method validate String string. If string is invalid than throw * IncorrectParameterException exception * - * @param val the value - * @param errorMessageFunction the error message function that apply value + * @param val the value + * @param errorMessageFunction the error message function that applies value */ public static void validateString(String val, Function errorMessageFunction) { if (val == null || val.isEmpty()) { @@ -90,8 +91,8 @@ public class Validator { * This method validate long value. If value isn't positive than throw * IncorrectParameterException exception * - * @param val the val - * @param errorMessage the error message for exception + * @param val the val + * @param errorMessage the error message for exception */ public static void validatePositiveNumber(long val, String errorMessage) { if (val <= 0) { @@ -103,36 +104,78 @@ public class Validator { * This method validate UUID id. If id is null than throw * IncorrectParameterException exception * - * @param id the id - * @param errorMessage the error message for exception + * @param id the id + * @param errorMessage the error message for exception */ + @Deprecated public static void validateId(UUID id, String errorMessage) { if (id == null) { throw new IncorrectParameterException(errorMessage); } } + /** + * This method validate UUID id. If id is null than throw + * IncorrectParameterException exception + * + * @param id the id + * @param errorMessageFunction the error message function for exception that applies id + */ + public static void validateId(UUID id, Function errorMessageFunction) { + if (id == null) { + throw new IncorrectParameterException(errorMessageFunction.apply(id)); + } + } /** * This method validate UUIDBased id. If id is null than throw * IncorrectParameterException exception * - * @param id the id - * @param errorMessage the error message for exception + * @param id the id + * @param errorMessage the error message for exception */ + @Deprecated public static void validateId(UUIDBased id, String errorMessage) { if (id == null || id.getId() == null) { throw new IncorrectParameterException(errorMessage); } } + /** + * This method validate UUIDBased id. If id is null than throw + * IncorrectParameterException exception + * + * @param id the id + * @param errorMessageFunction the error message function for exception that applies id + */ + public static void validateId(UUIDBased id, Function errorMessageFunction) { + if (id == null || id.getId() == null) { + throw new IncorrectParameterException(errorMessageFunction.apply(id)); + } + } + + /** + * This method validate UUIDBased id. If id is null than throw + * IncorrectParameterException exception + * + * @param id the id + * @param ids the list of ids + * @param errorMessageFunction the error message function for exception that applies ids + */ + static void validateId(UUIDBased id, List ids, Function, String> errorMessageFunction) { + if (id == null) { + throw new IncorrectParameterException(errorMessageFunction.apply(ids)); + } + } + /** * This method validate list of UUIDBased ids. If at least one of the ids is null than throw * IncorrectParameterException exception * - * @param ids the list of ids - * @param errorMessage the error message for exception + * @param ids the list of ids + * @param errorMessage the error message for exception */ + @Deprecated public static void validateIds(List ids, String errorMessage) { if (ids == null || ids.isEmpty()) { throw new IncorrectParameterException(errorMessage); @@ -143,11 +186,28 @@ public class Validator { } } + /** + * This method validate list of UUIDBased ids. If at least one of the ids is null than throw + * IncorrectParameterException exception + * + * @param ids the list of ids + * @param errorMessageFunction the error message function for exception that applies ids + */ + public static void validateIds(List ids, Function, String> errorMessageFunction) { + if (ids == null || ids.isEmpty()) { + throw new IncorrectParameterException(errorMessageFunction.apply(ids)); + } else { + for (UUIDBased id : ids) { + validateId(id, ids, errorMessageFunction); + } + } + } + /** * This method validate PageLink page link. If pageLink is invalid than throw * IncorrectParameterException exception * - * @param pageLink the page link + * @param pageLink the page link */ public static void validatePageLink(PageLink pageLink) { if (pageLink == null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java index 5ef0b7a2ba..2d0cdbd248 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java @@ -47,9 +47,7 @@ public class AssetDataValidator extends DataValidator { @Override protected void validateCreate(TenantId tenantId, Asset asset) { - if (!BaseAssetService.TB_SERVICE_QUEUE.equals(asset.getType())) { - validateNumberOfEntitiesPerTenant(tenantId, EntityType.ASSET); - } + validateNumberOfEntitiesPerTenant(tenantId, EntityType.ASSET); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueStatsDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueStatsDataValidator.java new file mode 100644 index 0000000000..92134b98d5 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueStatsDataValidator.java @@ -0,0 +1,40 @@ +/** + * 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.dao.service.validator; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.queue.QueueStats; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; + +@Component +public class QueueStatsDataValidator extends DataValidator { + + @Override + protected void validateDataImpl(TenantId tenantId, QueueStats queueStats) { + if (queueStats.getTenantId() == null) { + throw new DataValidationException("Tenant id should be specified!."); + } + if (queueStats.getQueueName() == null) { + throw new DataValidationException("Queue name should be specified!."); + } + if (StringUtils.isEmpty(queueStats.getServiceId())) { + throw new DataValidationException("Service id should be specified!."); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java index 095e51082a..da0ca9c577 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java @@ -39,14 +39,14 @@ public class AdminSettingsServiceImpl implements AdminSettingsService { @Override public AdminSettings findAdminSettingsById(TenantId tenantId, AdminSettingsId adminSettingsId) { log.trace("Executing findAdminSettingsById [{}]", adminSettingsId); - Validator.validateId(adminSettingsId, "Incorrect adminSettingsId " + adminSettingsId); + Validator.validateId(adminSettingsId, id -> "Incorrect adminSettingsId " + id); return adminSettingsDao.findById(tenantId, adminSettingsId.getId()); } @Override public AdminSettings findAdminSettingsByKey(TenantId tenantId, String key) { log.trace("Executing findAdminSettingsByKey [{}]", key); - Validator.validateString(key, "Incorrect key " + key); + Validator.validateString(key, k -> "Incorrect key " + k); return findAdminSettingsByTenantIdAndKey(TenantId.SYS_TENANT_ID, key); } @@ -82,7 +82,7 @@ public class AdminSettingsServiceImpl implements AdminSettingsService { @Override public boolean deleteAdminSettingsByTenantIdAndKey(TenantId tenantId, String key) { log.trace("Executing deleteAdminSettings, tenantId [{}], key [{}]", tenantId, key); - Validator.validateString(key, "Incorrect key " + key); + Validator.validateString(key, k -> "Incorrect key " + k); return adminSettingsDao.removeByTenantIdAndKey(tenantId.getId(), key); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java index f216da4445..83827973ba 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java @@ -333,6 +333,9 @@ public interface AlarmRepository extends JpaRepository { @Query("SELECT a.id FROM AlarmEntity a WHERE a.tenantId = :tenantId AND a.assigneeId = :assigneeId") Page findAlarmIdsByAssigneeId(@Param("tenantId") UUID tenantId, @Param("assigneeId") UUID assigneeId, Pageable pageable); + @Query("SELECT a.id FROM AlarmEntity a WHERE a.tenantId = :tenantId AND a.originatorId = :originatorId") + Page findAlarmIdsByOriginatorId(@Param("tenantId") UUID tenantId, @Param("originatorId") UUID originatorId, Pageable pageable); + @Query(value = "SELECT create_or_update_active_alarm(:t_id, :c_id, :a_id, :a_created_ts, :a_o_id, :a_o_type, :a_type, :a_severity, " + ":a_start_ts, :a_end_ts, :a_details, :a_propagate, :a_propagate_to_owner, " + ":a_propagate_to_tenant, :a_propagation_types, :a_creation_enabled)", nativeQuery = true) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java index 1483cf8a8e..c326c6452b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java @@ -296,6 +296,12 @@ public class JpaAlarmDao extends JpaAbstractDao implements A .mapData(AlarmId::new); } + @Override + public PageData findAlarmIdsByOriginatorId(TenantId tenantId, EntityId originatorId, PageLink pageLink) { + return DaoUtil.pageToPageData(alarmRepository.findAlarmIdsByOriginatorId(tenantId.getId(), originatorId.getId(), DaoUtil.toPageable(pageLink))) + .mapData(AlarmId::new); + } + @Override public void createEntityAlarmRecord(EntityAlarm entityAlarm) { log.debug("Saving entity {}", entityAlarm); @@ -388,7 +394,7 @@ public class JpaAlarmDao extends JpaAbstractDao implements A @Override public PageData findTenantAlarmTypes(UUID tenantId, PageLink pageLink) { - Page page = alarmRepository.findTenantAlarmTypes(tenantId, Objects.toString(pageLink.getTextSearch(), ""), toPageable(pageLink)); + Page page = alarmRepository.findTenantAlarmTypes(tenantId, Objects.toString(pageLink.getTextSearch(), ""), toPageable(pageLink, false)); if (page.isEmpty()) { return PageData.emptyPageData(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java index bbe212fb0b..f65e0db1c6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java @@ -189,7 +189,7 @@ public interface AssetRepository extends JpaRepository, Expor @Param("searchText") String searchText, Pageable pageable); - Long countByTenantIdAndTypeIsNot(UUID tenantId, String type); + Long countByTenantId(UUID tenantId); @Query("SELECT externalId FROM AssetEntity WHERE id = :id") UUID getExternalIdById(@Param("id") UUID id); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java index b77559be7b..0ef4370f30 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java @@ -43,7 +43,6 @@ import java.util.Optional; import java.util.UUID; import static org.thingsboard.server.dao.DaoUtil.convertTenantEntityInfosToDto; -import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; /** * Created by Valerii Sosliuk on 5/19/2017. @@ -244,7 +243,7 @@ public class JpaAssetDao extends JpaAbstractDao implements A @Override public Long countByTenantId(TenantId tenantId) { - return assetRepository.countByTenantIdAndTypeIsNot(tenantId.getId(), TB_SERVICE_QUEUE); + return assetRepository.countByTenantId(tenantId.getId()); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java index d2b2567bd7..896fde6644 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java @@ -244,6 +244,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { entityTableMap.put(EntityType.DEVICE_PROFILE, "device_profile"); entityTableMap.put(EntityType.ASSET_PROFILE, "asset_profile"); entityTableMap.put(EntityType.TENANT_PROFILE, "tenant_profile"); + entityTableMap.put(EntityType.QUEUE_STATS, "queue_stats"); entityNameColumns.put(EntityType.DEVICE, "name"); entityNameColumns.put(EntityType.CUSTOMER, "title"); @@ -262,6 +263,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { entityNameColumns.put(EntityType.TB_RESOURCE, "search_text"); entityNameColumns.put(EntityType.EDGE, "name"); entityNameColumns.put(EntityType.QUEUE, "name"); + entityNameColumns.put(EntityType.QUEUE_STATS, "queue_name"); } public static EntityType[] RELATION_QUERY_ENTITY_TYPES = new EntityType[]{ diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java index b21d3a8de5..df285657b3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java @@ -75,6 +75,8 @@ public class EntityKeyMapping { public static final String PHONE = "phone"; public static final String ADDITIONAL_INFO = "additionalInfo"; public static final String RELATED_PARENT_ID = "parentId"; + public static final String QUEUE_NAME = "queueName"; + public static final String SERVICE_ID = "serviceId"; public static final List typedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, ADDITIONAL_INFO); public static final List widgetEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME); @@ -106,6 +108,7 @@ public class EntityKeyMapping { allowedEntityFieldMap.put(EntityType.API_USAGE_STATE, apiUsageStateEntityFields); allowedEntityFieldMap.put(EntityType.DEVICE_PROFILE, Set.of(CREATED_TIME, NAME, TYPE)); allowedEntityFieldMap.put(EntityType.ASSET_PROFILE, Set.of(CREATED_TIME, NAME)); + allowedEntityFieldMap.put(EntityType.QUEUE_STATS, new HashSet<>(Arrays.asList(CREATED_TIME, QUEUE_NAME, SERVICE_ID))); entityFieldColumnMap.put(CREATED_TIME, ModelConstants.CREATED_TIME_PROPERTY); entityFieldColumnMap.put(ENTITY_TYPE, ModelConstants.ENTITY_TYPE_PROPERTY); @@ -126,6 +129,8 @@ public class EntityKeyMapping { entityFieldColumnMap.put(PHONE, ModelConstants.PHONE_PROPERTY); entityFieldColumnMap.put(ADDITIONAL_INFO, ModelConstants.ADDITIONAL_INFO_PROPERTY); entityFieldColumnMap.put(RELATED_PARENT_ID, "parent_id"); + entityFieldColumnMap.put(QUEUE_NAME, ModelConstants.QUEUE_STATS_QUEUE_NAME_PROPERTY); + entityFieldColumnMap.put(SERVICE_ID, ModelConstants.QUEUE_STATS_SERVICE_ID_PROPERTY); Map contactBasedAliases = new HashMap<>(); contactBasedAliases.put(NAME, TITLE); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueStatsDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueStatsDao.java new file mode 100644 index 0000000000..ac57d54e90 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueStatsDao.java @@ -0,0 +1,66 @@ +/** + * 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.dao.sql.queue; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.queue.QueueStats; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.QueueStatsEntity; +import org.thingsboard.server.dao.queue.QueueStatsDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.List; +import java.util.UUID; + +@Slf4j +@Component +@SqlDao +public class JpaQueueStatsDao extends JpaAbstractDao implements QueueStatsDao { + + @Autowired + private QueueStatsRepository queueStatsRepository; + + @Override + protected Class getEntityClass() { + return QueueStatsEntity.class; + } + + @Override + protected JpaRepository getRepository() { + return queueStatsRepository; + } + + @Override + public QueueStats findByTenantIdQueueNameAndServiceId(TenantId tenantId, String queueName, String serviceId) { + return DaoUtil.getData(queueStatsRepository.findByTenantIdAndQueueNameAndServiceId(tenantId.getId(), queueName, serviceId)); + } + + @Override + public List findByTenantId(TenantId tenantId) { + return DaoUtil.convertDataList(queueStatsRepository.findByTenantId(tenantId.getId())); + } + + @Override + public void deleteByTenantId(TenantId tenantId) { + queueStatsRepository.deleteByTenantId(tenantId.getId()); + } + +} \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/queue/QueueStatsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/QueueStatsRepository.java new file mode 100644 index 0000000000..65df3a65fa --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/QueueStatsRepository.java @@ -0,0 +1,39 @@ +/** + * 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.dao.sql.queue; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.dao.model.sql.QueueStatsEntity; + +import java.util.List; +import java.util.UUID; + +public interface QueueStatsRepository extends JpaRepository { + + QueueStatsEntity findByTenantIdAndQueueNameAndServiceId(UUID tenantId, String queueName, String serviceId); + + List findByTenantId(UUID tenantId); + + @Transactional + @Modifying + @Query("DELETE FROM QueueStatsEntity t WHERE t.tenantId = :tenantId") + void deleteByTenantId(@Param("tenantId") UUID tenantId); + +} \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java index 2c16db4d34..e1939a595c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java @@ -19,6 +19,7 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; @@ -67,8 +68,9 @@ public class JpaRpcDao extends JpaAbstractDao implements RpcDao return DaoUtil.toPageData(rpcRepository.findAllByTenantId(tenantId.getId(), DaoUtil.toPageable(pageLink))); } + @Transactional @Override - public Long deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime) { + public int deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime) { return rpcRepository.deleteOutdatedRpcByTenantId(tenantId.getId(), expirationTime); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java index e04d000a65..fe7c95d249 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.rpc; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.rpc.RpcStatus; @@ -32,7 +33,8 @@ public interface RpcRepository extends JpaRepository { Page findAllByTenantId(UUID tenantId, Pageable pageable); - @Query(value = "WITH deleted AS (DELETE FROM rpc WHERE (tenant_id = :tenantId AND created_time < :expirationTime) IS TRUE RETURNING *) SELECT count(*) FROM deleted", + @Modifying + @Query(value = "DELETE FROM rpc WHERE tenant_id = :tenantId AND created_time < :expirationTime", nativeQuery = true) - Long deleteOutdatedRpcByTenantId(@Param("tenantId") UUID tenantId, @Param("expirationTime") Long expirationTime); + int deleteOutdatedRpcByTenantId(@Param("tenantId") UUID tenantId, @Param("expirationTime") Long expirationTime); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java index 02f3bc4671..acbc7041fb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java @@ -51,6 +51,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; public class TenantProfileServiceImpl extends AbstractCachedEntityService implements TenantProfileService { private static final String INCORRECT_TENANT_PROFILE_ID = "Incorrect tenantProfileId "; + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; @Autowired private TenantProfileDao tenantProfileDao; @@ -74,7 +75,7 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_PROFILE_ID + id); return cache.getAndPutInTransaction(TenantProfileCacheKey.fromId(tenantProfileId), () -> tenantProfileDao.findById(tenantId, tenantProfileId.getId()), true); } @@ -111,7 +112,8 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); + validateId(tenantProfileId, id -> INCORRECT_TENANT_PROFILE_ID + id); TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenantProfileId.getId()); if (tenantProfile != null && tenantProfile.isDefault()) { throw new DataValidationException("Deletion of Default Tenant Profile is prohibited!"); @@ -185,7 +187,8 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); + validateId(tenantProfileId, id -> INCORRECT_TENANT_PROFILE_ID + id); TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenantProfileId.getId()); if (!tenantProfile.isDefault()) { tenantProfile.setDefault(true); diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 32c145788e..a33d714319 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -94,7 +94,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return cache.getAndPutInTransaction(tenantId, () -> tenantDao.findById(tenantId, tenantId.getId()), true); } @@ -102,14 +102,14 @@ public class TenantServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return tenantDao.findTenantInfoById(tenantId, tenantId.getId()); } @Override public ListenableFuture findTenantByIdAsync(TenantId callerId, TenantId tenantId) { log.trace("Executing findTenantByIdAsync [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return tenantDao.findByIdAsync(callerId, tenantId.getId()); } @@ -154,7 +154,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); userService.deleteByTenantId(tenantId); adminSettingsService.deleteAdminSettingsByTenantId(tenantId); @@ -167,7 +167,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService "Invalid entity id " + id); ApiUsageState apiUsageState = findApiUsageStateByEntityId(entityId); if (apiUsageState != null) { apiUsageStateDao.removeById(apiUsageState.getTenantId(), apiUsageState.getUuidId()); @@ -87,7 +87,7 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A public ApiUsageState createDefaultApiUsageState(TenantId tenantId, EntityId entityId) { entityId = Objects.requireNonNullElse(entityId, tenantId); log.trace("Executing createDefaultUsageRecord [{}]", entityId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); ApiUsageState apiUsageState = new ApiUsageState(); apiUsageState.setTenantId(tenantId); apiUsageState.setEntityId(entityId); @@ -142,7 +142,7 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A @Override public ApiUsageState update(ApiUsageState apiUsageState) { log.trace("Executing save [{}]", apiUsageState.getTenantId()); - validateId(apiUsageState.getTenantId(), INCORRECT_TENANT_ID + apiUsageState.getTenantId()); + validateId(apiUsageState.getTenantId(), id -> INCORRECT_TENANT_ID + id); validateId(apiUsageState.getId(), "Can't save new usage state. Only update is allowed!"); ApiUsageState savedState = apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState); eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedState.getTenantId()).entityId(savedState.getId()) @@ -153,21 +153,21 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A @Override public ApiUsageState findTenantApiUsageState(TenantId tenantId) { log.trace("Executing findTenantUsageRecord, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return apiUsageStateDao.findTenantApiUsageState(tenantId.getId()); } @Override public ApiUsageState findApiUsageStateByEntityId(EntityId entityId) { - validateId(entityId.getId(), "Invalid entity id"); + validateId(entityId.getId(), id -> "Invalid entity id " + id); return apiUsageStateDao.findApiUsageStateByEntityId(entityId); } @Override public ApiUsageState findApiUsageStateById(TenantId tenantId, ApiUsageStateId id) { log.trace("Executing findApiUsageStateById, tenantId [{}], apiUsageStateId [{}]", tenantId, id); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(id, "Incorrect apiUsageStateId " + id); + validateId(tenantId, t -> INCORRECT_TENANT_ID + t); + validateId(id, u -> "Incorrect apiUsageStateId " + u); return apiUsageStateDao.findById(tenantId, id.getId()); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 6f3e855979..8403520e04 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -100,7 +100,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public User findUserByEmail(TenantId tenantId, String email) { log.trace("Executing findUserByEmail [{}]", email); - validateString(email, "Incorrect email " + email); + validateString(email, e -> "Incorrect email " + e); if (userLoginCaseSensitive) { return userDao.findByEmail(tenantId, email); } else { @@ -111,22 +111,22 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public User findUserByTenantIdAndEmail(TenantId tenantId, String email) { log.trace("Executing findUserByTenantIdAndEmail [{}][{}]", tenantId, email); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(email, "Incorrect email " + email); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateString(email, e -> "Incorrect email " + e); return userDao.findByTenantIdAndEmail(tenantId, email); } @Override public User findUserById(TenantId tenantId, UserId userId) { log.trace("Executing findUserById [{}]", userId); - validateId(userId, INCORRECT_USER_ID + userId); + validateId(userId, id -> INCORRECT_USER_ID + id); return userDao.findById(tenantId, userId.getId()); } @Override public ListenableFuture findUserByIdAsync(TenantId tenantId, UserId userId) { log.trace("Executing findUserByIdAsync [{}]", userId); - validateId(userId, INCORRECT_USER_ID + userId); + validateId(userId, id -> INCORRECT_USER_ID + id); return userDao.findByIdAsync(tenantId, userId.getId()); } @@ -159,21 +159,21 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public UserCredentials findUserCredentialsByUserId(TenantId tenantId, UserId userId) { log.trace("Executing findUserCredentialsByUserId [{}]", userId); - validateId(userId, INCORRECT_USER_ID + userId); + validateId(userId, id -> INCORRECT_USER_ID + id); return userCredentialsDao.findByUserId(tenantId, userId.getId()); } @Override public UserCredentials findUserCredentialsByActivateToken(TenantId tenantId, String activateToken) { log.trace("Executing findUserCredentialsByActivateToken [{}]", activateToken); - validateString(activateToken, "Incorrect activateToken " + activateToken); + validateString(activateToken, t -> "Incorrect activateToken " + t); return userCredentialsDao.findByActivateToken(tenantId, activateToken); } @Override public UserCredentials findUserCredentialsByResetToken(TenantId tenantId, String resetToken) { log.trace("Executing findUserCredentialsByResetToken [{}]", resetToken); - validateString(resetToken, "Incorrect resetToken " + resetToken); + validateString(resetToken, t -> "Incorrect resetToken " + t); return userCredentialsDao.findByResetToken(tenantId, resetToken); } @@ -192,8 +192,8 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public UserCredentials activateUserCredentials(TenantId tenantId, String activateToken, String password) { log.trace("Executing activateUserCredentials activateToken [{}], password [{}]", activateToken, password); - validateString(activateToken, "Incorrect activateToken " + activateToken); - validateString(password, "Incorrect password " + password); + validateString(activateToken, t -> "Incorrect activateToken " + t); + validateString(password, p -> "Incorrect password " + p); UserCredentials userCredentials = userCredentialsDao.findByActivateToken(tenantId, activateToken); if (userCredentials == null) { throw new IncorrectParameterException(String.format("Unable to find user credentials by activateToken [%s]", activateToken)); @@ -259,7 +259,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic Objects.requireNonNull(user, "User is null"); UserId userId = user.getId(); log.trace("[{}] Executing deleteUser [{}]", tenantId, userId); - validateId(userId, INCORRECT_USER_ID + userId); + validateId(userId, id -> INCORRECT_USER_ID + id); userCredentialsDao.removeByUserId(tenantId, userId); userAuthSettingsDao.removeByUserId(userId); userSettingsDao.removeByUserId(tenantId, userId); @@ -276,7 +276,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public PageData findUsersByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findUsersByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return userDao.findByTenantId(tenantId.getId(), pageLink); } @@ -284,7 +284,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public PageData findTenantAdmins(TenantId tenantId, PageLink pageLink) { log.trace("Executing findTenantAdmins, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return userDao.findTenantAdmins(tenantId.getId(), pageLink); } @@ -317,7 +317,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public void deleteTenantAdmins(TenantId tenantId) { log.trace("Executing deleteTenantAdmins, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); tenantAdminsRemover.removeEntities(tenantId, tenantId); } @@ -330,8 +330,8 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public PageData findCustomerUsers(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findCustomerUsers, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, "Incorrect customerId " + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> "Incorrect customerId " + id); validatePageLink(pageLink); return userDao.findCustomerUsers(tenantId.getId(), customerId.getId(), pageLink); } @@ -339,24 +339,24 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public PageData findUsersByCustomerIds(TenantId tenantId, List customerIds, PageLink pageLink) { log.trace("Executing findTenantAndCustomerUsers, tenantId [{}], customerIds [{}], pageLink [{}]", tenantId, customerIds, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); - customerIds.forEach(customerId -> {validateId(customerId, "Incorrect customerId " + customerId);}); + customerIds.forEach(customerId -> validateId(customerId, id -> "Incorrect customerId " + id)); return userDao.findUsersByCustomerIds(tenantId.getId(), customerIds, pageLink); } @Override public void deleteCustomerUsers(TenantId tenantId, CustomerId customerId) { log.trace("Executing deleteCustomerUsers, customerId [{}]", customerId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, "Incorrect customerId " + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> "Incorrect customerId " + id); customerUsersRemover.removeEntities(tenantId, customerId); } @Override public void setUserCredentialsEnabled(TenantId tenantId, UserId userId, boolean enabled) { log.trace("Executing setUserCredentialsEnabled [{}], [{}]", userId, enabled); - validateId(userId, INCORRECT_USER_ID + userId); + validateId(userId, id -> INCORRECT_USER_ID + id); UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId()); userCredentials.setEnabled(enabled); saveUserCredentials(tenantId, userCredentials); diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserSettingsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserSettingsServiceImpl.java index adc1ce3a9a..9ed18eac38 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserSettingsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserSettingsServiceImpl.java @@ -48,14 +48,14 @@ public class UserSettingsServiceImpl extends AbstractCachedService INCORRECT_USER_ID + id); return doSaveUserSettings(tenantId, userSettings); } @Override public void updateUserSettings(TenantId tenantId, UserId userId, UserSettingsType type, JsonNode settings) { log.trace("Executing updateUserSettings for user [{}], [{}]", userId, settings); - validateId(userId, INCORRECT_USER_ID + userId); + validateId(userId, id -> INCORRECT_USER_ID + id); var key = new UserSettingsCompositeKey(userId.getId(), type.name()); UserSettings oldSettings = userSettingsDao.findById(tenantId, key); @@ -71,7 +71,7 @@ public class UserSettingsServiceImpl extends AbstractCachedService INCORRECT_USER_ID + id); var key = new UserSettingsCompositeKey(userId.getId(), type.name()); return cache.getAndPutInTransaction(key, @@ -81,7 +81,7 @@ public class UserSettingsServiceImpl extends AbstractCachedService jsonPaths) { log.trace("Executing deleteUserSettings for user [{}]", userId); - validateId(userId, INCORRECT_USER_ID + userId); + validateId(userId, id -> INCORRECT_USER_ID + id); var key = new UserSettingsCompositeKey(userId.getId(), type.name()); UserSettings userSettings = userSettingsDao.findById(tenantId, key); if (userSettings == null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java index aad37a8f66..8d24746929 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java @@ -71,21 +71,21 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { @Override public WidgetType findWidgetTypeById(TenantId tenantId, WidgetTypeId widgetTypeId) { log.trace("Executing findWidgetTypeById [{}]", widgetTypeId); - Validator.validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId); + Validator.validateId(widgetTypeId, id -> "Incorrect widgetTypeId " + id); return widgetTypeDao.findWidgetTypeById(tenantId, widgetTypeId.getId()); } @Override public WidgetTypeDetails findWidgetTypeDetailsById(TenantId tenantId, WidgetTypeId widgetTypeId) { log.trace("Executing findWidgetTypeDetailsById [{}]", widgetTypeId); - Validator.validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId); + Validator.validateId(widgetTypeId, id -> "Incorrect widgetTypeId " + id); return widgetTypeDao.findById(tenantId, widgetTypeId.getId()); } @Override public boolean widgetTypeExistsByTenantIdAndWidgetTypeId(TenantId tenantId, WidgetTypeId widgetTypeId) { log.trace("Executing widgetTypeExistsByTenantIdAndWidgetTypeId, tenantId [{}], widgetTypeId [{}]", tenantId, widgetTypeId); - Validator.validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId); + Validator.validateId(widgetTypeId, id -> "Incorrect widgetTypeId " + id); return widgetTypeDao.existsByTenantIdAndId(tenantId, widgetTypeId.getId()); } @@ -110,7 +110,7 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { @Override public void deleteWidgetType(TenantId tenantId, WidgetTypeId widgetTypeId) { log.trace("Executing deleteWidgetType [{}]", widgetTypeId); - Validator.validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId); + Validator.validateId(widgetTypeId, id -> "Incorrect widgetTypeId " + id); widgetTypeDao.removeById(tenantId, widgetTypeId.getId()); eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(widgetTypeId).build()); } @@ -126,7 +126,7 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { public PageData findAllTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { log.trace("Executing findAllTenantWidgetTypesByTenantIdAndPageLink, tenantId [{}], fullSearch [{}], deprecatedFilter [{}], widgetTypes [{}], pageLink [{}]", tenantId, fullSearch, deprecatedFilter, widgetTypes, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return widgetTypeDao.findAllTenantWidgetTypesByTenantId(tenantId.getId(), fullSearch, deprecatedFilter, widgetTypes, pageLink); } @@ -135,7 +135,7 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { public PageData findTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { log.trace("Executing findTenantWidgetTypesByTenantIdAndPageLink, tenantId [{}], fullSearch [{}], deprecatedFilter [{}], widgetTypes [{}], pageLink [{}]", tenantId, fullSearch, deprecatedFilter, widgetTypes, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return widgetTypeDao.findTenantWidgetTypesByTenantId(tenantId.getId(), fullSearch, deprecatedFilter, widgetTypes, pageLink); } @@ -143,16 +143,16 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { @Override public List findWidgetTypesByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId) { log.trace("Executing findWidgetTypesByWidgetsBundleId, tenantId [{}], widgetsBundleId [{}]", tenantId, widgetsBundleId); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(widgetsBundleId, id -> INCORRECT_WIDGETS_BUNDLE_ID + id); return widgetTypeDao.findWidgetTypesByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId()); } @Override public List findWidgetTypesDetailsByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId) { log.trace("Executing findWidgetTypesDetailsByWidgetsBundleId, tenantId [{}], widgetsBundleId [{}]", tenantId, widgetsBundleId); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(widgetsBundleId, id -> INCORRECT_WIDGETS_BUNDLE_ID + id); return widgetTypeDao.findWidgetTypesDetailsByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId()); } @@ -162,8 +162,8 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { log.trace("Executing findWidgetTypesInfosByWidgetsBundleId, tenantId [{}], widgetsBundleId [{}], fullSearch [{}], deprecatedFilter [{}], widgetTypes [{}], pageLink [{}]", tenantId, widgetsBundleId, fullSearch, deprecatedFilter, widgetTypes, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(widgetsBundleId, id -> INCORRECT_WIDGETS_BUNDLE_ID + id); Validator.validatePageLink(pageLink); return widgetTypeDao.findWidgetTypesInfosByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId(), fullSearch, deprecatedFilter, widgetTypes, pageLink); } @@ -171,27 +171,27 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { @Override public List findWidgetFqnsByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId) { log.trace("Executing findWidgetTypesInfosByWidgetsBundleId, tenantId [{}], widgetsBundleId [{}]", tenantId, widgetsBundleId); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(widgetsBundleId, id -> INCORRECT_WIDGETS_BUNDLE_ID + id); return widgetTypeDao.findWidgetFqnsByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId()); } @Override public WidgetType findWidgetTypeByTenantIdAndFqn(TenantId tenantId, String fqn) { log.trace("Executing findWidgetTypeByTenantIdAndFqn, tenantId [{}], fqn [{}]", tenantId, fqn); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateString(fqn, "Incorrect fqn " + fqn); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateString(fqn, f -> "Incorrect fqn " + f); return widgetTypeDao.findByTenantIdAndFqn(tenantId.getId(), fqn); } @Override public void updateWidgetsBundleWidgetTypes(TenantId tenantId, WidgetsBundleId widgetsBundleId, List widgetTypeIds) { log.trace("Executing updateWidgetsBundleWidgetTypes, tenantId [{}], widgetsBundleId [{}], widgetTypeIds [{}]", tenantId, widgetsBundleId, widgetTypeIds); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(widgetsBundleId, id -> INCORRECT_WIDGETS_BUNDLE_ID + id); Validator.checkNotNull(widgetTypeIds, "Incorrect widgetTypeIds " + widgetTypeIds); if (!widgetTypeIds.isEmpty()) { - validateIds(widgetTypeIds, "Incorrect widgetTypeIds " + widgetTypeIds); + validateIds(widgetTypeIds, ids -> "Incorrect widgetTypeIds " + ids); } List bundleWidgets = new ArrayList<>(); for (int index = 0; index < widgetTypeIds.size(); index++) { @@ -222,7 +222,7 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { @Override public void deleteWidgetTypesByTenantId(TenantId tenantId) { log.trace("Executing deleteWidgetTypesByTenantId, tenantId [{}]", tenantId); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); tenantWidgetTypeRemover.removeEntities(tenantId, tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java index 1631bfbdbf..3d16bb519b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java @@ -66,7 +66,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public WidgetsBundle findWidgetsBundleById(TenantId tenantId, WidgetsBundleId widgetsBundleId) { log.trace("Executing findWidgetsBundleById [{}]", widgetsBundleId); - Validator.validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId); + Validator.validateId(widgetsBundleId, id -> "Incorrect widgetsBundleId " + id); return widgetsBundleDao.findById(tenantId, widgetsBundleId.getId()); } @@ -91,7 +91,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public void deleteWidgetsBundle(TenantId tenantId, WidgetsBundleId widgetsBundleId) { log.trace("Executing deleteWidgetsBundle [{}]", widgetsBundleId); - Validator.validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId); + Validator.validateId(widgetsBundleId, id -> "Incorrect widgetsBundleId " + id); WidgetsBundle widgetsBundle = findWidgetsBundleById(tenantId, widgetsBundleId); if (widgetsBundle == null) { throw new IncorrectParameterException("Unable to delete non-existent widgets bundle."); @@ -103,8 +103,8 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public WidgetsBundle findWidgetsBundleByTenantIdAndAlias(TenantId tenantId, String alias) { log.trace("Executing findWidgetsBundleByTenantIdAndAlias, tenantId [{}], alias [{}]", tenantId, alias); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateString(alias, "Incorrect alias " + alias); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateString(alias, a -> "Incorrect alias " + a); return widgetsBundleDao.findWidgetsBundleByTenantIdAndAlias(tenantId.getId(), alias); } @@ -134,7 +134,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public PageData findTenantWidgetsBundlesByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findTenantWidgetsBundlesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return widgetsBundleDao.findTenantWidgetsBundlesByTenantId(tenantId.getId(), pageLink); } @@ -142,7 +142,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public PageData findAllTenantWidgetsBundlesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, PageLink pageLink) { log.trace("Executing findAllTenantWidgetsBundlesByTenantIdAndPageLink, tenantId [{}], fullSearch [{}], pageLink [{}]", tenantId, fullSearch, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId.getId(), fullSearch, pageLink); } @@ -150,7 +150,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public PageData findTenantWidgetsBundlesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, PageLink pageLink) { log.trace("Executing findTenantWidgetsBundlesByTenantIdAndPageLink, tenantId [{}], fullSearch [{}], pageLink [{}]", tenantId, fullSearch, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return widgetsBundleDao.findTenantWidgetsBundlesByTenantId(tenantId.getId(), fullSearch, pageLink); } @@ -158,7 +158,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public List findAllTenantWidgetsBundlesByTenantId(TenantId tenantId) { log.trace("Executing findAllTenantWidgetsBundlesByTenantId, tenantId [{}]", tenantId); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); List widgetsBundles = new ArrayList<>(); PageLink pageLink = new PageLink(DEFAULT_WIDGETS_BUNDLE_LIMIT); PageData pageData; @@ -175,7 +175,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public void deleteWidgetsBundlesByTenantId(TenantId tenantId) { log.trace("Executing deleteWidgetsBundlesByTenantId, tenantId [{}]", tenantId); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); tenantWidgetsBundleRemover.removeEntities(tenantId, tenantId); } diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 2dce04d67f..2236c7e6f8 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -559,6 +559,7 @@ CREATE TABLE IF NOT EXISTS key_dictionary CREATE TABLE IF NOT EXISTS oauth2_params ( id uuid NOT NULL CONSTRAINT oauth2_params_pkey PRIMARY KEY, enabled boolean, + edge_enabled boolean, tenant_id uuid, created_time bigint NOT NULL ); @@ -883,3 +884,12 @@ CREATE TABLE IF NOT EXISTS alarm_types ( CONSTRAINT tenant_id_type_unq_key UNIQUE (tenant_id, type), CONSTRAINT fk_entity_tenant_id FOREIGN KEY (tenant_id) REFERENCES tenant(id) ON DELETE CASCADE ); + +CREATE TABLE IF NOT EXISTS queue_stats ( + id uuid NOT NULL CONSTRAINT queue_stats_pkey PRIMARY KEY, + created_time bigint NOT NULL, + tenant_id uuid NOT NULL, + queue_name varchar(255) NOT NULL, + service_id varchar(255) NOT NULL, + CONSTRAINT queue_stats_name_unq_key UNIQUE (tenant_id, queue_name, service_id) +); \ No newline at end of file diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ServiceTest.java index ae9aa72be9..281709c9af 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ServiceTest.java @@ -47,7 +47,7 @@ import java.util.stream.Collectors; @DaoSqlTest public class OAuth2ServiceTest extends AbstractServiceTest { - private static final OAuth2Info EMPTY_PARAMS = new OAuth2Info(false, Collections.emptyList()); + private static final OAuth2Info EMPTY_PARAMS = new OAuth2Info(false, false, Collections.emptyList()); @Autowired protected OAuth2Service oAuth2Service; @@ -66,7 +66,7 @@ public class OAuth2ServiceTest extends AbstractServiceTest { @Test public void testSaveHttpAndMixedDomainsTogether() { - OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), @@ -87,7 +87,7 @@ public class OAuth2ServiceTest extends AbstractServiceTest { @Test public void testSaveHttpsAndMixedDomainsTogether() { - OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build(), @@ -153,7 +153,7 @@ public class OAuth2ServiceTest extends AbstractServiceTest { Assert.assertNotNull(foundOAuth2Info); Assert.assertEquals(oAuth2Info, foundOAuth2Info); - OAuth2Info newOAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2Info newOAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( OAuth2DomainInfo.builder().name("another-domain").scheme(SchemeType.HTTPS).build() @@ -194,7 +194,7 @@ public class OAuth2ServiceTest extends AbstractServiceTest { List thirdGroup = Lists.newArrayList( validRegistrationInfo() ); - OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), @@ -318,7 +318,7 @@ public class OAuth2ServiceTest extends AbstractServiceTest { validRegistrationInfo(), validRegistrationInfo() ); - OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), @@ -363,7 +363,7 @@ public class OAuth2ServiceTest extends AbstractServiceTest { @Test public void testGetDisabledOAuth2Clients() { - OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), @@ -402,7 +402,7 @@ public class OAuth2ServiceTest extends AbstractServiceTest { @Test public void testFindAllRegistrations() { - OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), @@ -451,7 +451,7 @@ public class OAuth2ServiceTest extends AbstractServiceTest { @Test public void testFindRegistrationById() { - OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), @@ -495,7 +495,7 @@ public class OAuth2ServiceTest extends AbstractServiceTest { @Test public void testFindAppSecret() { - OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), @@ -547,7 +547,7 @@ public class OAuth2ServiceTest extends AbstractServiceTest { @Test public void testFindClientsByPackageAndPlatform() { - OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), @@ -596,7 +596,7 @@ public class OAuth2ServiceTest extends AbstractServiceTest { } private OAuth2Info createDefaultOAuth2Info() { - return new OAuth2Info(true, Lists.newArrayList( + return new OAuth2Info(true, false, Lists.newArrayList( OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), @@ -664,4 +664,5 @@ public class OAuth2ServiceTest extends AbstractServiceTest { .appSecret(appSecret != null ? appSecret : StringUtils.randomAlphanumeric(24)) .build(); } + } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/QueueStatsServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/QueueStatsServiceTest.java new file mode 100644 index 0000000000..45a49ba5d2 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/QueueStatsServiceTest.java @@ -0,0 +1,134 @@ +/** + * 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.dao.service; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.queue.QueueStats; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.queue.QueueStatsService; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + + +@DaoSqlTest +public class QueueStatsServiceTest extends AbstractServiceTest { + + @Autowired + QueueStatsService queueStatsService; + + private TenantId tenantId; + + @Before + public void before() throws NoSuchFieldException, IllegalAccessException { + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + Tenant savedTenant = tenantService.saveTenant(tenant); + Assert.assertNotNull(savedTenant); + tenantId = savedTenant.getId(); + } + + @After + public void after() { + tenantService.deleteTenant(tenantId); + } + + @Test + public void testSaveQueueStats() { + QueueStats queueStats = new QueueStats(); + queueStats.setTenantId(tenantId); + String queueName = StringUtils.randomAlphabetic(8); + queueStats.setQueueName(queueName); + queueStats.setServiceId(StringUtils.randomAlphabetic(8)); + + QueueStats savedQueueStats = queueStatsService.save(tenantId, queueStats); + Assert.assertNotNull(savedQueueStats); + Assert.assertNotNull(savedQueueStats.getId()); + Assert.assertTrue(savedQueueStats.getCreatedTime() > 0); + Assert.assertEquals(queueStats.getTenantId(), savedQueueStats.getTenantId()); + Assert.assertEquals(savedQueueStats.getQueueName(), queueStats.getQueueName()); + + QueueStats retrievedQueueStatsById = queueStatsService.findQueueStatsById(tenantId, savedQueueStats.getId()); + Assert.assertEquals(retrievedQueueStatsById.getQueueName(), queueName); + + String secondQueueName = StringUtils.randomAlphabetic(8); + queueStats.setQueueName(secondQueueName); + QueueStats savedQueueStats2 = queueStatsService.save(tenantId, queueStats); + QueueStats retrievedQueueStatsById2 = queueStatsService.findQueueStatsById(tenantId, savedQueueStats2.getId()); + Assert.assertEquals(retrievedQueueStatsById2.getQueueName(), secondQueueName); + + List queueStatsList = queueStatsService.findByTenantId(tenantId); + Assert.assertEquals(2, queueStatsList.size()); + assertThat(queueStatsList).containsOnly(retrievedQueueStatsById, retrievedQueueStatsById2); + + queueStatsService.deleteByTenantId(tenantId); + QueueStats retrievedQueueStatsAfterDelete = queueStatsService.findQueueStatsById(tenantId, savedQueueStats.getId()); + Assert.assertNull(retrievedQueueStatsAfterDelete); + } + + @Test + public void testSaveWithNullQueueName() { + QueueStats queueStats = new QueueStats(); + queueStats.setTenantId(tenantId); + queueStats.setQueueName(null); + queueStats.setServiceId(StringUtils.randomAlphabetic(8)); + + Assertions.assertThrows(DataValidationException.class, () -> { + queueStatsService.save(tenantId, queueStats); + }); + } + + @Test + public void testSaveWithNullServiceId() { + QueueStats queueStats = new QueueStats(); + queueStats.setTenantId(tenantId); + queueStats.setQueueName(StringUtils.randomAlphabetic(8)); + queueStats.setServiceId(null); + + Assertions.assertThrows(DataValidationException.class, () -> { + queueStatsService.save(tenantId, queueStats); + }); + } + + @Test + public void testFindByTenantIdAndNameAndServiceId() { + QueueStats queueStats = new QueueStats(); + queueStats.setTenantId(tenantId); + queueStats.setQueueName(StringUtils.randomAlphabetic(8)); + queueStats.setServiceId(StringUtils.randomAlphabetic(8)); + QueueStats savedQueueStats = queueStatsService.save(tenantId, queueStats); + + QueueStats queueStats2 = new QueueStats(); + queueStats2.setTenantId(tenantId); + queueStats2.setQueueName(StringUtils.randomAlphabetic(8)); + queueStats2.setServiceId(StringUtils.randomAlphabetic(8)); + queueStatsService.save(tenantId, queueStats2); + + QueueStats retrievedQueueStatsById = queueStatsService.findByTenantIdAndNameAndServiceId(tenantId, queueStats.getQueueName(), queueStats.getServiceId()); + assertThat(retrievedQueueStatsById).isEqualTo(savedQueueStats); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java new file mode 100644 index 0000000000..4242cb69ae --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java @@ -0,0 +1,130 @@ +/** + * 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.dao.service; + +import org.junit.jupiter.api.Test; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.dao.exception.IncorrectParameterException; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class ValidatorTest { + + final DeviceId goodDeviceId = new DeviceId(UUID.fromString("18594c15-9f05-4cda-b58e-70172467c3e5")); + final UserId nullUserId = new UserId(null); + + @Test + void validateEntityIdTest() { + Validator.validateEntityId(TenantId.SYS_TENANT_ID, id -> "Incorrect entityId " + id); + Validator.validateEntityId(goodDeviceId, id -> "Incorrect entityId " + id); + + assertThatThrownBy(() -> Validator.validateEntityId(null, id -> "Incorrect entityId " + id)) + .as("EntityId is null") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect entityId null"); + + assertThatThrownBy(() -> Validator.validateEntityId(nullUserId, id -> "Incorrect entityId " + id)) + .as("EntityId with null UUID") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect entityId null"); + } + + @Test + void validateStringTest() { + Validator.validateString("Hello", s -> "Incorrect string " + s); + Validator.validateString(" ", s -> "Incorrect string " + s); + Validator.validateString("\n", s -> "Incorrect string " + s); + + assertThatThrownBy(() -> Validator.validateString(null, s -> "Incorrect string " + s)) + .as("String is null") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect string null"); + + assertThatThrownBy(() -> Validator.validateString("", s -> "Incorrect string " + s)) + .as("String is empty") + .isInstanceOf(IncorrectParameterException.class) + .hasMessage("Incorrect string "); + + assertThatThrownBy(() -> Validator.validateString("", s -> "Incorrect string [" + s + "]")) + .as("String is empty []") + .isInstanceOf(IncorrectParameterException.class) + .hasMessage("Incorrect string []"); + } + + @Test + void validateUUIDIdTest() { + Validator.validateId(UUID.randomUUID(), id -> "Incorrect Id " + id); + + assertThatThrownBy(() -> Validator.validateId((UUID) null, id -> "Incorrect Id " + id)) + .as("Id is null") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect Id null"); + } + + @Test + void validateUUIDBasedIdTest() { + Validator.validateId(TenantId.SYS_TENANT_ID, id -> "Incorrect Id " + id); + Validator.validateId(goodDeviceId, id -> "Incorrect Id " + id); + + assertThatThrownBy(() -> Validator.validateId((UUIDBased) null, id -> "Incorrect Id " + id)) + .as("Id is null") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect Id null"); + + assertThatThrownBy(() -> Validator.validateId(nullUserId, id -> "Incorrect Id " + id)) + .as("Id with null UUIDBased") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect Id null"); + + } + + @Test + void validateIdsTest() { + List list = List.of(goodDeviceId); + Validator.validateIds(list, ids -> "Incorrect Id " + ids); + + assertThatThrownBy(() -> Validator.validateIds(null, id -> "Incorrect Ids " + id)) + .as("Ids are null") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect Ids null"); + + assertThatThrownBy(() -> Validator.validateIds(Collections.emptyList(), ids -> "Incorrect Ids " + ids)) + .as("List is empty") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect Ids []"); + + List badList = new ArrayList<>(2); + badList.add(goodDeviceId); + badList.add(null); + + // Incorrect Ids [18594c15-9f05-4cda-b58e-70172467c3e5, null] + assertThatThrownBy(() -> Validator.validateIds(badList, ids -> "Incorrect Ids " + ids)) + .as("List contains null") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect Ids ") + .hasMessageContaining(goodDeviceId.getId().toString()) + .hasMessageContaining("null"); + + } +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java index f6ef6e8940..037377dc7a 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java @@ -81,7 +81,7 @@ public class JpaAlarmCommentDaoTest extends AbstractJpaDaoTest { private void saveAlarmComment(UUID id, UUID alarmId, UUID userId, AlarmCommentType type) { AlarmComment alarmComment = new AlarmComment(); alarmComment.setId(new AlarmCommentId(id)); - alarmComment.setAlarmId(TenantId.fromUUID(alarmId)); + alarmComment.setAlarmId(new AlarmId(alarmId)); alarmComment.setUserId(new UserId(userId)); alarmComment.setType(type); alarmComment.setComment(JacksonUtil.newObjectNode().put("text", RandomStringUtils.randomAlphanumeric(10))); diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDaoTest.java new file mode 100644 index 0000000000..d2889dfb3c --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDaoTest.java @@ -0,0 +1,59 @@ +/** + * 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.dao.sql.rpc; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rpc.Rpc; +import org.thingsboard.server.common.data.rpc.RpcStatus; +import org.thingsboard.server.dao.AbstractJpaDaoTest; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +public class JpaRpcDaoTest extends AbstractJpaDaoTest { + + @Autowired + JpaRpcDao rpcDao; + + @Test + public void deleteOutdated() { + Rpc rpc = new Rpc(); + rpc.setTenantId(TenantId.SYS_TENANT_ID); + rpc.setDeviceId(new DeviceId(UUID.randomUUID())); + rpc.setStatus(RpcStatus.QUEUED); + rpc.setRequest(JacksonUtil.toJsonNode("{}")); + rpcDao.saveAndFlush(rpc.getTenantId(), rpc); + + rpc.setId(null); + rpcDao.saveAndFlush(rpc.getTenantId(), rpc); + + TenantId tenantId = TenantId.fromUUID(UUID.fromString("3d193a7a-774b-4c05-84d5-f7fdcf7a37cf")); + rpc.setId(null); + rpc.setTenantId(tenantId); + rpc.setDeviceId(new DeviceId(UUID.randomUUID())); + rpcDao.saveAndFlush(rpc.getTenantId(), rpc); + + assertThat(rpcDao.deleteOutdatedRpcByTenantId(TenantId.SYS_TENANT_ID, 0L)).isEqualTo(0); + assertThat(rpcDao.deleteOutdatedRpcByTenantId(TenantId.SYS_TENANT_ID, Long.MAX_VALUE)).isEqualTo(2); + assertThat(rpcDao.deleteOutdatedRpcByTenantId(tenantId, System.currentTimeMillis() + 1)).isEqualTo(1); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java index acff0902f1..e8f3bba9db 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java @@ -21,6 +21,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.page.PageData; @@ -43,6 +44,7 @@ import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; @@ -160,6 +162,30 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest { assertEquals(new WidgetTypeInfo(widgetTypeList.get(2)), widgetTypes.getData().get(0)); } + @Test + public void testFindSystemWidgetTypesForSameName() throws InterruptedException { + List sameNameList = new ArrayList<>(); + + for (int i = 0; i < 20; i++) { + Thread.sleep(2); + var widgetType = saveWidgetType(TenantId.SYS_TENANT_ID, "widgetName"); + sameNameList.add(widgetType); + widgetTypeList.add(widgetType); + } + sameNameList.sort(Comparator.comparing(BaseWidgetType::getName).thenComparing((BaseWidgetType baseWidgetType) -> baseWidgetType.getId().getId())); + List expected = sameNameList.stream().map(WidgetTypeInfo::new).collect(Collectors.toList()); + + PageData widgetTypesFirstPage = widgetTypeDao.findSystemWidgetTypes(TenantId.SYS_TENANT_ID, true, DeprecatedFilter.ALL, Collections.singletonList("static"), + new PageLink(10, 0, null, new SortOrder("name"))); + assertEquals(10, widgetTypesFirstPage.getData().size()); + assertThat(widgetTypesFirstPage.getData()).containsExactlyElementsOf(expected.subList(0, 10)); + + PageData widgetTypesSecondPage = widgetTypeDao.findSystemWidgetTypes(TenantId.SYS_TENANT_ID, true, DeprecatedFilter.ALL, Collections.singletonList("static"), + new PageLink(10, 1, null, new SortOrder("name"))); + assertEquals(10, widgetTypesSecondPage.getData().size()); + assertThat(widgetTypesSecondPage.getData()).containsExactlyElementsOf(expected.subList(10, 20)); + } + @Test public void testTagsSearchInFindBySystemWidgetTypes() { for (var entry : SHOULD_FIND_SEARCH_TO_TAGS_MAP.entrySet()) { @@ -355,4 +381,15 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest { assertEquals(0, result.size()); widgetTypeDao.removeById(tenantId, details.getUuidId()); } + + private WidgetTypeDetails saveWidgetType(TenantId tenantId, String name) { + WidgetTypeDetails widgetType = new WidgetTypeDetails(); + widgetType.setTenantId(tenantId); + widgetType.setDescription("WIDGET_TYPE_DESCRIPTION" + StringUtils.randomAlphabetic(7)); + widgetType.setName(name); + var descriptor = JacksonUtil.newObjectNode(); + descriptor.put("type","static"); + widgetType.setDescriptor(descriptor); + return widgetTypeDao.save(TenantId.SYS_TENANT_ID, widgetType); + } } diff --git a/dao/src/test/resources/logback.xml b/dao/src/test/resources/logback.xml index 61397ec6f1..5e293b2982 100644 --- a/dao/src/test/resources/logback.xml +++ b/dao/src/test/resources/logback.xml @@ -10,6 +10,9 @@ + + + diff --git a/docker/haproxy/config/blocklist.txt b/docker/haproxy/config/blocklist.txt new file mode 100644 index 0000000000..ff9429857f --- /dev/null +++ b/docker/haproxy/config/blocklist.txt @@ -0,0 +1,3 @@ +# Blocked subnets and IPs. Use CIDR or IP by one per line +5.136.0.0/13 +217.199.254.1 diff --git a/docker/haproxy/config/haproxy.cfg b/docker/haproxy/config/haproxy.cfg index aa752502bf..757efe692f 100644 --- a/docker/haproxy/config/haproxy.cfg +++ b/docker/haproxy/config/haproxy.cfg @@ -41,6 +41,15 @@ listen stats listen mqtt-in bind *:${MQTT_PORT} mode tcp + + stick-table type ip size 60k expire 60s store conn_cur + + acl trustlist src -f /config/trustlist.txt + acl blocklist src -f /config/blocklist.txt + tcp-request connection accept if trustlist + tcp-request connection reject if blocklist or { src_conn_cur ge 50 } + tcp-request connection track-sc1 src + option clitcpka # For TCP keep-alive timeout client 3h timeout server 3h @@ -52,6 +61,15 @@ listen mqtt-in listen edges-rpc-in bind *:${EDGES_RPC_PORT} mode tcp + + stick-table type ip size 60k expire 60s store conn_cur + + acl trustlist src -f /config/trustlist.txt + acl blocklist src -f /config/blocklist.txt + tcp-request connection accept if trustlist + tcp-request connection reject if blocklist or { src_conn_cur ge 5 } + tcp-request connection track-sc1 src + option clitcpka # For TCP keep-alive timeout client 3h timeout server 3h @@ -63,18 +81,28 @@ listen edges-rpc-in frontend http-in bind *:${HTTP_PORT} alpn h2,http/1.1 + stick-table type ip size 60k expire 60s store conn_cur + + acl trustlist src -f /config/trustlist.txt + acl blocklist src -f /config/blocklist.txt + tcp-request connection accept if trustlist + tcp-request connection reject if blocklist or { src_conn_cur ge 50 } + tcp-request connection track-sc1 src + option forwardfor http-request add-header "X-Forwarded-Proto" "http" acl transport_http_acl path_beg /api/v1/ acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/ + acl tb_images_api_acl path_beg /api/images/ acl tb_api_acl path_beg /api/ /swagger /webjars /v2/ /v3/ /static/rulenode/ /oauth2/ /login/oauth2/ /static/widgets/ redirect scheme https if !letsencrypt_http_acl !transport_http_acl { env(FORCE_HTTPS_REDIRECT) -m str true } use_backend letsencrypt_http if letsencrypt_http_acl use_backend tb-http-backend if transport_http_acl + use_backend tb-images-api-backend if tb_images_api_acl use_backend tb-api-backend if tb_api_acl default_backend tb-web-backend @@ -82,14 +110,24 @@ frontend http-in frontend https_in bind *:${HTTPS_PORT} ssl crt /usr/local/etc/haproxy/default.pem crt /usr/local/etc/haproxy/certs.d ciphers ECDHE-RSA-AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM alpn h2,http/1.1 + stick-table type ip size 60k expire 60s store conn_cur + + acl trustlist src -f /config/trustlist.txt + acl blocklist src -f /config/blocklist.txt + tcp-request connection accept if trustlist + tcp-request connection reject if blocklist or { src_conn_cur ge 50 } + tcp-request connection track-sc1 src + option forwardfor http-request add-header "X-Forwarded-Proto" "https" acl transport_http_acl path_beg /api/v1/ + acl tb_images_api_acl path_beg /api/images/ acl tb_api_acl path_beg /api/ /swagger /webjars /v2/ /v3/ /static/rulenode/ /oauth2/ /login/oauth2/ /static/widgets/ use_backend tb-http-backend if transport_http_acl + use_backend tb-images-api-backend if tb_images_api_acl use_backend tb-api-backend if tb_api_acl default_backend tb-web-backend @@ -98,24 +136,76 @@ backend letsencrypt_http server letsencrypt_http_srv 127.0.0.1:8080 backend tb-web-backend + timeout queue 60s balance leastconn option tcp-check option log-health-checks - server tbWeb1 tb-web-ui1:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 - server tbWeb2 tb-web-ui2:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 + server tbWeb1 tb-web-ui1:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 + server tbWeb2 tb-web-ui2:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 http-request set-header X-Forwarded-Port %[dst_port] backend tb-http-backend + timeout queue 60s balance leastconn option tcp-check option log-health-checks - server tbHttp1 tb-http-transport1:8081 check inter 5s resolvers docker_resolver resolve-prefer ipv4 - server tbHttp2 tb-http-transport2:8081 check inter 5s resolvers docker_resolver resolve-prefer ipv4 + server tbHttp1 tb-http-transport1:8081 check inter 5s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 + server tbHttp2 tb-http-transport2:8081 check inter 5s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 + +# Dummy backends for a stick-table purpose only. +# There is only one stick-table per proxy. At the moment of writing this doc, +# it does not seem useful to have multiple tables per proxy. If this happens +# to be required, simply create a dummy backend with a stick-table in it and +# reference it +# https://www.haproxy.com/documentation/haproxy-configuration-manual/latest/#stick-table +backend st_src_rate10s + stick-table type ip size 60k expire 10s store http_req_rate(10s) + +backend st_src_rate1m + stick-table type ip size 60k expire 1m store http_req_rate(1m) backend tb-api-backend + timeout queue 60s balance source option tcp-check option log-health-checks - server tbApi1 tb-core1:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 - server tbApi2 tb-core2:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 + + http-request track-sc0 src table st_src_rate10s + http-request track-sc1 src table st_src_rate1m + + acl trustlist src -f /config/trustlist.txt + http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 } !trustlist + http-request deny deny_status 429 if { sc_http_req_rate(1) gt 300 } !trustlist + + http-request set-header X-Forwarded-Port %[dst_port] + server tbApi1 tb-core1:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 + server tbApi2 tb-core2:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 + +# Dummy backends for a stick-table purpose only. +# There is only one stick-table per proxy. At the moment of writing this doc, +# it does not seem useful to have multiple tables per proxy. If this happens +# to be required, simply create a dummy backend with a stick-table in it and +# reference it +# https://www.haproxy.com/documentation/haproxy-configuration-manual/latest/#stick-table +backend st_images_src_rate10s + stick-table type ip size 60k expire 10s store http_req_rate(10s) + +backend st_images_src_rate1m + stick-table type ip size 60k expire 1m store http_req_rate(1m) + +backend tb-images-api-backend + timeout queue 60s + balance source + option tcp-check + option log-health-checks + + http-request track-sc0 src table st_images_src_rate10s + http-request track-sc1 src table st_images_src_rate1m + + acl trustlist src -f /config/trustlist.txt + http-request deny deny_status 429 if { sc_http_req_rate(0) gt 1000 } !trustlist + http-request deny deny_status 429 if { sc_http_req_rate(1) gt 3000 } !trustlist + http-request set-header X-Forwarded-Port %[dst_port] + server tbImagesApi1 tb-core1:8080 check inter 10s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 + server tbImagesApi2 tb-core2:8080 check inter 10s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 diff --git a/docker/haproxy/config/trustlist.txt b/docker/haproxy/config/trustlist.txt new file mode 100644 index 0000000000..93716b46d5 --- /dev/null +++ b/docker/haproxy/config/trustlist.txt @@ -0,0 +1,12 @@ +# Trusted list is intended to do not apply any limitations for trustees +# +# Private subnet example +# 10.0.0.0/8 +# Docker-compose subnet +172.16.0.0/12 +# Local network subnet +192.168.0.0/16 +# Allow loopback interface +127.0.0.1 +::1 +# Allow trusted IPs or CIDRs below diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index f0869beffa..d0864c013a 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -15,7 +15,7 @@ "dependencies": { "@aws-sdk/client-sqs": "^3.121.0", "@azure/service-bus": "^7.5.1", - "@google-cloud/pubsub": "^3.0.1", + "@google-cloud/pubsub": "^4.3.3", "amqplib": "^0.10.0", "config": "^3.3.7", "express": "^4.18.1", diff --git a/msa/js-executor/yarn.lock b/msa/js-executor/yarn.lock index eb6443e53d..42a32a93c4 100644 --- a/msa/js-executor/yarn.lock +++ b/msa/js-executor/yarn.lock @@ -797,68 +797,68 @@ enabled "2.0.x" kuler "^2.0.0" -"@google-cloud/paginator@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-4.0.0.tgz#9c3e01544717aecb9a922b4269ff298f30a0f1bb" - integrity sha512-wNmCZl+2G2DmgT/VlF+AROf80SoaC/CwS8trwmjNaq26VRNK8yPbU5F/Vy+R9oDAGKWQU2k8+Op5H4kFJVXFaQ== +"@google-cloud/paginator@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-5.0.0.tgz#b8cc62f151685095d11467402cbf417c41bf14e6" + integrity sha512-87aeg6QQcEPxGCOthnpUjvw4xAZ57G7pL8FS0C4e/81fr3FjkpUpibf1s2v5XGyGhUVGF4Jfg7yEcxqn2iUw1w== dependencies: arrify "^2.0.0" extend "^3.0.2" -"@google-cloud/precise-date@^2.0.0": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@google-cloud/precise-date/-/precise-date-2.0.3.tgz#14f6f28ce35dabf3882e7aeab1c9d51bd473faed" - integrity sha512-+SDJ3ZvGkF7hzo6BGa8ZqeK3F6Z4+S+KviC9oOK+XCs3tfMyJCh/4j93XIWINgMMDIh9BgEvlw4306VxlXIlYA== +"@google-cloud/precise-date@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@google-cloud/precise-date/-/precise-date-4.0.0.tgz#e179893a3ad628b17a6fabdfcc9d468753aac11a" + integrity sha512-1TUx3KdaU3cN7nfCdNf+UVqA/PSX29Cjcox3fZZBtINlRrXVTmUkQnCKv2MbBUbCopbK4olAT1IHl76uZyCiVA== -"@google-cloud/projectify@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@google-cloud/projectify/-/projectify-2.0.1.tgz#13350ee609346435c795bbfe133a08dfeab78d65" - integrity sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ== +"@google-cloud/projectify@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@google-cloud/projectify/-/projectify-4.0.0.tgz#d600e0433daf51b88c1fa95ac7f02e38e80a07be" + integrity sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA== -"@google-cloud/promisify@^2.0.0": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-2.0.2.tgz#81d654b4cb227c65c7ad2f9a7715262febd409ed" - integrity sha512-EvuabjzzZ9E2+OaYf+7P9OAiiwbTxKYL0oGLnREQd+Su2NTQBpomkdlkBowFvyWsaV0d1sSGxrKpSNcrhPqbxg== +"@google-cloud/promisify@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-4.0.0.tgz#a906e533ebdd0f754dca2509933334ce58b8c8b1" + integrity sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g== -"@google-cloud/pubsub@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@google-cloud/pubsub/-/pubsub-3.0.1.tgz#3a6bb9649a1b4309d82d8670a9c01375e622692f" - integrity sha512-dznNbRd/Y8J0C0xvdvCPi3B1msK/dj/Nya+NQZ2doUOLT6eoa261tBwk9umOQs5L5GKcdlqQKbBjrNjDYVbzQA== - dependencies: - "@google-cloud/paginator" "^4.0.0" - "@google-cloud/precise-date" "^2.0.0" - "@google-cloud/projectify" "^2.0.0" - "@google-cloud/promisify" "^2.0.0" - "@opentelemetry/api" "^1.0.0" - "@opentelemetry/semantic-conventions" "^1.0.0" +"@google-cloud/pubsub@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@google-cloud/pubsub/-/pubsub-4.3.3.tgz#3d3f947ae8fca1694388592f22f77fdc3008ee8d" + integrity sha512-vJKh9L4dHf1XGSDKS1SB0IpqP/sUajQh4/QwhYasuq/NjzfHSxqSt+CuhrFGb5/gioTWE4gce0sn7h1SW7qESg== + dependencies: + "@google-cloud/paginator" "^5.0.0" + "@google-cloud/precise-date" "^4.0.0" + "@google-cloud/projectify" "^4.0.0" + "@google-cloud/promisify" "^4.0.0" + "@opentelemetry/api" "^1.6.0" + "@opentelemetry/semantic-conventions" "~1.21.0" "@types/duplexify" "^3.6.0" "@types/long" "^4.0.0" arrify "^2.0.0" extend "^3.0.2" - google-auth-library "^8.0.2" - google-gax "^3.0.1" + google-auth-library "^9.3.0" + google-gax "^4.3.1" + heap-js "^2.2.0" is-stream-ended "^0.1.4" lodash.snakecase "^4.1.1" p-defer "^3.0.0" -"@grpc/grpc-js@~1.6.0": - version "1.6.7" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.6.7.tgz#4c4fa998ff719fe859ac19fe977fdef097bb99aa" - integrity sha512-eBM03pu9hd3VqDQG+kHahiG1x80RGkkqqRb1Pchcwqej/KkAH95gAvKs6laqaHCycYaPK+TKuNQnOz9UXYA8qw== +"@grpc/grpc-js@~1.10.0": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.10.6.tgz#1e3eb1af911dc888fbef7452f56a7573b8284d54" + integrity sha512-xP58G7wDQ4TCmN/cMUHh00DS7SRDv/+lC+xFLrTkMIN8h55X5NhZMLYbvy7dSELP15qlI6hPhNCRWVMtZMwqLA== dependencies: - "@grpc/proto-loader" "^0.6.4" - "@types/node" ">=12.12.47" + "@grpc/proto-loader" "^0.7.10" + "@js-sdsl/ordered-map" "^4.4.2" -"@grpc/proto-loader@^0.6.12", "@grpc/proto-loader@^0.6.4": - version "0.6.13" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.6.13.tgz#008f989b72a40c60c96cd4088522f09b05ac66bc" - integrity sha512-FjxPYDRTn6Ec3V0arm1FtSpmP6V50wuph2yILpyvTKzjc76oDdoihXqM1DzOW5ubvCC8GivfCnNtfaRE8myJ7g== +"@grpc/proto-loader@^0.7.0", "@grpc/proto-loader@^0.7.10": + version "0.7.12" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.12.tgz#787b58e3e3771df30b1567c057b6ab89e3a42911" + integrity sha512-DCVwMxqYzpUCiDMl7hQ384FqP4T3DbNpXU8pt681l3UWCip1WUiD5JrkImUwCB9a7f2cq4CUTmi5r/xIMRPY1Q== dependencies: - "@types/long" "^4.0.1" lodash.camelcase "^4.3.0" - long "^4.0.0" - protobufjs "^6.11.3" - yargs "^16.2.0" + long "^5.0.0" + protobufjs "^7.2.4" + yargs "^17.7.2" "@jridgewell/resolve-uri@^3.0.3": version "3.0.8" @@ -878,6 +878,11 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@js-sdsl/ordered-map@^4.4.2": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz#9299f82874bab9e4c7f9c48d865becbfe8d6907c" + integrity sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw== + "@nodelib/fs.scandir@2.1.3": version "2.1.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" @@ -899,15 +904,20 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" -"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.0.1": +"@opentelemetry/api@^1.0.1": version "1.1.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.1.0.tgz#563539048255bbe1a5f4f586a4a10a1bb737f44a" integrity sha512-hf+3bwuBwtXsugA2ULBc95qxrOqP2pOekLz34BJhcAKawt94vfeNyUKpYc0lZQ/3sCP6LqRa7UAdHA7i5UODzQ== -"@opentelemetry/semantic-conventions@^1.0.0": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz#ba07b864a3c955f061aa30ea3ef7f4ae4449794a" - integrity sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA== +"@opentelemetry/api@^1.6.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.8.0.tgz#5aa7abb48f23f693068ed2999ae627d2f7d902ec" + integrity sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w== + +"@opentelemetry/semantic-conventions@~1.21.0": + version "1.21.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.21.0.tgz#83f7479c524ab523ac2df702ade30b9724476c72" + integrity sha512-lkC8kZYntxVKr7b8xmjCVUgE0a8xgDakPyDo9uSWavXPyYqLgYYGdEd2j8NxihRyb6UwpX3G/hFUF4/9q2V+/g== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -974,6 +984,11 @@ dependencies: defer-to-connect "^1.0.1" +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -1015,6 +1030,11 @@ "@types/connect" "*" "@types/node" "*" +"@types/caseless@*": + version "0.12.5" + resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5" + integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg== + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -1088,7 +1108,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.1.tgz#fdf6f6c6c73d3d8eee9c98a9a0485bc524b048d7" integrity sha512-HnYlg/BRF8uC1FyKRFZwRaCPTPYKa+6I8QiUZFLredaGOou481cgFS4wKRFyKvQtX8xudqkSdBczJHIYSQYKrQ== -"@types/node@>=12.12.47", "@types/node@>=13.7.0": +"@types/node@>=13.7.0": version "17.0.42" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.42.tgz#d7e8f22700efc94d125103075c074396b5f41f9b" integrity sha512-Q5BPGyGKcvQgAMbsr7qEGN/kIPN6zZecYYABeTDBizOsau+2NMdSVTar9UQw21A2+JyA2KRNDYaYrPB0Rpk2oQ== @@ -1108,6 +1128,16 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== +"@types/request@^2.48.8": + version "2.48.12" + resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.12.tgz#0f590f615a10f87da18e9790ac94c29ec4c5ef30" + integrity sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw== + dependencies: + "@types/caseless" "*" + "@types/node" "*" + "@types/tough-cookie" "*" + form-data "^2.5.0" + "@types/serve-static@*": version "1.13.10" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" @@ -1116,6 +1146,11 @@ "@types/mime" "^1" "@types/node" "*" +"@types/tough-cookie@*": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" + integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== + "@types/tunnel@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.3.tgz#f109e730b072b3136347561fc558c9358bb8c6e9" @@ -1167,6 +1202,13 @@ agent-base@6: dependencies: debug "4" +agent-base@^7.0.2: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + amqplib@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/amqplib/-/amqplib-0.10.0.tgz#766d696f8ceae097ee9eb73e6796999e5d40a1db" @@ -1462,6 +1504,15 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" @@ -1522,7 +1573,7 @@ colorspace@1.1.x: color "3.0.x" text-hex "1.0.x" -combined-stream@^1.0.8: +combined-stream@^1.0.6, combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -1609,7 +1660,7 @@ debug@2.6.9, debug@~2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.1.1: +debug@4: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -1623,6 +1674,13 @@ debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" @@ -1921,11 +1979,6 @@ fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= -fast-text-encoding@^1.0.0, fast-text-encoding@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53" - integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== - fast-xml-parser@3.19.0: version "3.19.0" resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz#cb637ec3f3999f51406dd8ff0e6fc4d83e520d01" @@ -1982,6 +2035,15 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +form-data@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" + integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + form-data@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" @@ -2081,34 +2143,23 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -gaxios@^4.0.0: - version "4.3.3" - resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-4.3.3.tgz#d44bdefe52d34b6435cc41214fdb160b64abfc22" - integrity sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA== +gaxios@^6.0.0, gaxios@^6.1.1: + version "6.4.0" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-6.4.0.tgz#08a42cb44d5123a72efaaf9f786c266e7f18be70" + integrity sha512-apAloYrY4dlBGlhauDAYSZveafb5U6+L9titing1wox6BvWM0TSXBp603zTrLpyLMGkrcFgohnUN150dFN/zOA== dependencies: - abort-controller "^3.0.0" extend "^3.0.2" - https-proxy-agent "^5.0.0" + https-proxy-agent "^7.0.1" is-stream "^2.0.0" - node-fetch "^2.6.7" - -gaxios@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-5.0.0.tgz#df11e5d0a45831dd39eb5fbbba0d6a6b09815e70" - integrity sha512-VD/yc5ln6XU8Ch1hyYY6kRMBE0Yc2np3fPyeJeYHhrPs1i8rgnsApPMWyrugkl7LLoSqpOJVBWlQIa87OAvt8Q== - dependencies: - abort-controller "^3.0.0" - extend "^3.0.2" - https-proxy-agent "^5.0.0" - is-stream "^2.0.0" - node-fetch "^2.6.7" + node-fetch "^2.6.9" + uuid "^9.0.1" -gcp-metadata@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-5.0.0.tgz#a00f999f60a4461401e7c515f8a3267cfb401ee7" - integrity sha512-gfwuX3yA3nNsHSWUL4KG90UulNiq922Ukj3wLTrcnX33BB7PwB1o0ubR8KVvXu9nJH+P5w1j2SQSNNqto+H0DA== +gcp-metadata@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-6.1.0.tgz#9b0dd2b2445258e7597f2024332d20611cbd6b8c" + integrity sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg== dependencies: - gaxios "^5.0.0" + gaxios "^6.0.0" json-bigint "^1.0.0" get-caller-file@^2.0.5: @@ -2178,46 +2229,35 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -google-auth-library@^8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-8.0.2.tgz#5fa0f2d3795c3e4019d2bb315ade4454cc9c30b5" - integrity sha512-HoG+nWFAThLovKpvcbYzxgn+nBJPTfAwtq0GxPN821nOO+21+8oP7MoEHfd1sbDulUFFGfcjJr2CnJ4YssHcyg== +google-auth-library@^9.3.0: + version "9.7.0" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-9.7.0.tgz#dd99a08e2e3f70778de8be4ed8556460e237550a" + integrity sha512-I/AvzBiUXDzLOy4iIZ2W+Zq33W4lcukQv1nl7C8WUA6SQwyQwUwu3waNmWNAvzds//FG8SZ+DnKnW/2k6mQS8A== dependencies: - arrify "^2.0.0" base64-js "^1.3.0" ecdsa-sig-formatter "^1.0.11" - fast-text-encoding "^1.0.0" - gaxios "^5.0.0" - gcp-metadata "^5.0.0" - gtoken "^5.3.2" + gaxios "^6.1.1" + gcp-metadata "^6.1.0" + gtoken "^7.0.0" jws "^4.0.0" - lru-cache "^6.0.0" -google-gax@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-3.1.0.tgz#a6fea06bfd0157969ddc0a6d04c47980978ead02" - integrity sha512-OHKVHZtaYHwxrjiI4BH+IzRWH2Q75sZg+q0/RatxLicbeeCCIug99+NKQiE39B5rLnGENtcbUjmXiqJHbSrkSg== +google-gax@^4.3.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-4.3.2.tgz#417cbee97f2e68d78f641af19c0f15234c0dbd9c" + integrity sha512-2mw7qgei2LPdtGrmd1zvxQviOcduTnsvAWYzCxhOWXK4IQKmQztHnDQwD0ApB690fBQJemFKSU7DnceAy3RLzw== dependencies: - "@grpc/grpc-js" "~1.6.0" - "@grpc/proto-loader" "^0.6.12" + "@grpc/grpc-js" "~1.10.0" + "@grpc/proto-loader" "^0.7.0" "@types/long" "^4.0.0" abort-controller "^3.0.0" duplexify "^4.0.0" - fast-text-encoding "^1.0.3" - google-auth-library "^8.0.2" - is-stream-ended "^0.1.4" + google-auth-library "^9.3.0" node-fetch "^2.6.1" object-hash "^3.0.0" - proto3-json-serializer "^1.0.0" - protobufjs "6.11.3" - retry-request "^5.0.0" - -google-p12-pem@^3.1.3: - version "3.1.4" - resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-3.1.4.tgz#123f7b40da204de4ed1fbf2fd5be12c047fc8b3b" - integrity sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg== - dependencies: - node-forge "^1.3.1" + proto3-json-serializer "^2.0.0" + protobufjs "7.2.6" + retry-request "^7.0.0" + uuid "^9.0.1" got@^9.6.0: version "9.6.0" @@ -2241,13 +2281,12 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== -gtoken@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.3.2.tgz#deb7dc876abe002178e0515e383382ea9446d58f" - integrity sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ== +gtoken@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-7.1.0.tgz#d61b4ebd10132222817f7222b1e6064bd463fc26" + integrity sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw== dependencies: - gaxios "^4.0.0" - google-p12-pem "^3.1.3" + gaxios "^6.0.0" jws "^4.0.0" has-bigints@^1.0.1, has-bigints@^1.0.2: @@ -2301,6 +2340,11 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +heap-js@^2.2.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/heap-js/-/heap-js-2.5.0.tgz#487e268b1733b187ca04eccf52f8387be92b46cb" + integrity sha512-kUGoI3p7u6B41z/dp33G6OaL7J4DRqRYwVmeIlwLClx7yaaAy7hoDExnuejTKtuDwfcatGmddHDEOjf6EyIxtQ== + http-cache-semantics@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -2317,6 +2361,15 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + https-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" @@ -2325,6 +2378,14 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +https-proxy-agent@^7.0.1: + version "7.0.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" + integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg== + dependencies: + agent-base "^7.0.2" + debug "4" + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -2728,6 +2789,11 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== +long@^5.0.0: + version "5.2.3" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" + integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== + long@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/long/-/long-5.2.0.tgz#2696dadf4b4da2ce3f6f6b89186085d94d52fd61" @@ -2868,16 +2934,16 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= +ms@2.1.2, ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + ms@2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -ms@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - multistream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/multistream/-/multistream-4.1.0.tgz#7bf00dfd119556fbc153cff3de4c6d477909f5a8" @@ -2903,18 +2969,13 @@ node-abi@^2.21.0: dependencies: semver "^5.4.1" -node-fetch@^2.6.1, node-fetch@^2.6.6, node-fetch@^2.6.7: +node-fetch@^2.6.1, node-fetch@^2.6.6, node-fetch@^2.6.7, node-fetch@^2.6.9: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" -node-forge@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" - integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== - nodemon@^2.0.16: version "2.0.16" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.16.tgz#d71b31bfdb226c25de34afea53486c8ef225fdef" @@ -3164,17 +3225,17 @@ progress@^2.0.3: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -proto3-json-serializer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/proto3-json-serializer/-/proto3-json-serializer-1.0.1.tgz#5d2b0b3c85568edb62984c94ce2e726985de44be" - integrity sha512-jtnGKL8EE1mTOl2qgMZJQXbS22dlb2K07i4b5vp8yMGMJ6mFSgBg4Ossu/g9NCuamdYXHjp3XxyWw4tJ0G/uKw== +proto3-json-serializer@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/proto3-json-serializer/-/proto3-json-serializer-2.0.1.tgz#da0b510f6d6e584b1b5c271f045c26728abe71e0" + integrity sha512-8awBvjO+FwkMd6gNoGFZyqkHZXCFd54CIYTb6De7dPaufGJ2XNW+QUNqbMr8MaAocMdb+KpsD4rxEOaTBDCffA== dependencies: - protobufjs "^6.11.3" + protobufjs "^7.2.5" -protobufjs@6.11.3, protobufjs@^6.11.3: - version "6.11.3" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" - integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== +protobufjs@7.2.6, protobufjs@^7.2.4, protobufjs@^7.2.5: + version "7.2.6" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215" + integrity sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -3186,9 +3247,8 @@ protobufjs@6.11.3, protobufjs@^6.11.3: "@protobufjs/path" "^1.1.2" "@protobufjs/pool" "^1.1.0" "@protobufjs/utf8" "^1.1.0" - "@types/long" "^4.0.1" "@types/node" ">=13.7.0" - long "^4.0.0" + long "^5.0.0" proxy-addr@~2.0.7: version "2.0.7" @@ -3363,13 +3423,14 @@ responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" -retry-request@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-5.0.0.tgz#886ff8ec0e77fffbe66a4d5e90fd8f6646b6eae4" - integrity sha512-vBZdBxUordje9253imlmGtppC5gdcwZmNz7JnU2ui+KKFPk25keR+0c020AVV20oesYxIFOI0Kh3HE88/59ieg== +retry-request@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-7.0.2.tgz#60bf48cfb424ec01b03fca6665dee91d06dd95f3" + integrity sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w== dependencies: - debug "^4.1.1" + "@types/request" "^2.48.8" extend "^3.0.2" + teeny-request "^9.0.0" reusify@^1.0.4: version "1.0.4" @@ -3547,6 +3608,13 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +stream-events@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5" + integrity sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg== + dependencies: + stubs "^3.0.0" + stream-meter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/stream-meter/-/stream-meter-1.0.4.tgz#52af95aa5ea760a2491716704dbff90f73afdd1d" @@ -3594,7 +3662,7 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string-width@^4.2.2: +string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -3680,6 +3748,11 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +stubs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" + integrity sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw== + supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -3720,6 +3793,17 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" +teeny-request@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-9.0.0.tgz#18140de2eb6595771b1b02203312dfad79a4716d" + integrity sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g== + dependencies: + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + node-fetch "^2.6.9" + stream-events "^1.0.5" + uuid "^9.0.0" + text-hex@1.0.x: version "1.0.0" resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" @@ -3973,6 +4057,11 @@ uuid@^8.3.0, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.0, uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" @@ -4138,6 +4227,11 @@ yargs-parser@^20.2.2: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" @@ -4151,6 +4245,19 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" diff --git a/pom.xml b/pom.xml index a9b7efc653..332a88b758 100755 --- a/pom.xml +++ b/pom.xml @@ -72,13 +72,13 @@ 1.3.4 4.2.1 2.2.6 - 3.10.0 + 3.11.0 2.0.0-M14 2.9.0 2.3.30 2.0.1 5.5.0 - 3.8.1 + 3.9.2 3.21.9 1.42.1 1.2.0 @@ -92,18 +92,18 @@ 4.8.0 3.0.0-M9 3.0.2 - 2.1.0 - 2.2.9 + 2.4.0TB + 2.2.20 0.7 1.18.2 1.69 2.0.1 - 42.5.0 + 42.7.3 org/thingsboard/server/gen/**/*, org/thingsboard/server/extensions/core/plugin/telemetry/gen/**/* 5.0.2 - 0.2.1 + 0.4.2 15.4 @@ -830,7 +830,6 @@ src/main/scripts/control/** src/main/scripts/windows/** src/main/resources/public/static/rulenode/** - src/main/java/org/eclipse/leshan/server/observation//** **/*.proto.js docker/haproxy/** docker/tb-node/** @@ -1775,7 +1774,7 @@ ${curator.version} - org.springdoc + org.thingsboard springdoc-openapi-starter-webmvc-ui ${springdoc-swagger.version} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index dbc652cb4e..fad13373c0 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -68,6 +68,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; @@ -315,6 +316,8 @@ public interface TbContext { QueueService getQueueService(); + QueueStatsService getQueueStatsService(); + ListeningExecutor getMailExecutor(); ListeningExecutor getSmsExecutor(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/credentials/CertPemCredentials.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/credentials/CertPemCredentials.java index 958957a3e2..40d899412d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/credentials/CertPemCredentials.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/credentials/CertPemCredentials.java @@ -87,7 +87,7 @@ public class CertPemCredentials implements ClientCredentials { private KeyManagerFactory createAndInitKeyManagerFactory() throws Exception { KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - kmf.init(loadKeyStore(), password.toCharArray()); + kmf.init(loadKeyStore(), SslUtil.getPassword(password)); return kmf; } @@ -107,7 +107,7 @@ public class CertPemCredentials implements ClientCredentials { CertPath certPath = factory.generateCertPath(certificates); List path = certPath.getCertificates(); Certificate[] x509Certificates = path.toArray(new Certificate[0]); - keyStore.setKeyEntry(PRIVATE_KEY_ALIAS, privateKey, password.toCharArray(), x509Certificates); + keyStore.setKeyEntry(PRIVATE_KEY_ALIAS, privateKey, SslUtil.getPassword(password), x509Certificates); } return keyStore; } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java index a62257245b..248dc1cfdb 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; import jakarta.annotation.Nullable; +import java.util.Objects; @Slf4j @RuleNode( @@ -57,7 +58,7 @@ public class TbCheckAlarmStatusNode implements TbNode { public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException { try { Alarm alarm = JacksonUtil.fromString(msg.getData(), Alarm.class); - + Objects.requireNonNull(alarm, "alarm is null"); ListenableFuture latest = ctx.getAlarmService().findAlarmByIdAsync(ctx.getTenantId(), alarm.getId()); Futures.addCallback(latest, new FutureCallback<>() { @@ -78,7 +79,11 @@ public class TbCheckAlarmStatusNode implements TbNode { } }, ctx.getDbCallbackExecutor()); } catch (Exception e) { - log.error("Failed to parse alarm: [{}]", msg.getData()); + if (e instanceof IllegalArgumentException || e instanceof NullPointerException) { + log.debug("[{}][{}] Failed to parse alarm: [{}] error [{}]", ctx.getTenantId(), ctx.getRuleChainName(), msg.getData(), e.getMessage()); + } else { + log.error("[{}][{}] Failed to parse alarm: [{}]", ctx.getTenantId(), ctx.getRuleChainName(), msg.getData(), e); + } throw new TbNodeException(e); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java index 0900137b73..152e193ea4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java @@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.NotificationTemplateId; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.QueueId; +import org.thingsboard.server.common.data.id.QueueStatsId; import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; @@ -141,6 +142,9 @@ public class TenantIdLoader { case NOTIFICATION_RULE: tenantEntity = ctx.getNotificationRuleService().findNotificationRuleById(ctxTenantId, new NotificationRuleId(id)); break; + case QUEUE_STATS: + tenantEntity = ctx.getQueueStatsService().findQueueStatsById(ctxTenantId, new QueueStatsId(id)); + break; default: throw new RuntimeException("Unexpected entity type: " + entityId.getEntityType()); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java index c79f962818..5b71db5dfb 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java @@ -38,8 +38,10 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.willReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -158,6 +160,19 @@ class TbCheckAlarmStatusNodeTest { assertThat(value).isInstanceOf(TbNodeException.class).hasMessage("No such alarm found."); } + @Test + void givenUnparseableAlarm_whenOnMsg_then_Failure() { + String msgData = "{\"Number\":1113718,\"id\":8.1}"; + TbMsg msg = getTbMsg(msgData); + willReturn("Default Rule Chain").given(ctx).getRuleChainName(); + + assertThatThrownBy(() -> node.onMsg(ctx, msg)) + .as("onMsg") + .isInstanceOf(TbNodeException.class) + .hasCauseInstanceOf(IllegalArgumentException.class) + .hasMessage("java.lang.IllegalArgumentException: The given string value cannot be transformed to Json object: {\"Number\":1113718,\"id\":8.1}"); + } + private TbMsg getTbMsg(String msgData) { return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, msgData); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java index 5648b02454..e06be15fe6 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java @@ -56,6 +56,7 @@ import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.common.data.queue.QueueStats; import org.thingsboard.server.common.data.rpc.Rpc; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleNode; @@ -73,6 +74,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.user.UserService; @@ -135,6 +137,8 @@ public class TenantIdLoaderTest { private NotificationRequestService notificationRequestService; @Mock private NotificationRuleService notificationRuleService; + @Mock + private QueueStatsService queueStatsService; private TenantId tenantId; private TenantProfileId tenantProfileId; @@ -352,6 +356,12 @@ public class TenantIdLoaderTest { when(ctx.getNotificationRuleService()).thenReturn(notificationRuleService); doReturn(notificationRule).when(notificationRuleService).findNotificationRuleById(eq(tenantId), any()); break; + case QUEUE_STATS: + QueueStats queueStats = new QueueStats(); + queueStats.setTenantId(tenantId); + when(ctx.getQueueStatsService()).thenReturn(queueStatsService); + doReturn(queueStats).when(queueStatsService).findQueueStatsById(eq(tenantId), any()); + break; default: throw new RuntimeException("Unexpected originator EntityType " + entityType); } diff --git a/rule-engine/rule-engine-components/src/test/resources/logback-test.xml b/rule-engine/rule-engine-components/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..0695fe1e1a --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + diff --git a/transport/lwm2m/src/main/java/org/eclipse/leshan/server/observation/ObservationServiceImpl.java b/transport/lwm2m/src/main/java/org/eclipse/leshan/server/observation/ObservationServiceImpl.java deleted file mode 100644 index 89a2f07179..0000000000 --- a/transport/lwm2m/src/main/java/org/eclipse/leshan/server/observation/ObservationServiceImpl.java +++ /dev/null @@ -1,296 +0,0 @@ -/** - * 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. - * - * Copyright (c) 2016 Sierra Wireless and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.html. - * - * Contributors: - * Sierra Wireless - initial API and implementation - * Michał Wadowski (Orange) - Add Observe-Composite feature. - */ -package org.eclipse.leshan.server.observation; - -import lombok.extern.slf4j.Slf4j; -import org.eclipse.leshan.core.node.LwM2mPath; -import org.eclipse.leshan.core.observation.CompositeObservation; -import org.eclipse.leshan.core.observation.Observation; -import org.eclipse.leshan.core.observation.SingleObservation; -import org.eclipse.leshan.core.peer.LwM2mPeer; -import org.eclipse.leshan.core.response.ObserveCompositeResponse; -import org.eclipse.leshan.core.response.ObserveResponse; -import org.eclipse.leshan.server.endpoint.LwM2mServerEndpoint; -import org.eclipse.leshan.server.endpoint.LwM2mServerEndpointsProvider; -import org.eclipse.leshan.server.profile.ClientProfile; -import org.eclipse.leshan.server.registration.Registration; -import org.eclipse.leshan.server.registration.RegistrationStore; -import org.eclipse.leshan.server.registration.RegistrationUpdate; -import org.eclipse.leshan.server.registration.UpdatedRegistration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * Implementation of the {@link ObservationService} accessing the persisted observation via the provided - * {@link RegistrationStore}. - * - * When a new observation is added or changed or canceled, the registered listeners are notified. - */ -@Slf4j -public class ObservationServiceImpl implements ObservationService, LwM2mNotificationReceiver { - - private final Logger LOG = LoggerFactory.getLogger(ObservationServiceImpl.class); - - private final RegistrationStore registrationStore; - private final LwM2mServerEndpointsProvider endpointProvider; - private final boolean updateRegistrationOnNotification; - - private final List listeners = new CopyOnWriteArrayList<>();; - - /** - * Creates an instance of {@link ObservationServiceImpl} - */ - public ObservationServiceImpl(RegistrationStore store, LwM2mServerEndpointsProvider endpointProvider) { - this(store, endpointProvider, false); - } - - /** - * Creates an instance of {@link ObservationServiceImpl} - * - * @param updateRegistrationOnNotification will activate registration update on observe notification. - * - * @since 1.1 - */ - public ObservationServiceImpl(RegistrationStore store, LwM2mServerEndpointsProvider endpointProvider, - boolean updateRegistrationOnNotification) { - this.registrationStore = store; - this.updateRegistrationOnNotification = updateRegistrationOnNotification; - this.endpointProvider = endpointProvider; - } - - @Override - public int cancelObservations(Registration registration) { - // check registration id - String registrationId = registration.getId(); - if (registrationId == null) - return 0; - - Collection observations = registrationStore.removeObservations(registrationId); - if (observations == null) - return 0; - - for (Observation observation : observations) { - cancel(observation); - } - - return observations.size(); - } - - @Override - public int cancelObservations(Registration registration, String nodePath) { - if (registration == null || registration.getId() == null || nodePath == null || nodePath.isEmpty()) - return 0; - - Set observations = getObservationsForCancel(registration.getId(), nodePath); - for (Observation observation : observations) { - cancelObservation(observation); - } - return observations.size(); - } - - @Override - public int cancelCompositeObservations(Registration registration, String[] nodePaths) { - if (registration == null || registration.getId() == null || nodePaths == null || nodePaths.length == 0) - return 0; - - Set observations = getCompositeObservationsForCancel(registration.getId(), nodePaths); - for (Observation observation : observations) { - cancelObservation(observation); - } - return observations.size(); - } - - @Override - public void cancelObservation(Observation observation) { - if (observation == null) - return; - - registrationStore.removeObservation(observation.getRegistrationId(), observation.getId()); - cancel(observation); - } - - private void cancel(Observation observation) { - List endpoints = endpointProvider.getEndpoints(); - for (LwM2mServerEndpoint lwM2mEndpoint : endpoints) { - lwM2mEndpoint.cancelObservation(observation); - } - - for (ObservationListener listener : listeners) { - listener.cancelled(observation); - } - } - - @Override - public Set getObservations(Registration registration) { - return getObservations(registration.getId()); - } - - private Set getObservations(String registrationId) { - if (registrationId == null) - return Collections.emptySet(); - - return new HashSet<>(registrationStore.getObservations(registrationId)); - } - - private Set getCompositeObservationsForCancel(String registrationId, String[] nodePaths) { - if (registrationId == null || nodePaths == null) - return Collections.emptySet(); - - // array of String to array of LWM2M path - List lwPaths = new ArrayList<>(nodePaths.length); - for (int i = 0; i < nodePaths.length; i++) { - lwPaths.add(new LwM2mPath(nodePaths[i])); - } - - // search composite-observation - Set result = new HashSet<>(); - for (Observation obs : getObservations(registrationId)) { - if (obs instanceof CompositeObservation) { - if (lwPaths.equals(((CompositeObservation) obs).getPaths())) { - result.add(obs); - } - } - } - return result; - } - - private Set getObservationsForCancel(String registrationId, String nodePath) { - if (registrationId == null || nodePath == null) - return Collections.emptySet(); - - Set result = new HashSet<>(); - LwM2mPath lwPath = new LwM2mPath(nodePath); - for (Observation obs : getObservations(registrationId)) { - if (obs instanceof SingleObservation) { - LwM2mPath lwPathObs = ((SingleObservation) obs).getPath(); - if (lwPath.equals(lwPathObs) || lwPathObs.startWith(lwPath)) { // nodePath = "3", lwPathObs = "3/0/9": cancel for tne all lwPathObs - result.add(obs); - } else if (!lwPath.equals(lwPathObs) && lwPath.startWith(lwPathObs)) { // nodePath = "3/0/9", lwPathObs = "3": error... - String errorMsg = String.format( - "Unexpected error : There is registration with id [%s] existing observation [%s] includes input observation [%s]!", - registrationId, lwPathObs, lwPath); - throw new IllegalStateException(errorMsg); - } - } - } - - return result; - } - - @Override - public void addListener(ObservationListener listener) { - listeners.add(listener); - } - - @Override - public void removeListener(ObservationListener listener) { - listeners.remove(listener); - } - - private Registration updateRegistrationOnRegistration(Observation observation, LwM2mPeer sender, - ClientProfile profile) { - if (updateRegistrationOnNotification) { - RegistrationUpdate regUpdate = new RegistrationUpdate(observation.getRegistrationId(), sender, null, null, - null, null, null, null, null, null, null, null); - UpdatedRegistration updatedRegistration = registrationStore.updateRegistration(regUpdate); - if (updatedRegistration == null || updatedRegistration.getUpdatedRegistration() == null) { - String errorMsg = String.format( - "Unexpected error: There is no registration with id %s for this observation %s", - observation.getRegistrationId(), observation); - LOG.error(errorMsg); - throw new IllegalStateException(errorMsg); - } - return updatedRegistration.getUpdatedRegistration(); - } - return profile.getRegistration(); - } - - // ********** NotificationListener interface **********// - @Override - public void onNotification(SingleObservation observation, LwM2mPeer sender, ClientProfile profile, - ObserveResponse response) { - try { - Registration updatedRegistration = updateRegistrationOnRegistration(observation, sender, profile); - for (ObservationListener listener : listeners) { - listener.onResponse(observation, updatedRegistration, response); - } - } catch (Exception e) { - for (ObservationListener listener : listeners) { - listener.onError(observation, profile.getRegistration(), e); - } - } - } - - @Override - public void onNotification(CompositeObservation observation, LwM2mPeer sender, ClientProfile profile, - ObserveCompositeResponse response) { - try { - Registration updatedRegistration = updateRegistrationOnRegistration(observation, sender, profile); - for (ObservationListener listener : listeners) { - listener.onResponse(observation, updatedRegistration, response); - } - } catch (Exception e) { - for (ObservationListener listener : listeners) { - listener.onError(observation, profile.getRegistration(), e); - } - } - } - - @Override - public void onError(Observation observation, LwM2mPeer sender, ClientProfile profile, Exception error) { - for (ObservationListener listener : listeners) { - listener.onError(observation, profile.getRegistration(), error); - } - } - - @Override - public void newObservation(Observation observation, Registration registration) { - for (ObservationListener listener : listeners) { - listener.newObservation(observation, registration); - } - } - - @Override - public void cancelled(Observation observation) { - for (ObservationListener listener : listeners) { - listener.cancelled(observation); - } - - } -} diff --git a/ui-ngx/.eslintrc.json b/ui-ngx/.eslintrc.json index cf9f0e8238..764733e4d7 100644 --- a/ui-ngx/.eslintrc.json +++ b/ui-ngx/.eslintrc.json @@ -51,7 +51,8 @@ "import/order": "off", "@typescript-eslint/member-ordering": "off", "no-underscore-dangle": "off", - "@typescript-eslint/naming-convention": "off" + "@typescript-eslint/naming-convention": "off", + "jsdoc/newline-after-description": 0 } }, { diff --git a/ui-ngx/package.json b/ui-ngx/package.json index af2092e933..a5304207f5 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -30,7 +30,7 @@ "@date-io/date-fns": "1.3.7", "@flowjs/flow.js": "^2.14.1", "@flowjs/ngx-flow": "~0.6.0", - "@geoman-io/leaflet-geoman-free": "^2.13.0", + "@geoman-io/leaflet-geoman-free": "2.14.2", "@iplab/ngx-color-picker": "^15.0.2", "@juggle/resize-observer": "^3.4.0", "@mat-datetimepicker/core": "~11.0.3", @@ -55,7 +55,7 @@ "core-js": "^3.29.1", "date-fns": "2.0.0-alpha.27", "dayjs": "1.11.4", - "echarts": "^5.5.0", + "echarts": "https://github.com/thingsboard/echarts/archive/5.5.0-TB.tar.gz", "flot": "https://github.com/thingsboard/flot.git#0.9-work", "flot.curvedlines": "https://github.com/MichaelZinsmaier/CurvedLines.git#master", "font-awesome": "^4.7.0", @@ -67,11 +67,11 @@ "jstree": "^3.3.15", "jstree-bootstrap-theme": "^1.0.1", "jszip": "^3.10.1", - "leaflet": "~1.8.0", - "leaflet-polylinedecorator": "^1.6.0", - "leaflet-providers": "^1.13.0", - "leaflet.gridlayer.googlemutant": "^0.13.5", - "leaflet.markercluster": "^1.5.3", + "leaflet": "1.8.0", + "leaflet-polylinedecorator": "1.6.0", + "leaflet-providers": "1.13.0", + "leaflet.gridlayer.googlemutant": "0.14.1", + "leaflet.markercluster": "1.5.3", "libphonenumber-js": "^1.10.4", "marked": "^4.0.17", "moment": "^2.29.4", @@ -130,11 +130,11 @@ "@types/jasminewd2": "^2.0.10", "@types/jquery": "^3.5.16", "@types/js-beautify": "^1.13.3", - "@types/leaflet": "~1.8.0", - "@types/leaflet-polylinedecorator": "^1.6.1", - "@types/leaflet-providers": "^1.2.1", - "@types/leaflet.gridlayer.googlemutant": "^0.4.6", - "@types/leaflet.markercluster": "^1.5.1", + "@types/leaflet": "1.8.0", + "@types/leaflet-polylinedecorator": "1.6.4", + "@types/leaflet-providers": "1.2.4", + "@types/leaflet.gridlayer.googlemutant": "0.4.9", + "@types/leaflet.markercluster": "1.5.4", "@types/lodash": "^4.14.192", "@types/marked": "^4.0.8", "@types/node": "~18.15.11", diff --git a/ui-ngx/patches/echarts+5.5.0.patch b/ui-ngx/patches/echarts+5.5.0.patch deleted file mode 100644 index 4f1ebed81a..0000000000 --- a/ui-ngx/patches/echarts+5.5.0.patch +++ /dev/null @@ -1,426 +0,0 @@ -diff --git a/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js b/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js -index d6c05f3..aafb0b8 100644 ---- a/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js -+++ b/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js -@@ -362,7 +362,10 @@ var DataZoomModel = /** @class */function (_super) { - return axisProxy.getDataValueWindow(); - } - } else { -- return this.getAxisProxy(axisDim, axisIndex).getDataValueWindow(); -+ var axisProxy = this.getAxisProxy(axisDim, axisIndex); -+ if (axisProxy) { -+ return axisProxy.getDataValueWindow(); -+ } - } - }; - /** -@@ -381,10 +384,10 @@ var DataZoomModel = /** @class */function (_super) { - var axisInfo = this._targetAxisInfoMap.get(axisDim); - for (var j = 0; j < axisInfo.indexList.length; j++) { - var proxy = this.getAxisProxy(axisDim, axisInfo.indexList[j]); -- if (proxy.hostedBy(this)) { -+ if (proxy && proxy.hostedBy(this)) { - return proxy; - } -- if (!firstProxy) { -+ if (proxy && !firstProxy) { - firstProxy = proxy; - } - } -diff --git a/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js b/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js -index 06469b2..cf0b2ea 100644 ---- a/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js -+++ b/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js -@@ -96,11 +96,14 @@ var getRangeHandlers = { - range[0] = (range[0] - percentPoint) * scale + percentPoint; - range[1] = (range[1] - percentPoint) * scale + percentPoint; - // Restrict range. -- var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); -- sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan); -- this.range = range; -- if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) { -- return range; -+ var proxy = this.dataZoomModel.findRepresentativeAxisProxy(); -+ if (proxy) { -+ var minMaxSpan = proxy.getMinMaxSpan(); -+ sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan); -+ this.range = range; -+ if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) { -+ return range; -+ } - } - }, - pan: makeMover(function (range, axisModel, coordSysInfo, coordSysMainType, controller, e) { -diff --git a/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js b/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js -index 98912e0..0cda6be 100644 ---- a/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js -+++ b/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js -@@ -64,7 +64,7 @@ var DEFAULT_MOVE_HANDLE_SIZE = 7; - var HORIZONTAL = 'horizontal'; - var VERTICAL = 'vertical'; - var LABEL_GAP = 5; --var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter']; -+var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter', 'custom']; - var REALTIME_ANIMATION_CONFIG = { - easing: 'cubicOut', - duration: 100, -@@ -359,30 +359,33 @@ var SliderZoomView = /** @class */function (_super) { - var result; - var ecModel = this.ecModel; - dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { -- var seriesModels = dataZoomModel.getAxisProxy(axisDim, axisIndex).getTargetSeriesModels(); -- each(seriesModels, function (seriesModel) { -- if (result) { -- return; -- } -- if (showDataShadow !== true && indexOf(SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')) < 0) { -- return; -- } -- var thisAxis = ecModel.getComponent(getAxisMainType(axisDim), axisIndex).axis; -- var otherDim = getOtherDim(axisDim); -- var otherAxisInverse; -- var coordSys = seriesModel.coordinateSystem; -- if (otherDim != null && coordSys.getOtherAxis) { -- otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse; -- } -- otherDim = seriesModel.getData().mapDimension(otherDim); -- result = { -- thisAxis: thisAxis, -- series: seriesModel, -- thisDim: axisDim, -- otherDim: otherDim, -- otherAxisInverse: otherAxisInverse -- }; -- }, this); -+ var axisProxy = dataZoomModel.getAxisProxy(axisDim, axisIndex); -+ if (axisProxy) { -+ var seriesModels = axisProxy.getTargetSeriesModels(); -+ each(seriesModels, function (seriesModel) { -+ if (result) { -+ return; -+ } -+ if (showDataShadow !== true && indexOf(SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')) < 0) { -+ return; -+ } -+ var thisAxis = ecModel.getComponent(getAxisMainType(axisDim), axisIndex).axis; -+ var otherDim = getOtherDim(axisDim); -+ var otherAxisInverse; -+ var coordSys = seriesModel.coordinateSystem; -+ if (otherDim != null && coordSys.getOtherAxis) { -+ otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse; -+ } -+ otherDim = seriesModel.getData().mapDimension(otherDim); -+ result = { -+ thisAxis: thisAxis, -+ series: seriesModel, -+ thisDim: axisDim, -+ otherDim: otherDim, -+ otherAxisInverse: otherAxisInverse -+ }; -+ }, this); -+ } - }, this); - return result; - }; -@@ -530,12 +533,17 @@ var SliderZoomView = /** @class */function (_super) { - var dataZoomModel = this.dataZoomModel; - var handleEnds = this._handleEnds; - var viewExtend = this._getViewExtent(); -- var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); -- var percentExtent = [0, 100]; -- sliderMove(delta, handleEnds, viewExtend, dataZoomModel.get('zoomLock') ? 'all' : handleIndex, minMaxSpan.minSpan != null ? linearMap(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, minMaxSpan.maxSpan != null ? linearMap(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null); -- var lastRange = this._range; -- var range = this._range = asc([linearMap(handleEnds[0], viewExtend, percentExtent, true), linearMap(handleEnds[1], viewExtend, percentExtent, true)]); -- return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1]; -+ var proxy = dataZoomModel.findRepresentativeAxisProxy(); -+ if (proxy) { -+ var minMaxSpan = proxy.getMinMaxSpan(); -+ var percentExtent = [0, 100]; -+ sliderMove(delta, handleEnds, viewExtend, dataZoomModel.get('zoomLock') ? 'all' : handleIndex, minMaxSpan.minSpan != null ? linearMap(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, minMaxSpan.maxSpan != null ? linearMap(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null); -+ var lastRange = this._range; -+ var range = this._range = asc([linearMap(handleEnds[0], viewExtend, percentExtent, true), linearMap(handleEnds[1], viewExtend, percentExtent, true)]); -+ return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1]; -+ } else { -+ return false; -+ } - }; - SliderZoomView.prototype._updateView = function (nonRealtime) { - var displaybles = this._displayables; -diff --git a/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js b/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js -index ce98fed..e154118 100644 ---- a/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js -+++ b/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js -@@ -90,7 +90,10 @@ var dataZoomProcessor = { - // init stage and not after action dispatch handler, because - // reset should be called after seriesData.restoreData. - dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { -- dataZoomModel.getAxisProxy(axisDim, axisIndex).reset(dataZoomModel); -+ var axisProxy = dataZoomModel.getAxisProxy(axisDim, axisIndex); -+ if (axisProxy) { -+ axisProxy.reset(dataZoomModel); -+ } - }); - // Caution: data zoom filtering is order sensitive when using - // percent range and no min/max/scale set on axis. -@@ -107,7 +110,10 @@ var dataZoomProcessor = { - // So we should filter x-axis after reset x-axis immediately, - // and then reset y-axis and filter y-axis. - dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { -- dataZoomModel.getAxisProxy(axisDim, axisIndex).filterData(dataZoomModel, api); -+ var axisProxy = dataZoomModel.getAxisProxy(axisDim, axisIndex); -+ if (axisProxy) { -+ axisProxy.filterData(dataZoomModel, api); -+ } - }); - }); - ecModel.eachComponent('dataZoom', function (dataZoomModel) { -diff --git a/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js b/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js -index cf8d6bc..f9b9f90 100644 ---- a/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js -+++ b/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js -@@ -109,9 +109,12 @@ var DataZoomFeature = /** @class */function (_super) { - var axisModel = axis.model; - var dataZoomModel = findDataZoom(dimName, axisModel, ecModel); - // Restrict range. -- var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy(axisModel).getMinMaxSpan(); -- if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) { -- minMax = sliderMove(0, minMax.slice(), axis.scale.getExtent(), 0, minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan); -+ var proxy = dataZoomModel.findRepresentativeAxisProxy(axisModel); -+ if (proxy) { -+ var minMaxSpan = proxy.getMinMaxSpan(); -+ if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) { -+ minMax = sliderMove(0, minMax.slice(), axis.scale.getExtent(), 0, minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan); -+ } - } - dataZoomModel && (snapshot[dataZoomModel.id] = { - dataZoomId: dataZoomModel.id, -diff --git a/node_modules/echarts/lib/component/tooltip/TooltipView.js b/node_modules/echarts/lib/component/tooltip/TooltipView.js -index b8a9b95..11e49c0 100644 ---- a/node_modules/echarts/lib/component/tooltip/TooltipView.js -+++ b/node_modules/echarts/lib/component/tooltip/TooltipView.js -@@ -360,7 +360,7 @@ var TooltipView = /** @class */function (_super) { - each(itemCoordSys.dataByAxis, function (axisItem) { - var axisModel = ecModel.getComponent(axisItem.axisDim + 'Axis', axisItem.axisIndex); - var axisValue = axisItem.value; -- if (!axisModel || axisValue == null) { -+ if (!axisModel || !axisModel.axis || axisValue == null) { - return; - } - var axisValueLabel = axisPointerViewHelper.getValueLabel(axisValue, axisModel.axis, ecModel, axisItem.seriesDataIndices, axisItem.valueLabelOpt); -diff --git a/node_modules/echarts/lib/coord/axisHelper.js b/node_modules/echarts/lib/coord/axisHelper.js -index a76c66b..e5b7ee5 100644 ---- a/node_modules/echarts/lib/coord/axisHelper.js -+++ b/node_modules/echarts/lib/coord/axisHelper.js -@@ -187,7 +187,9 @@ export function createScaleByModel(model, axisType) { - }); - default: - // case 'value'/'interval', 'log', or others. -- return new (Scale.getClass(axisType) || IntervalScale)(); -+ return new (Scale.getClass(axisType) || IntervalScale)({ -+ ticksGenerator: model.get('ticksGenerator') -+ }); - } - } - } -diff --git a/node_modules/echarts/lib/coord/cartesian/Grid.js b/node_modules/echarts/lib/coord/cartesian/Grid.js -index 5b18f02..39a57f8 100644 ---- a/node_modules/echarts/lib/coord/cartesian/Grid.js -+++ b/node_modules/echarts/lib/coord/cartesian/Grid.js -@@ -91,11 +91,11 @@ var Grid = /** @class */function () { - var scale = axis.scale; - if ( - // Only value and log axis without interval support alignTicks. -- isIntervalOrLogScale(scale) && model.get('alignTicks') && model.get('interval') == null) { -+ isIntervalOrLogScale(scale) && model.get('alignTicks') && model.get('interval') == null && model.get('ticksGenerator') == null) { - axisNeedsAlign.push(axis); - } else { - niceScaleExtent(scale, model); -- if (isIntervalOrLogScale(scale)) { -+ if (isIntervalOrLogScale(scale) && !scale.isBlank()) { - // Can only align to interval or log axis. - alignTo = axis; - } -@@ -105,10 +105,15 @@ var Grid = /** @class */function () { - // All axes has set alignTicks. Pick the first one. - // PENDING. Should we find the axis that both set interval, min, max and align to this one? - if (axisNeedsAlign.length) { -- if (!alignTo) { -- alignTo = axisNeedsAlign.pop(); -- niceScaleExtent(alignTo.scale, alignTo.model); -+ while (!alignTo && axisNeedsAlign.length) { -+ var axis = axisNeedsAlign.pop(); -+ niceScaleExtent(axis.scale, axis.model); -+ if (!axis.scale.isBlank()) { -+ alignTo = axis; -+ } - } -+ } -+ if (axisNeedsAlign.length && alignTo) { - each(axisNeedsAlign, function (axis) { - alignScaleTicks(axis.scale, axis.model, alignTo.scale); - }); -diff --git a/node_modules/echarts/lib/data/SeriesData.js b/node_modules/echarts/lib/data/SeriesData.js -index 98d5ce8..1c293a6 100644 ---- a/node_modules/echarts/lib/data/SeriesData.js -+++ b/node_modules/echarts/lib/data/SeriesData.js -@@ -900,13 +900,16 @@ var SeriesData = /** @class */function () { - var dimInfo = data._dimInfos[dim]; - // Currently, only dimensions that has ordinalMeta can create inverted indices. - var ordinalMeta = dimInfo.ordinalMeta; -+ var stack = dimInfo.stack; - var store = data._store; -- if (ordinalMeta) { -- invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array(ordinalMeta.categories.length); -- // The default value of TypedArray is 0. To avoid miss -- // mapping to 0, we should set it as INDEX_NOT_FOUND. -- for (var i = 0; i < invertedIndices.length; i++) { -- invertedIndices[i] = INDEX_NOT_FOUND; -+ if (ordinalMeta || stack) { -+ invertedIndices = invertedIndicesMap[dim] = stack ? new Array(store.count()) : new CtorInt32Array(ordinalMeta.categories.length); -+ if (ordinalMeta) { -+ // The default value of TypedArray is 0. To avoid miss -+ // mapping to 0, we should set it as INDEX_NOT_FOUND. -+ for (var i = 0; i < invertedIndices.length; i++) { -+ invertedIndices[i] = INDEX_NOT_FOUND; -+ } - } - for (var i = 0; i < store.count(); i++) { - // Only support the case that all values are distinct. -diff --git a/node_modules/echarts/lib/data/Source.js b/node_modules/echarts/lib/data/Source.js -index 7dda49b..2dd2b98 100644 ---- a/node_modules/echarts/lib/data/Source.js -+++ b/node_modules/echarts/lib/data/Source.js -@@ -252,7 +252,8 @@ function normalizeDimensionsOption(dimensionsDefine) { - var item = { - name: rawItem.name, - displayName: rawItem.displayName, -- type: rawItem.type -+ type: rawItem.type, -+ stack: rawItem.stack - }; - // User can set null in dimensions. - // We don't auto specify name, otherwise a given name may -diff --git a/node_modules/echarts/lib/data/helper/createDimensions.js b/node_modules/echarts/lib/data/helper/createDimensions.js -index 00d7eb7..dd514b1 100644 ---- a/node_modules/echarts/lib/data/helper/createDimensions.js -+++ b/node_modules/echarts/lib/data/helper/createDimensions.js -@@ -110,6 +110,9 @@ source, opt) { - } - dimDefItem.type != null && (resultItem.type = dimDefItem.type); - dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName); -+ if (dimDefItem.stack) { -+ resultItem.stack = true; -+ } - var newIdx = resultList.length; - indicesMap[dimIdx] = newIdx; - resultItem.storeDimIndex = dimIdx; -diff --git a/node_modules/echarts/lib/data/helper/dataStackHelper.js b/node_modules/echarts/lib/data/helper/dataStackHelper.js -index c25de1b..ea8300d 100644 ---- a/node_modules/echarts/lib/data/helper/dataStackHelper.js -+++ b/node_modules/echarts/lib/data/helper/dataStackHelper.js -@@ -91,7 +91,7 @@ export function enableDataStack(seriesModel, dimensionsInput, opt) { - } - if (mayStack && !dimensionInfo.isExtraCoord) { - // Find the first ordinal dimension as the stackedByDimInfo. -- if (!byIndex && !stackedByDimInfo && dimensionInfo.ordinalMeta) { -+ if (!byIndex && !stackedByDimInfo && (dimensionInfo.ordinalMeta || dimensionInfo.stack)) { - stackedByDimInfo = dimensionInfo; - } - // Find the first stackable dimension as the stackedDimInfo. -diff --git a/node_modules/echarts/lib/scale/Interval.js b/node_modules/echarts/lib/scale/Interval.js -index 1094662..363c0a5 100644 ---- a/node_modules/echarts/lib/scale/Interval.js -+++ b/node_modules/echarts/lib/scale/Interval.js -@@ -46,12 +46,17 @@ import * as numberUtil from '../util/number.js'; - import * as formatUtil from '../util/format.js'; - import Scale from './Scale.js'; - import * as helper from './helper.js'; -+import { isFunction } from 'zrender/lib/core/util.js'; - var roundNumber = numberUtil.round; - var IntervalScale = /** @class */function (_super) { - __extends(IntervalScale, _super); -- function IntervalScale() { -- var _this = _super !== null && _super.apply(this, arguments) || this; -+ function IntervalScale(setting) { -+ var _this = _super.call(this, setting) || this; - _this.type = 'interval'; -+ var ticksGenerator = _this.getSetting('ticksGenerator'); -+ if (isFunction(ticksGenerator)) { -+ _this._ticksGenerator = ticksGenerator; -+ } - // Step is calculated in adjustExtent. - _this._interval = 0; - _this._intervalPrecision = 2; -@@ -104,7 +109,17 @@ var IntervalScale = /** @class */function (_super) { - var extent = this._extent; - var niceTickExtent = this._niceExtent; - var intervalPrecision = this._intervalPrecision; -- var ticks = []; -+ var ticksGenerator = this._ticksGenerator; -+ var ticks; -+ if (ticksGenerator) { -+ try { -+ ticks = ticksGenerator(extent, interval, niceTickExtent, intervalPrecision); -+ if (ticks) { -+ return ticks; -+ } -+ } catch (_e) {} -+ } -+ ticks = []; - // If interval is 0, return []; - if (!interval) { - return ticks; -diff --git a/node_modules/echarts/types/dist/shared.d.ts b/node_modules/echarts/types/dist/shared.d.ts -index ca74097..ef41ce2 100644 ---- a/node_modules/echarts/types/dist/shared.d.ts -+++ b/node_modules/echarts/types/dist/shared.d.ts -@@ -2422,6 +2422,9 @@ interface AxisBaseOptionCommon extends ComponentOption, AnimationOptionMixin { - max: number; - }) => ScaleDataValue); - } -+ -+declare type NumericAxisTicksGenerator = (extent?: number[], interval?: number, niceTickExtent?: number[], intervalPrecision?: number) => {value: number}[]; -+ - interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon { - boundaryGap?: [number | string, number | string]; - /** -@@ -2447,6 +2450,8 @@ interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon { - * Will be ignored if interval is set. - */ - alignTicks?: boolean; -+ -+ ticksGenerator?: NumericAxisTicksGenerator; - } - interface CategoryAxisBaseOption extends AxisBaseOptionCommon { - type?: 'category'; -@@ -6412,6 +6417,7 @@ declare type DimensionDefinition = { - type?: DataStoreDimensionType; - name?: DimensionName; - displayName?: string; -+ stack?: boolean; - }; - declare type DimensionDefinitionLoose = DimensionDefinition['name'] | DimensionDefinition; - declare const SOURCE_FORMAT_ORIGINAL: "original"; -diff --git a/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts b/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts -index c5c2792..d524b70 100644 ---- a/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts -+++ b/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts -@@ -56,6 +56,9 @@ export interface AxisBaseOptionCommon extends ComponentOption, AnimationOptionMi - max: number; - }) => ScaleDataValue); - } -+ -+export declare type NumericAxisTicksGenerator = (extent?: number[], interval?: number, niceTickExtent?: number[], intervalPrecision?: number) => {value: number}[]; -+ - export interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon { - boundaryGap?: [number | string, number | string]; - /** -@@ -81,6 +84,8 @@ export interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon { - * Will be ignored if interval is set. - */ - alignTicks?: boolean; -+ -+ ticksGenerator?: NumericAxisTicksGenerator; - } - export interface CategoryAxisBaseOption extends AxisBaseOptionCommon { - type?: 'category'; diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index b29fd6c9a0..ce1c26f17d 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -716,6 +716,7 @@ export class EntityService { entityTypes.push(EntityType.CUSTOMER); entityTypes.push(EntityType.USER); entityTypes.push(EntityType.DASHBOARD); + entityTypes.push(EntityType.QUEUE_STATS); if (authState.edgesSupportEnabled) { entityTypes.push(EntityType.EDGE); } @@ -795,6 +796,10 @@ export class EntityService { case EntityType.API_USAGE_STATE: entityFieldKeys.push(entityFields.name.keyName); break; + case EntityType.QUEUE_STATS: + entityFieldKeys.push(entityFields.queueName.keyName); + entityFieldKeys.push(entityFields.serviceId.keyName); + break; } return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys; } diff --git a/ui-ngx/src/app/modules/home/components/profile/asset-profile.component.html b/ui-ngx/src/app/modules/home/components/profile/asset-profile.component.html index 5bf94d5c86..67f3bcdf13 100644 --- a/ui-ngx/src/app/modules/home/components/profile/asset-profile.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/asset-profile.component.html @@ -31,7 +31,7 @@ + + + + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.ts new file mode 100644 index 0000000000..6908b086af --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.ts @@ -0,0 +1,124 @@ +/// +/// 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. +/// + +import { Component, forwardRef, Input, OnInit, Renderer2, ViewContainerRef } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { deepClone } from '@core/utils'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { WidgetConfig } from '@shared/models/widget.models'; +import { + TimeSeriesChartThreshold, + TimeSeriesChartYAxisId +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { + TimeSeriesChartThresholdSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component'; + +@Component({ + selector: 'tb-time-series-chart-threshold-settings', + templateUrl: './time-series-chart-threshold-settings.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TimeSeriesChartThresholdSettingsComponent), + multi: true + } + ] +}) +export class TimeSeriesChartThresholdSettingsComponent implements OnInit, ControlValueAccessor { + + @Input() + disabled: boolean; + + @Input() + widgetConfig: WidgetConfig; + + @Input() + yAxisIds: TimeSeriesChartYAxisId[]; + + @Input() + @coerceBoolean() + hideYAxis = false; + + @Input() + @coerceBoolean() + boxButton = false; + + @Input() + icon = 'settings'; + + @Input() + title = 'widgets.time-series-chart.threshold.threshold-settings'; + + private modelValue: Partial; + + private propagateChange = null; + + constructor(private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef) {} + + ngOnInit(): void { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } + + writeValue(value: Partial): void { + this.modelValue = value; + } + + openThresholdSettingsPopup($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + thresholdSettings: deepClone(this.modelValue), + panelTitle: this.title, + widgetConfig: this.widgetConfig, + hideYAxis: this.hideYAxis, + yAxisIds: this.yAxisIds + }; + const thresholdSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, TimeSeriesChartThresholdSettingsPanelComponent, ['leftOnly', 'leftTopOnly', 'leftBottomOnly'], true, null, + ctx, + {}, + {}, {}, true); + thresholdSettingsPanelPopover.tbComponentRef.instance.popover = thresholdSettingsPanelPopover; + thresholdSettingsPanelPopover.tbComponentRef.instance.thresholdSettingsApplied.subscribe((thresholdSettings) => { + thresholdSettingsPanelPopover.hide(); + this.modelValue = thresholdSettings; + this.propagateChange(this.modelValue); + }); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html index e975eb4653..ba0325da93 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html @@ -21,7 +21,7 @@
widgets.time-series-chart.threshold.source
widgets.time-series-chart.threshold.key-value
-
widgets.time-series-chart.axis.y-axis
+
widgets.time-series-chart.axis.y-axis
widgets.color.color
widget-config.units-short
widget-config.decimals-short
@@ -31,6 +31,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts index 9cb5e5ced2..95e3b242c3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts @@ -38,6 +38,7 @@ import { IAliasController } from '@core/api/widget-api.models'; import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; import { DataKey, Datasource, WidgetConfig } from '@shared/models/widget.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-time-series-chart-thresholds-panel', @@ -77,6 +78,10 @@ export class TimeSeriesChartThresholdsPanelComponent implements ControlValueAcce @Input() yAxisIds: TimeSeriesChartYAxisId[]; + @Input() + @coerceBoolean() + hideYAxis = false; + thresholdsFormGroup: UntypedFormGroup; private propagateChange = (_val: any) => {}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.html index 239c6ea18c..28c79a1310 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
widgets.time-series-chart.no-aggregation-bar-width-strategy
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts index 36d8cb917a..8601c48159 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts @@ -29,6 +29,7 @@ import { timeSeriesChartNoAggregationBarWidthStrategyTranslations } from '@home/components/widget/lib/chart/time-series-chart.models'; import { merge } from 'rxjs'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-time-series-no-aggregation-bar-width-settings', @@ -53,6 +54,10 @@ export class TimeSeriesNoAggregationBarWidthSettingsComponent implements OnInit, @Input() disabled: boolean; + @Input() + @coerceBoolean() + stroked = false; + private modelValue: TimeSeriesChartNoAggregationBarWidthSettings; private propagateChange = null; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts index 5ed50a226f..86e460ff9d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts @@ -127,6 +127,12 @@ import { import { AutoDateFormatSettingsComponent } from '@home/components/widget/lib/settings/common/auto-date-format-settings.component'; +import { + TimeSeriesChartFillSettingsComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component'; +import { + TimeSeriesChartThresholdSettingsComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component'; @NgModule({ declarations: [ @@ -174,6 +180,8 @@ import { TimeSeriesChartYAxisRowComponent, TimeSeriesChartYAxisSettingsPanelComponent, TimeSeriesChartAnimationSettingsComponent, + TimeSeriesChartFillSettingsComponent, + TimeSeriesChartThresholdSettingsComponent, DataKeyInputComponent, EntityAliasInputComponent ], @@ -227,6 +235,8 @@ import { TimeSeriesChartYAxisRowComponent, TimeSeriesChartYAxisSettingsPanelComponent, TimeSeriesChartAnimationSettingsComponent, + TimeSeriesChartFillSettingsComponent, + TimeSeriesChartThresholdSettingsComponent, DataKeyInputComponent, EntityAliasInputComponent ], diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index 8522734eda..5b83f859e1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts @@ -336,9 +336,6 @@ import { import { TimeSeriesChartLineSettingsComponent } from '@home/components/widget/lib/settings/chart/time-series-chart-line-settings.component'; -import { - TimeSeriesChartFillSettingsComponent -} from '@home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component'; import { TimeSeriesChartBarSettingsComponent } from '@home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component'; @@ -467,7 +464,6 @@ import { TimeSeriesChartKeySettingsComponent, TimeSeriesChartLineSettingsComponent, TimeSeriesChartBarSettingsComponent, - TimeSeriesChartFillSettingsComponent, TimeSeriesChartWidgetSettingsComponent ], imports: [ @@ -596,7 +592,6 @@ import { TimeSeriesChartKeySettingsComponent, TimeSeriesChartLineSettingsComponent, TimeSeriesChartBarSettingsComponent, - TimeSeriesChartFillSettingsComponent, TimeSeriesChartWidgetSettingsComponent ] }) diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index b01f37a717..2e85f8b54b 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -29,13 +29,18 @@
-
- - {{ 'admin.oauth2.enable' | translate }} - -
+
+
+ + {{ 'admin.oauth2.enable' | translate }} + + + {{ 'admin.oauth2.edge-enable' | translate }} + +
+
-
+
diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss index a476f438e3..639fb877f2 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss @@ -13,7 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@import '../../../../../scss/constants'; + :host{ + .gap-xs-12 { + @media #{$mat-xs} { + gap: 12px; + } + } + + .mdc-evolution-chip-set .mdc-evolution-chip { + margin-top: 0; + margin-bottom: 0; + } + .checkbox-row { margin-top: 1em; } @@ -31,7 +44,7 @@ } .container{ - margin-bottom: 1em; + margin-bottom: 16px; .tb-highlight{ margin: 0; diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index 1d346c70f1..f6f8340550 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -204,13 +204,23 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha private buildOAuth2SettingsForm(): void { this.oauth2SettingsForm = this.fb.group({ oauth2ParamsInfos: this.fb.array([]), - enabled: [false] + enabled: [false], + edgeEnabled: [{value: false, disabled: true}] }); + + this.subscriptions.push(this.oauth2SettingsForm.get('enabled').valueChanges.subscribe(enabled => { + if (enabled) { + this.oauth2SettingsForm.get('edgeEnabled').enable(); + } else { + this.oauth2SettingsForm.get('edgeEnabled').patchValue(false); + this.oauth2SettingsForm.get('edgeEnabled').disable(); + } + })) } private initOAuth2Settings(oauth2Info: OAuth2Info): void { if (oauth2Info) { - this.oauth2SettingsForm.patchValue({enabled: oauth2Info.enabled}, {emitEvent: false}); + this.oauth2SettingsForm.patchValue({enabled: oauth2Info.enabled, edgeEnabled: oauth2Info.edgeEnabled}); oauth2Info.oauth2ParamsInfos.forEach((oauth2ParamsInfo) => { this.oauth2ParamsInfos.push(this.buildOAuth2ParamsInfoForm(oauth2ParamsInfo)); }); diff --git a/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profiles-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profiles-table-config.resolver.ts index 301949927b..d9eb000e0f 100644 --- a/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profiles-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profiles-table-config.resolver.ts @@ -31,7 +31,7 @@ import { DialogService } from '@core/services/dialog.service'; import { MatDialog } from '@angular/material/dialog'; import { ImportExportService } from '@shared/import-export/import-export.service'; import { HomeDialogsService } from '@home/dialogs/home-dialogs.service'; -import { AssetProfile, TB_SERVICE_QUEUE } from '@shared/models/asset.models'; +import { AssetProfile } from '@shared/models/asset.models'; import { AssetProfileService } from '@core/http/asset-profile.service'; import { AssetProfileComponent } from '@home/components/profile/asset-profile.component'; import { AssetProfileTabsComponent } from './asset-profile-tabs.component'; @@ -78,7 +78,7 @@ export class AssetProfilesTableConfigResolver implements Resolve !assetProfile.default && TB_SERVICE_QUEUE !== assetProfile.name, + isEnabled: (assetProfile) => !assetProfile.default, onAction: ($event, entity) => this.setDefaultAssetProfile($event, entity) } ); @@ -94,9 +94,8 @@ export class AssetProfilesTableConfigResolver implements Resolve this.assetProfileService.saveAssetProfile(assetProfile); this.config.deleteEntity = id => this.assetProfileService.deleteAssetProfile(id.id); this.config.onEntityAction = action => this.onAssetProfileAction(action); - this.config.deleteEnabled = (assetProfile) => assetProfile && !assetProfile.default && TB_SERVICE_QUEUE !== assetProfile.name; - this.config.entitySelectionEnabled = (assetProfile) => assetProfile && !assetProfile.default && TB_SERVICE_QUEUE !== assetProfile.name; - this.config.detailsReadonly = (assetProfile) => assetProfile && TB_SERVICE_QUEUE === assetProfile.name; + this.config.deleteEnabled = (assetProfile) => assetProfile && !assetProfile.default; + this.config.entitySelectionEnabled = (assetProfile) => assetProfile && !assetProfile.default; this.config.addActionDescriptors = this.configureAddActions(); } diff --git a/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.scss index f7ed839541..ef5a1c0ff4 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.scss @@ -155,7 +155,9 @@ height: 18px; background: #305680; mask-image: url(/assets/copy-code-icon.svg); + -webkit-mask-image: url(/assets/copy-code-icon.svg); mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; } } } diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.scss index 4ff80e2c21..b28b28863e 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.scss @@ -92,7 +92,9 @@ height: 18px; background: $tb-primary-color; mask-image: url(/assets/copy-code-icon.svg); + -webkit-mask-image: url(/assets/copy-code-icon.svg); mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; } } &.multiline { diff --git a/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.html b/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.html index 4a60b29ba9..ea6a4cc603 100644 --- a/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.html +++ b/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.html @@ -85,12 +85,11 @@
- diff --git a/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.ts b/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.ts index 260a7667f8..a1183acf88 100644 --- a/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.ts +++ b/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, forwardRef, Input, OnDestroy, Renderer2, ViewContainerRef } from '@angular/core'; +import { ChangeDetectorRef, Component, forwardRef, Input, OnDestroy } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -24,10 +24,13 @@ import { DndDropEvent } from 'ngx-drag-drop'; import { isUndefined } from '@core/utils'; import { coerceBoolean } from '@shared/decorators/coercion'; import { ImageLinkType } from '@shared/components/image/gallery-image-input.component'; -import { TbPopoverService } from '@shared/components/popover.service'; -import { MatButton } from '@angular/material/button'; -import { ImageGalleryComponent } from '@shared/components/image/image-gallery.component'; -import { prependTbImagePrefixToUrls, removeTbImagePrefixFromUrls } from '@shared/models/resource.models'; +import { + ImageResourceInfo, + prependTbImagePrefixToUrls, + removeTbImagePrefixFromUrls +} from '@shared/models/resource.models'; +import { MatDialog } from '@angular/material/dialog'; +import { ImageGalleryDialogComponent } from '@shared/components/image/image-gallery-dialog.component'; @Component({ selector: 'tb-multiple-gallery-image-input', @@ -66,10 +69,8 @@ export class MultipleGalleryImageInputComponent extends PageComponent implements private propagateChange = null; constructor(protected store: Store, - private cd: ChangeDetectorRef, - private renderer: Renderer2, - private viewContainerRef: ViewContainerRef, - private popoverService: TbPopoverService) { + private dialog: MatDialog, + private cd: ChangeDetectorRef) { super(store); } @@ -130,31 +131,21 @@ export class MultipleGalleryImageInputComponent extends PageComponent implements this.updateModel(); } - toggleGallery($event: Event, browseGalleryButton: MatButton) { + toggleGallery($event: Event) { if ($event) { $event.stopPropagation(); } - const trigger = browseGalleryButton._elementRef.nativeElement; - if (this.popoverService.hasPopover(trigger)) { - this.popoverService.hidePopover(trigger); - } else { - const ctx: any = { - pageMode: false, - popoverMode: true, - mode: 'grid', - selectionMode: true - }; - const imageGalleryPopover = this.popoverService.displayPopover(trigger, this.renderer, - this.viewContainerRef, ImageGalleryComponent, 'top', true, null, - ctx, - {}, - {}, {}, true); - imageGalleryPopover.tbComponentRef.instance.imageSelected.subscribe((image) => { - imageGalleryPopover.hide(); + this.dialog.open(ImageGalleryDialogComponent, { + autoFocus: false, + disableClose: false, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'] + }).afterClosed().subscribe((image) => { + if (image) { this.imageUrls.push(image.link); this.updateModel(); - }); - } + } + }); } imageDragStart(index: number) { diff --git a/ui-ngx/src/app/shared/components/time/timeinterval.component.ts b/ui-ngx/src/app/shared/components/time/timeinterval.component.ts index 8775b0a6ed..8ed6fb2034 100644 --- a/ui-ngx/src/app/shared/components/time/timeinterval.component.ts +++ b/ui-ngx/src/app/shared/components/time/timeinterval.component.ts @@ -21,6 +21,7 @@ import { coerceNumberProperty } from '@angular/cdk/coercion'; import { SubscriptSizing } from '@angular/material/form-field'; import { coerceBoolean } from '@shared/decorators/coercion'; import { Interval, IntervalMath, TimeInterval } from '@shared/models/time/time.models'; +import { isDefined } from '@core/utils'; @Component({ selector: 'tb-timeinterval', @@ -90,14 +91,17 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { secs = 0; interval: Interval = 0; - modelValue: Interval; + intervals: Array; advanced = false; - rendered = false; - intervals: Array; + private modelValue: Interval; + private rendered = false; + private propagateChangeValue: any; - private propagateChange = (_: any) => {}; + private propagateChange = (value: any) => { + this.propagateChangeValue = value; + }; constructor(private timeService: TimeService) { } @@ -108,6 +112,9 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { registerOnChange(fn: any): void { this.propagateChange = fn; + if (isDefined(this.propagateChangeValue)) { + this.propagateChange(this.propagateChangeValue); + } } registerOnTouched(fn: any): void { @@ -132,7 +139,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { } } - setInterval(interval: Interval) { + private setInterval(interval: Interval) { if (!this.advanced) { this.interval = interval; } @@ -143,7 +150,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { this.secs = intervalSeconds % 60; } - boundInterval(updateToPreferred = false) { + private boundInterval(updateToPreferred = false) { const min = this.timeService.boundMinInterval(this.minValue); const max = this.timeService.boundMaxInterval(this.maxValue); this.intervals = this.timeService.getIntervals(this.minValue, this.maxValue, this.useCalendarIntervals); @@ -165,7 +172,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { } } - updateView(updateToPreferred = false) { + private updateView(updateToPreferred = false) { if (!this.rendered) { return; } @@ -187,7 +194,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { this.boundInterval(updateToPreferred); } - calculateIntervalMs(): number { + private calculateIntervalMs(): number { return (this.days * 86400 + this.hours * 3600 + this.mins * 60 + @@ -232,7 +239,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { } } - onSecsChange() { + private onSecsChange() { if (typeof this.secs === 'undefined') { return; } @@ -252,7 +259,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { this.updateView(); } - onMinsChange() { + private onMinsChange() { if (typeof this.mins === 'undefined') { return; } @@ -272,7 +279,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { this.updateView(); } - onHoursChange() { + private onHoursChange() { if (typeof this.hours === 'undefined') { return; } @@ -292,7 +299,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { this.updateView(); } - onDaysChange() { + private onDaysChange() { if (typeof this.days === 'undefined') { return; } diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts index 0c0dd4294c..857d2c7cb7 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts @@ -20,7 +20,6 @@ import { AggregationType, DAY, HistoryWindowType, - QuickTimeInterval, quickTimeIntervalPeriod, RealtimeWindowType, Timewindow, diff --git a/ui-ngx/src/app/shared/models/asset.models.ts b/ui-ngx/src/app/shared/models/asset.models.ts index bcdde08009..6ba8bc8abb 100644 --- a/ui-ngx/src/app/shared/models/asset.models.ts +++ b/ui-ngx/src/app/shared/models/asset.models.ts @@ -24,8 +24,6 @@ import { RuleChainId } from '@shared/models/id/rule-chain-id'; import { DashboardId } from '@shared/models/id/dashboard-id'; import { EntityInfoData, HasTenantId } from '@shared/models/entity.models'; -export const TB_SERVICE_QUEUE = 'TbServiceQueue'; - export interface AssetProfile extends BaseData, HasTenantId, ExportableEntity { tenantId?: TenantId; name: string; diff --git a/ui-ngx/src/app/shared/models/entity-type.models.ts b/ui-ngx/src/app/shared/models/entity-type.models.ts index 194327f671..885ea90929 100644 --- a/ui-ngx/src/app/shared/models/entity-type.models.ts +++ b/ui-ngx/src/app/shared/models/entity-type.models.ts @@ -39,6 +39,7 @@ export enum EntityType { OTA_PACKAGE = 'OTA_PACKAGE', RPC = 'RPC', QUEUE = 'QUEUE', + QUEUE_STATS = 'QUEUE_STATS', NOTIFICATION = 'NOTIFICATION', NOTIFICATION_REQUEST = 'NOTIFICATION_REQUEST', NOTIFICATION_RULE = 'NOTIFICATION_RULE', @@ -362,6 +363,13 @@ export const entityTypeTranslations = new Map{{entityId}}
due to the same credentials are already present in the database for another device.
Please consider disabling the load credentials setting in the restore form.", "missing-referenced-entity": "Failed to load the {{sourceEntityTypeName}} with external id {{sourceEntityId}}
because it references missing {{targetEntityTypeName}} with id {{targetEntityId}}.", "runtime-failed": "Failed: {{message}}", - "auto-commit-settings-read-only-hint": "Auto-commit feature doesn't work with enabled read-only option in Repository settings." + "auto-commit-settings-read-only-hint": "Auto-commit feature doesn't work with enabled read-only option in Repository settings.", + "rollback-on-error": "Rollback on error", + "rollback-on-error-hint": "If you have a large amount of entities to restore, consider disabling this option to increase performance.\n Note, if an error occurs over the course of version loading, already persisted entities (with relations, attributes, etc.) will stay as is" }, "widget": { "widget-library": "Widgets library", @@ -5425,7 +5432,7 @@ "bar-appearance": "Bar appearance", "label-on-bar": "Label on bar", "value-on-bar": "Value on bar", - "bar-chart-card-style": "Bar chart card style" + "bar-chart-style": "Bar chart style" }, "battery-level": { "layout": "Layout", @@ -6175,10 +6182,14 @@ "range-chart": { "chart": "Chart", "data-zoom": "Data zoom", + "range-chart-appearance": "Range chart appearance", "range-colors": "Range colors", "out-of-range-color": "Out of range color", + "show-range-thresholds": "Show range thresholds", + "range-thresholds-settings": "Range thresholds settings", "fill-area": "Fill area", - "range-chart-card-style": "Range chart card style" + "fill-area-opacity": "Fill area opacity", + "range-chart-style": "Range chart style" }, "rpc": { "value-settings": "Value settings", @@ -6772,7 +6783,8 @@ "label-position-inside-middle-bottom": "Inside middle bottom", "label-position-inside-end": "Inside end", "label-position-inside-end-top": "Inside end top", - "label-position-inside-end-bottom": "Inside end bottom" + "label-position-inside-end-bottom": "Inside end bottom", + "label-background": "Label background" }, "axis": { "axes": "Axes", @@ -6853,6 +6865,7 @@ "show-points": "Show points", "point-label": "Point label", "point-label-hint": "Display label with value over the series point.", + "point-label-background": "Point label background", "point-shape": "Point shape", "point-size": "Point size" }, @@ -6861,7 +6874,8 @@ "border-width": "Border width", "border-radius": "Border radius", "label": "Label", - "label-hint": "Display label with value over the bar." + "label-hint": "Display label with value over the bar.", + "label-background": "Label background" } } }, diff --git a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json index 014666017c..58d4a0168f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -5172,7 +5172,7 @@ "bar-appearance":"Wygląd słupka", "label-on-bar":"Etykieta na słupku", "value-on-bar":"Wartość na słupku", - "bar-chart-card-style":"Styl karty wykresu słupkowego" + "bar-chart-style":"Styl wykresu słupkowego" }, "battery-level":{ "layout":"Układ", @@ -5924,7 +5924,7 @@ "range-colors":"Kolory zakresu", "out-of-range-color":"Kolor poza zakresem", "fill-area":"Wypełnij obszar", - "range-chart-card-style":"Styl karty wykresu zakresu" + "range-chart-style":"Styl wykresu zakresu" }, "rpc":{ "value-settings":"Ustawienia wartości", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index fdc0274cdf..886151fdf2 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -5491,7 +5491,7 @@ "range-colors": "范围颜色", "out-of-range-color": "超出范围颜色", "fill-area": "填充区域", - "range-chart-card-style": "范围图表卡片样式" + "range-chart-style": "范围图样式" }, "rpc": { "value-settings": "值设置", diff --git a/ui-ngx/src/form.scss b/ui-ngx/src/form.scss index e2390c795d..f4d3cdb398 100644 --- a/ui-ngx/src/form.scss +++ b/ui-ngx/src/form.scss @@ -588,9 +588,13 @@ bottom: 0; background: $tb-primary-color; mask-image: url(/assets/home/no_data_folder_bg.svg); + -webkit-mask-image: url(/assets/home/no_data_folder_bg.svg); mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; mask-size: contain; + -webkit-mask-size: contain; mask-position: center; + -webkit-mask-position: center; } } diff --git a/ui-ngx/yarn.lock b/ui-ngx/yarn.lock index ca5c179ad5..ddcd48243b 100644 --- a/ui-ngx/yarn.lock +++ b/ui-ngx/yarn.lock @@ -1582,10 +1582,10 @@ dependencies: tslib "^2.2.0" -"@geoman-io/leaflet-geoman-free@^2.13.0": - version "2.16.0" - resolved "https://registry.yarnpkg.com/@geoman-io/leaflet-geoman-free/-/leaflet-geoman-free-2.16.0.tgz#c8a92fcc2cdd5770ebf43f8ef9f03cc8ef842359" - integrity sha512-BnKAAoTXraWVFfhX/0gT/iBgAz1BPfpbdQ9dJamEFI4lIku9UNXXluu/E0k7YkZETq0tENX2GPnKLB4p+VgrSw== +"@geoman-io/leaflet-geoman-free@2.14.2": + version "2.14.2" + resolved "https://registry.yarnpkg.com/@geoman-io/leaflet-geoman-free/-/leaflet-geoman-free-2.14.2.tgz#c84c2115c263f34d11dc0b43859551639fe3d56b" + integrity sha512-6lIyG8RvSVdFjVjiQgBPyNASjymSyqzsiUeBW0pA+q41lB5fAg4SDC6SfJvWdEyDHa81Jb5FWjUkCc9O+u0gbg== dependencies: "@turf/boolean-contains" "^6.5.0" "@turf/kinks" "^6.5.0" @@ -3079,28 +3079,28 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/leaflet-polylinedecorator@^1.6.1": +"@types/leaflet-polylinedecorator@1.6.4": version "1.6.4" resolved "https://registry.yarnpkg.com/@types/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.4.tgz#2270b84585bd28bbf1fef237be8480196c44ac06" integrity sha512-meH/jC58Drt/9aqLhuzMCUQjHgMTud+UuQBK5gT6E8M6OfM/rftz/VgvdxOCCAQC9isrp4pqahDgEswesb5X3A== dependencies: "@types/leaflet" "*" -"@types/leaflet-providers@^1.2.1": +"@types/leaflet-providers@1.2.4": version "1.2.4" resolved "https://registry.yarnpkg.com/@types/leaflet-providers/-/leaflet-providers-1.2.4.tgz#284e8a197d4c2dbfeda436842e03b2adb502be16" integrity sha512-4wYEpreixp+G5t510s202eQ5eubOmxHevIfCNrzUZbzp50XQsC9lSetX7MnPl3ANRJnUBbCWOzZ9EQFiH0Jm+g== dependencies: "@types/leaflet" "*" -"@types/leaflet.gridlayer.googlemutant@^0.4.6": +"@types/leaflet.gridlayer.googlemutant@0.4.9": version "0.4.9" resolved "https://registry.yarnpkg.com/@types/leaflet.gridlayer.googlemutant/-/leaflet.gridlayer.googlemutant-0.4.9.tgz#9090ddf4578ce631d22b8aafc5ed12525b4fbf90" integrity sha512-u/5Avs1KKkeABRDixGbGp2ldFs67+fYIATpkvvFgN+LQPNfq7dFd5adkksshiQBfYJ4SaQg1VJgN2dNirIC0aQ== dependencies: "@types/leaflet" "*" -"@types/leaflet.markercluster@^1.5.1": +"@types/leaflet.markercluster@1.5.4": version "1.5.4" resolved "https://registry.yarnpkg.com/@types/leaflet.markercluster/-/leaflet.markercluster-1.5.4.tgz#2ab43417cf3f6a42d0f1baf4e1c8f659cf1dc3a1" integrity sha512-tfMP8J62+wfsVLDLGh5Zh1JZxijCaBmVsMAX78MkLPwvPitmZZtSin5aWOVRhZrCS+pEOZwNzexbfWXlY+7yjg== @@ -3114,7 +3114,7 @@ dependencies: "@types/geojson" "*" -"@types/leaflet@~1.8.0": +"@types/leaflet@1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@types/leaflet/-/leaflet-1.8.0.tgz#dc92d3e868fb6d5067b4b59fa08cd4441f84fabe" integrity sha512-+sXFmiJTFdhaXXIGFlV5re9AdqtAODoXbGAvxx02e5SHXL3ir7ClP5J7pahO8VmzKY3dth4RUS1nf2BTT+DW1A== @@ -5437,10 +5437,9 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -echarts@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/echarts/-/echarts-5.5.0.tgz#c13945a7f3acdd67c134d8a9ac67e917830113ac" - integrity sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw== +"echarts@https://github.com/thingsboard/echarts/archive/5.5.0-TB.tar.gz": + version "5.5.0-TB" + resolved "https://github.com/thingsboard/echarts/archive/5.5.0-TB.tar.gz#d1017728576b3fd65532eb17e503d1349f7feaed" dependencies: tslib "2.3.0" zrender "5.5.0" @@ -7750,14 +7749,14 @@ lcov-parse@1.0.0: resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-1.0.0.tgz#eb0d46b54111ebc561acb4c408ef9363bdc8f7e0" integrity sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ== -leaflet-polylinedecorator@^1.6.0: +leaflet-polylinedecorator@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.0.tgz#9ef79fd1b5302d67b72efe959a8ecd2553f27266" integrity sha512-kn3krmZRetgvN0wjhgYL8kvyLS0tUogAl0vtHuXQnwlYNjbl7aLQpkoFUo8UB8gVZoB0dhI4Tb55VdTJAcYzzQ== dependencies: leaflet-rotatedmarker "^0.2.0" -leaflet-providers@^1.13.0: +leaflet-providers@1.13.0: version "1.13.0" resolved "https://registry.yarnpkg.com/leaflet-providers/-/leaflet-providers-1.13.0.tgz#10c843a23d5823a65096d40ad53f27029e13434b" integrity sha512-f/sN5wdgBbVA2jcCYzScIfYNxKdn2wBJP9bu+5cRX9Xj6g8Bt1G9Sr8WgJAt/ckIFIc3LVVxCBNFpSCfTuUElg== @@ -7767,17 +7766,17 @@ leaflet-rotatedmarker@^0.2.0: resolved "https://registry.yarnpkg.com/leaflet-rotatedmarker/-/leaflet-rotatedmarker-0.2.0.tgz#4467f49f98d1bfd56959bd9c6705203dd2601277" integrity sha512-yc97gxLXwbZa+Gk9VCcqI0CkvIBC9oNTTjFsHqq4EQvANrvaboib4UdeQLyTnEqDpaXHCqzwwVIDHtvz2mUiDg== -leaflet.gridlayer.googlemutant@^0.13.5: - version "0.13.5" - resolved "https://registry.yarnpkg.com/leaflet.gridlayer.googlemutant/-/leaflet.gridlayer.googlemutant-0.13.5.tgz#7182c26f479e726bff8b85a7ebf9d3f44dd3ea57" - integrity sha512-DHUEXpo1t0WZ9tpdLUHHkrTK7LldCXr/gqkV5E/4hHidDJB2srceLLCZj4PV/pl0jPdTkVBT+Lr8nL9EZDB5hw== +leaflet.gridlayer.googlemutant@0.14.1: + version "0.14.1" + resolved "https://registry.yarnpkg.com/leaflet.gridlayer.googlemutant/-/leaflet.gridlayer.googlemutant-0.14.1.tgz#c282209aa1a39eb2f87d8aaa4e9894181c9e20a0" + integrity sha512-/OYxEjmgxO1U1KOhTzg+m8c0b95J0943LU8DXQmdJu/x2f+1Ur78rvEPO2QCS0cmwZ3m6FvE5I3zXnBzJNWRCA== -leaflet.markercluster@^1.5.3: +leaflet.markercluster@1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/leaflet.markercluster/-/leaflet.markercluster-1.5.3.tgz#9cdb52a4eab92671832e1ef9899669e80efc4056" integrity sha512-vPTw/Bndq7eQHjLBVlWpnGeLa3t+3zGiuM7fJwCkiMFq+nmRuG3RI3f7f4N4TDX7T4NpbAXpR2+NTRSEGfCSeA== -leaflet@~1.8.0: +leaflet@1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.8.0.tgz#4615db4a22a304e8e692cae9270b983b38a2055e" integrity sha512-gwhMjFCQiYs3x/Sf+d49f10ERXaEFCPr+nVTryhAW8DWbMGqJqt9G4XuIaHmFW08zYvhgdzqXGr8AlW8v8dQkA==