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 extends ThingsboardErrorResponse> 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