Browse Source

Merge pull request #14929 from dashevchenko/openApiClientGeneration

Refactor APIs to follow OpenAPI standard
pull/15236/head
Viacheslav Klimov 7 months ago
committed by GitHub
parent
commit
22788378ee
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 289
      application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
  2. 4
      application/src/main/java/org/thingsboard/server/controller/AdminController.java
  3. 4
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  4. 42
      application/src/main/java/org/thingsboard/server/controller/AssetController.java
  5. 24
      application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java
  6. 15
      application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
  7. 4
      application/src/main/java/org/thingsboard/server/controller/AuthController.java
  8. 44
      application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java
  9. 4
      application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
  10. 24
      application/src/main/java/org/thingsboard/server/controller/CustomerController.java
  11. 37
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  12. 28
      application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java
  13. 43
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  14. 30
      application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
  15. 6
      application/src/main/java/org/thingsboard/server/controller/DomainController.java
  16. 42
      application/src/main/java/org/thingsboard/server/controller/EdgeController.java
  17. 9
      application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java
  18. 163
      application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
  19. 46
      application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
  20. 19
      application/src/main/java/org/thingsboard/server/controller/EventController.java
  21. 4
      application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java
  22. 4
      application/src/main/java/org/thingsboard/server/controller/MailConfigTemplateController.java
  23. 6
      application/src/main/java/org/thingsboard/server/controller/MobileAppBundleController.java
  24. 4
      application/src/main/java/org/thingsboard/server/controller/MobileAppController.java
  25. 41
      application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java
  26. 4
      application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java
  27. 15
      application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java
  28. 30
      application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java
  29. 2
      application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java
  30. 8
      application/src/main/java/org/thingsboard/server/controller/QueueController.java
  31. 17
      application/src/main/java/org/thingsboard/server/controller/QueueStatsController.java
  32. 8
      application/src/main/java/org/thingsboard/server/controller/RpcV1Controller.java
  33. 8
      application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java
  34. 32
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  35. 26
      application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java
  36. 19
      application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
  37. 92
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  38. 18
      application/src/main/java/org/thingsboard/server/controller/TenantController.java
  39. 15
      application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java
  40. 9
      application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java
  41. 4
      application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthController.java
  42. 71
      application/src/main/java/org/thingsboard/server/controller/UserController.java
  43. 30
      application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
  44. 31
      application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
  45. 1
      application/src/test/java/org/thingsboard/server/edge/NotificationEdgeTest.java
  46. 1
      application/src/test/java/org/thingsboard/server/service/sync/vc/VersionControlTest.java
  47. 3
      common/data/src/main/java/org/thingsboard/server/common/data/BaseDataWithAdditionalInfo.java
  48. 2
      common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java
  49. 10
      common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java
  50. 1
      common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java
  51. 12
      common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java
  52. 4
      common/data/src/main/java/org/thingsboard/server/common/data/User.java
  53. 12
      common/data/src/main/java/org/thingsboard/server/common/data/ai/dto/TbChatResponse.java
  54. 10
      common/data/src/main/java/org/thingsboard/server/common/data/ai/dto/TbContent.java
  55. 10
      common/data/src/main/java/org/thingsboard/server/common/data/ai/dto/TbUserMessage.java
  56. 23
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/AiModelConfig.java
  57. 3
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AmazonBedrockChatModelConfig.java
  58. 3
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AnthropicChatModelConfig.java
  59. 3
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AzureOpenAiChatModelConfig.java
  60. 3
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GitHubModelsChatModelConfig.java
  61. 3
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GoogleAiGeminiChatModelConfig.java
  62. 3
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GoogleVertexAiGeminiChatModelConfig.java
  63. 3
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/MistralAiChatModelConfig.java
  64. 3
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/OllamaChatModelConfig.java
  65. 3
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/OpenAiChatModelConfig.java
  66. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AmazonBedrockProviderConfig.java
  67. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AnthropicProviderConfig.java
  68. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AzureOpenAiProviderConfig.java
  69. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GitHubModelsProviderConfig.java
  70. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GoogleAiGeminiProviderConfig.java
  71. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GoogleVertexAiGeminiProviderConfig.java
  72. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/MistralAiProviderConfig.java
  73. 12
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/OllamaProviderConfig.java
  74. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/OpenAiProviderConfig.java
  75. 4
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java
  76. 2
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/AlarmRule.java
  77. 11
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/AlarmCondition.java
  78. 3
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/DurationAlarmCondition.java
  79. 2
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/RepeatingAlarmCondition.java
  80. 3
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/SimpleAlarmCondition.java
  81. 9
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/AlarmConditionExpression.java
  82. 5
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/AlarmConditionFilter.java
  83. 1
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/SimpleAlarmConditionExpression.java
  84. 3
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/BooleanFilterPredicate.java
  85. 5
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/ComplexFilterPredicate.java
  86. 3
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/FilterPredicateType.java
  87. 14
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/KeyFilterPredicate.java
  88. 3
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/NoDataFilterPredicate.java
  89. 3
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/NumericFilterPredicate.java
  90. 2
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/SimpleKeyFilterPredicate.java
  91. 3
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/StringFilterPredicate.java
  92. 12
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/AlarmSchedule.java
  93. 3
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/AlarmScheduleType.java
  94. 3
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/AnyTimeSchedule.java
  95. 2
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/CustomTimeSchedule.java
  96. 3
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/CustomTimeScheduleItem.java
  97. 2
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/SpecificTimeSchedule.java
  98. 2
      common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedField.java
  99. 2
      common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AlarmCalculatedFieldConfiguration.java
  100. 2
      common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/Argument.java

289
application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java

@ -17,9 +17,11 @@ package org.thingsboard.server.config;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
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.jackson.ModelResolver;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
@ -31,6 +33,7 @@ import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.IntegerSchema;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.RequestBody;
@ -39,10 +42,10 @@ 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.tags.Tag;
import jakarta.annotation.PostConstruct;
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;
@ -54,9 +57,7 @@ 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;
@ -66,7 +67,6 @@ import org.thingsboard.server.exception.ThingsboardErrorResponse;
import org.thingsboard.server.service.security.auth.rest.LoginRequest;
import org.thingsboard.server.service.security.auth.rest.LoginResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedHashMap;
@ -74,6 +74,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.stream.Collectors;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@ -83,11 +84,16 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@Profile("!test")
public class SwaggerConfiguration {
@PostConstruct
public void configureModelResolver() {
ModelResolver.enumsAsRef = true;
}
public static final String LOGIN_ENDPOINT = "/api/auth/login";
public static final String REFRESH_TOKEN_ENDPOINT = "/api/auth/token";
private static final String LOGIN_PASSWORD_SCHEME = "HTTP login form";
private static final String API_KEY_SCHEME = "API key form";
private static final String LOGIN_PASSWORD_SCHEME = "HttpLoginForm";
private static final String API_KEY_SCHEME = "ApiKeyForm";
private static final ApiResponses loginResponses = loginResponses();
private static final ApiResponses defaultErrorResponses = defaultErrorResponses(false);
@ -157,9 +163,9 @@ public class SwaggerConfiguration {
.in(SecurityScheme.In.HEADER)
.description("""
Enter the API key value with 'ApiKey' prefix in format: **ApiKey <your_api_key_value>**
Example: **ApiKey tb_5te51SkLRYpjGrujUGwqkjFvooWBlQpVe2An2Dr3w13wjfxDW**
<br>**NOTE**: Use only ONE authentication method at a time. If both are authorized, JWT auth takes the priority.<br>
""");
@ -213,11 +219,12 @@ public class SwaggerConfiguration {
private void addLoginOperation(OpenAPI openAPI) {
var operation = new Operation();
operation.summary("Login method to get user JWT token data");
operation.operationId("login");
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")
@ -235,11 +242,12 @@ public class SwaggerConfiguration {
private void addRefreshTokenOperation(OpenAPI openAPI) {
var operation = new Operation();
operation.summary("Refresh user JWT token data");
operation.operationId("refreshToken");
operation.description("""
Method to refresh JWT token. Provide a valid refresh token to get a new JWT token.
The response contains a new token that can be used for authorization.
`X-Authorization: Bearer $JWT_TOKEN_VALUE`""");
var requestBody = new RequestBody().description("Refresh token request")
@ -260,7 +268,6 @@ public class SwaggerConfiguration {
return GroupedOpenApi.builder()
.group(groupName)
.pathsToMatch(apiPath)
.addRouterOperationCustomizer(routerOperationCustomizer(localSpringDocParameterNameDiscoverer))
.addOperationCustomizer(operationCustomizer())
.addOpenApiCustomizer(customOpenApiCustomizer())
.build();
@ -270,9 +277,21 @@ public class SwaggerConfiguration {
@Lazy(false)
ModelConverter mapAwareConverter() {
return (type, context, chain) -> {
JavaType javaType = Json.mapper().constructType(type.getType());
if (javaType != null) {
Class<?> cls = javaType.getRawClass();
Schema<?> atomicSchema = switch (cls.getName()) {
case "java.util.concurrent.atomic.AtomicInteger" -> new IntegerSchema().format("int32");
case "java.util.concurrent.atomic.AtomicLong" -> new IntegerSchema().format("int64");
case "com.google.common.util.concurrent.AtomicDouble" -> new IntegerSchema().format("double");
default -> null;
};
if (atomicSchema != null) {
return atomicSchema;
}
}
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)) {
@ -292,45 +311,18 @@ public class SwaggerConfiguration {
}
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)");
Schema<?> errorCodeSchema = new Schema<>()
.type("integer")
.description("Platform error code")
._enum(Arrays.stream(ThingsboardErrorCode.values())
.map(ThingsboardErrorCode::getErrorCode)
.collect(Collectors.toList()));
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 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<String> 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;
};
.addSchemas("ThingsboardCredentialsExpiredResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(ThingsboardCredentialsExpiredResponse.class)).schema)
.addSchemas("ThingsboardErrorCode", errorCodeSchema);
}
private OperationCustomizer operationCustomizer() {
@ -370,17 +362,58 @@ public class SwaggerConfiguration {
});
sortedPaths.setExtensions(paths.getExtensions());
openAPI.setPaths(sortedPaths);
if (openAPI.getComponents() != null && openAPI.getComponents().getSchemas() != null) {
Map<String, Schema> schemas = openAPI.getComponents().getSchemas();
// Fix all schemas: if they have additionalProperties but no type, set type to object
schemas.forEach((schemaName, schema) -> {
if (schema.getAdditionalProperties() != null && schema.getType() == null) {
schema.setType("object");
log.debug("Added type 'object' to schema: {}", schemaName);
}
});
// Fix polymorphic properties: replace inline oneOf with base type $ref
schemas.values().forEach(schema -> {
replaceInlineOneOfProperties(schema, schemas);
if (schema.getAllOf() != null) {
List<Schema> allOf = schema.getAllOf();
for (Schema allOfElement : allOf) {
replaceInlineOneOfProperties(allOfElement, schemas);
}
}
});
// Fix polymorphic request/response bodies: replace inline oneOf with base type $ref
paths.values().stream()
.flatMap(pathItem -> pathItem.readOperationsMap().values().stream())
.forEach(operation -> {
// Request bodies
if (operation.getRequestBody() != null && operation.getRequestBody().getContent() != null) {
replaceInlineOneOfInContent(operation.getRequestBody().getContent(), schemas);
}
// Response bodies
if (operation.getResponses() != null) {
operation.getResponses().values().stream()
.filter(response -> response.getContent() != null)
.forEach(response -> replaceInlineOneOfInContent(response.getContent(), schemas));
}
});
}
// Set JsonNode schema last so model scanning cannot overwrite it
openAPI.getComponents().addSchemas("JsonNode", new Schema<>()
.description("A value representing the any type (object or primitive)")
.example(JacksonUtil.newObjectNode()));
var sortedSchemas = new TreeMap<>(openAPI.getComponents().getSchemas());
openAPI.getComponents().setSchemas(new LinkedHashMap<>(sortedSchemas));
};
}
private SecurityRequirement createSecurityRequirement(String schemeName) {
return new SecurityRequirement().addList(schemeName, Arrays.asList(
Authority.SYS_ADMIN.name(),
Authority.TENANT_ADMIN.name(),
Authority.CUSTOMER_USER.name()
));
return new SecurityRequirement().addList(schemeName, List.of());
}
private Tag extractTagFromPath(Map.Entry<String, PathItem> entry) {
@ -388,6 +421,102 @@ public class SwaggerConfiguration {
return tagName != null ? tagFromTagItem(tagName) : null;
}
private String findBaseTypeForOneOf(Map<String, Schema> schemas, List<Schema> oneOfSchemas) {
if (oneOfSchemas.isEmpty()) {
return null;
}
for (Schema oneOfSchema : oneOfSchemas) {
String ref = oneOfSchema.get$ref();
if (ref == null) {
continue;
}
String refName = ref.substring(ref.lastIndexOf('/') + 1);
// Check if this entry is itself a base type with discriminator
Schema<?> candidate = schemas.get(refName);
if (candidate != null && candidate.getDiscriminator() != null
&& candidate.getDiscriminator().getMapping() != null) {
return refName;
}
// Check if this subtype is in another schema's discriminator mapping
String baseType = schemas.entrySet().stream()
.filter(entry -> {
Schema<?> schema = entry.getValue();
if (schema.getDiscriminator() != null && schema.getDiscriminator().getMapping() != null) {
return schema.getDiscriminator().getMapping().values().stream()
.anyMatch(r -> r.endsWith("/" + refName));
}
return false;
})
.map(Map.Entry::getKey)
.findFirst()
.orElse(null);
if (baseType != null) {
return baseType;
}
}
return null;
}
private void replaceInlineOneOfInContent(Content content, Map<String, Schema> schemas) {
content.values().forEach(mediaType -> {
Schema<?> schema = mediaType.getSchema();
if (schema != null && schema.getOneOf() != null && !schema.getOneOf().isEmpty()) {
String baseType = findBaseTypeForOneOf(schemas, schema.getOneOf());
if (baseType != null) {
Schema<?> refSchema = new Schema<>();
refSchema.set$ref("#/components/schemas/" + baseType);
mediaType.setSchema(refSchema);
log.debug("Replaced oneOf in content with $ref to {}", baseType);
}
}
});
}
@SuppressWarnings("unchecked")
private void replaceInlineOneOfProperties(Schema<?> schema, Map<String, Schema> allSchemas) {
if (schema == null || schema.getProperties() == null) {
return;
}
schema.getProperties().forEach((propName, propSchema) -> {
if (propSchema instanceof Schema) {
Schema<?> prop = (Schema<?>) propSchema;
// Check if additionalProperties has oneOf (polymorphic map values)
if (prop.getAdditionalProperties() instanceof Schema) {
Schema<?> additionalProps = (Schema<?>) prop.getAdditionalProperties();
if (additionalProps.getOneOf() != null && !additionalProps.getOneOf().isEmpty()) {
String baseType = findBaseTypeForOneOf(allSchemas, additionalProps.getOneOf());
if (baseType != null) {
Schema<?> refSchema = new Schema<>();
refSchema.set$ref("#/components/schemas/" + baseType);
prop.setAdditionalProperties(refSchema);
log.debug("Replaced oneOf in additionalProperties with $ref to {} in property {}", baseType, propName);
}
}
}
// If property has oneOf, try to find the base discriminated type
if (prop.getOneOf() != null && !prop.getOneOf().isEmpty()) {
String baseType = findBaseTypeForOneOf(allSchemas, prop.getOneOf());
if (baseType != null) {
Schema<?> refSchema = new Schema<>();
refSchema.set$ref("#/components/schemas/" + baseType);
if (prop.getDescription() != null) {
refSchema.setDescription(prop.getDescription());
}
schema.getProperties().put(propName, refSchema);
log.debug("Replaced oneOf with $ref to {} in property {}", baseType, propName);
}
}
}
});
}
private String tagItemFromPathItem(PathItem item) {
var operations = item.readOperationsMap().values();
var operation = operations.stream().findAny();
@ -480,28 +609,34 @@ public class SwaggerConfiguration {
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<ThingsboardCredentialsExpiredResponse>().$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
Map<String, Example> unauthorizedExamples = new LinkedHashMap<>();
unauthorizedExamples.put("bad-credentials", errorExample("Bad credentials",
ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)));
unauthorizedExamples.put("token-expired", errorExample("JWT token expired",
ThingsboardErrorResponse.of("Token has expired", ThingsboardErrorCode.JWT_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED)));
unauthorizedExamples.put("account-disabled", errorExample("Disabled account",
ThingsboardErrorResponse.of("User account is not active", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)));
unauthorizedExamples.put("account-locked", errorExample("Locked account",
ThingsboardErrorResponse.of("User account is locked due to security policy", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)));
unauthorizedExamples.put("authentication-failed", errorExample("General authentication error",
ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)));
unauthorizedExamples.put("credentials-expired", errorExample("Expired credentials",
ThingsboardCredentialsExpiredResponse.of("User password expired!", StringUtils.randomAlphanumeric(30))));
Schema<? extends ThingsboardErrorResponse> unauthorizedSchema = new Schema<>();
unauthorizedSchema.oneOf(List.of(
new Schema<ThingsboardErrorResponse>().$ref("#/components/schemas/ThingsboardErrorResponse"),
new Schema<ThingsboardCredentialsExpiredResponse>().$ref("#/components/schemas/ThingsboardCredentialsExpiredResponse")
));
apiResponses.addApiResponse("401", errorResponse("Unauthorized", unauthorizedExamples, unauthorizedSchema));
return apiResponses;
}
@ -521,9 +656,11 @@ public class SwaggerConfiguration {
}
private static Example errorExample(String summary, ThingsboardErrorResponse example) {
var node = (ObjectNode) JacksonUtil.valueToTree(example);
node.put("timestamp", 1609459200000L);
return new Example()
.summary(summary)
.value(example);
.value(node);
}
}

4
application/src/main/java/org/thingsboard/server/controller/AdminController.java

@ -417,7 +417,7 @@ public class AdminController extends BaseController {
"provider sends authorization code to specified redirect uri.)")
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@GetMapping(value = "/mail/oauth2/authorize", produces = "application/text")
public String getAuthorizationUrl(HttpServletRequest request, HttpServletResponse response) throws ThingsboardException {
public String getMailOAuth2AuthorizationUrl(HttpServletRequest request, HttpServletResponse response) throws ThingsboardException {
String state = StringUtils.generateSafeToken();
if (request.getParameter(PREV_URI_PATH_PARAMETER) != null) {
CookieUtils.addCookie(response, PREV_URI_COOKIE_NAME, request.getParameter(PREV_URI_PATH_PARAMETER), 180);
@ -442,7 +442,7 @@ public class AdminController extends BaseController {
}
@GetMapping(value = "/mail/oauth2/code", params = {"code", "state"})
public void codeProcessingUrl(
public void handleMailOAuth2Callback(
@RequestParam(value = "code") String code, @RequestParam(value = "state") String state,
HttpServletRequest request, HttpServletResponse response) throws ThingsboardException, IOException {
Optional<Cookie> prevUrlOpt = CookieUtils.getCookie(request, PREV_URI_COOKIE_NAME);

4
application/src/main/java/org/thingsboard/server/controller/AlarmController.java

@ -227,12 +227,12 @@ public class AlarmController extends BaseController {
return tbAlarmService.unassign(alarm, System.currentTimeMillis(), getCurrentUser());
}
@ApiOperation(value = "Get Alarms (getAlarms)",
@ApiOperation(value = "Get Alarms (getAlarmsByEntity)",
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)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/alarm/{entityType}/{entityId}")
public PageData<AlarmInfo> getAlarms(
public PageData<AlarmInfo> getAlarmsByEntity(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE"))
@PathVariable(ENTITY_TYPE) String strEntityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)

42
application/src/main/java/org/thingsboard/server/controller/AssetController.java

@ -16,6 +16,7 @@
package org.thingsboard.server.controller;
import com.google.common.util.concurrent.ListenableFuture;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -23,6 +24,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@ -217,8 +219,7 @@ public class AssetController extends BaseController {
notes = "Returns a page of assets owned by tenant. " +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/assets", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/tenant/assets")
public PageData<Asset> getTenantAssets(
@Parameter(description = PAGE_SIZE_DESCRIPTION)
@RequestParam int pageSize,
@ -245,8 +246,7 @@ public class AssetController extends BaseController {
notes = "Returns a page of assets info objects owned by tenant. " +
PAGE_DATA_PARAMETERS + ASSET_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/assetInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/tenant/assetInfos")
public PageData<AssetInfo> getTenantAssetInfos(
@Parameter(description = PAGE_SIZE_DESCRIPTION)
@RequestParam int pageSize,
@ -274,25 +274,30 @@ public class AssetController extends BaseController {
}
}
@ApiOperation(value = "Get Tenant Asset (getTenantAsset)",
@Hidden
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/tenant/assets", params = {"assetName"})
public Asset getTenantAsset(@RequestParam String assetName) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(assetService.findAssetByTenantIdAndName(tenantId, assetName));
}
@ApiOperation(value = "Get Tenant Asset (getTenantAssetByName)",
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)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/assets", params = {"assetName"}, method = RequestMethod.GET)
@ResponseBody
public Asset getTenantAsset(
@GetMapping(value = "/tenant/asset")
public Asset getTenantAssetByName(
@Parameter(description = ASSET_NAME_DESCRIPTION)
@RequestParam String assetName) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(assetService.findAssetByTenantIdAndName(tenantId, assetName));
return getTenantAsset(assetName);
}
@ApiOperation(value = "Get Customer Assets (getCustomerAssets)",
notes = "Returns a page of assets objects assigned to customer. " +
PAGE_DATA_PARAMETERS)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/assets", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/customer/{customerId}/assets")
public PageData<Asset> getCustomerAssets(
@Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable("customerId") String strCustomerId,
@ -324,8 +329,7 @@ public class AssetController extends BaseController {
notes = "Returns a page of assets info objects assigned to customer. " +
PAGE_DATA_PARAMETERS + ASSET_INFO_DESCRIPTION)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/assetInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/customer/{customerId}/assetInfos")
public PageData<AssetInfo> getCustomerAssetInfos(
@Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable("customerId") String strCustomerId,
@ -361,8 +365,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. ")
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/assets", params = {"assetIds"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/assets")
public List<Asset> getAssetsByIds(
@Parameter(description = "A list of assets ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")))
@RequestParam("assetIds") String[] strAssetIds) throws ThingsboardException, ExecutionException, InterruptedException {
@ -383,14 +386,14 @@ public class AssetController extends BaseController {
return checkNotNull(assets.get());
}
@ApiOperation(value = "Find related assets (findByQuery)",
@ApiOperation(value = "Find related assets (findAssetsByQuery)",
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.")
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/assets", method = RequestMethod.POST)
@ResponseBody
public List<Asset> findByQuery(@RequestBody AssetSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException {
public List<Asset> findAssetsByQuery(@RequestBody AssetSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException {
checkNotNull(query);
checkNotNull(query.getParameters());
checkNotNull(query.getAssetTypes());
@ -469,8 +472,7 @@ public class AssetController extends BaseController {
notes = "Returns a page of assets assigned to edge. " +
PAGE_DATA_PARAMETERS)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/edge/{edgeId}/assets", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/edge/{edgeId}/assets")
public PageData<Asset> getEdgeAssets(
@Parameter(description = EDGE_ID_PARAM_DESCRIPTION)
@PathVariable(EDGE_ID) String strEdgeId,

24
application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.controller;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -175,8 +176,7 @@ public class AssetProfileController extends BaseController {
notes = "Returns a page of asset profile objects owned by tenant. " +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/assetProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/assetProfiles")
public PageData<AssetProfile> getAssetProfiles(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -196,8 +196,7 @@ public class AssetProfileController extends BaseController {
notes = "Returns a page of asset profile info objects owned by tenant. " +
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
@GetMapping(value = "/assetProfileInfos")
public PageData<AssetProfileInfo> getAssetProfileInfos(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -227,12 +226,10 @@ public class AssetProfileController extends BaseController {
return checkNotNull(assetProfileService.findAssetProfileNamesByTenantId(tenantId, activeOnly));
}
@ApiOperation(value = "Get Asset Profiles By Ids (getAssetProfilesByIds)",
notes = "Requested asset profiles must be owned by tenant which is performing the request. " +
NEW_LINE)
@Hidden
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/assetProfileInfos", params = {"assetProfileIds"})
public List<AssetProfileInfo> getAssetProfilesByIds(
public List<AssetProfileInfo> getAssetProfilesByIdsV1(
@Parameter(description = "A list of asset profile ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("assetProfileIds") Set<UUID> assetProfileUUIDs) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
@ -243,4 +240,15 @@ public class AssetProfileController extends BaseController {
return assetProfileService.findAssetProfilesByIds(tenantId, assetProfileIds);
}
@ApiOperation(value = "Get Asset Profiles By Ids (getAssetProfilesByIds)",
notes = "Requested asset profiles must be owned by tenant which is performing the request. " +
NEW_LINE)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/assetProfileInfos/list")
public List<AssetProfileInfo> getAssetProfilesByIds(
@Parameter(description = "A list of asset profile ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("assetProfileIds") Set<UUID> assetProfileUUIDs) throws ThingsboardException {
return getAssetProfilesByIdsV1(assetProfileUUIDs);
}
}

15
application/src/main/java/org/thingsboard/server/controller/AuditLogController.java

@ -18,11 +18,10 @@ package org.thingsboard.server.controller;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.audit.ActionType;
@ -74,8 +73,7 @@ public class AuditLogController extends BaseController {
"and users actions (login, logout, etc.) that belong to this customer. " +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/audit/logs/customer/{customerId}", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/audit/logs/customer/{customerId}")
public PageData<AuditLog> getAuditLogsByCustomerId(
@Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable("customerId") String strCustomerId,
@ -107,8 +105,7 @@ public class AuditLogController extends BaseController {
"For example, RPC call to a particular device, or alarm acknowledgment for a specific device, etc. " +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/audit/logs/user/{userId}", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/audit/logs/user/{userId}")
public PageData<AuditLog> getAuditLogsByUserId(
@Parameter(description = USER_ID_PARAM_DESCRIPTION)
@PathVariable("userId") String strUserId,
@ -141,8 +138,7 @@ public class AuditLogController extends BaseController {
"For example to see when a device was created, updated, assigned to some customer, or even deleted from the system. " +
PAGE_DATA_PARAMETERS + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/audit/logs/entity/{entityType}/{entityId}", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/audit/logs/entity/{entityType}/{entityId}")
public PageData<AuditLog> getAuditLogsByEntityId(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE"))
@PathVariable("entityType") String strEntityType,
@ -176,8 +172,7 @@ public class AuditLogController extends BaseController {
notes = "Returns a page of audit logs related to all entities in the scope of the current user's Tenant. " +
PAGE_DATA_PARAMETERS + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/audit/logs", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/audit/logs")
public PageData<AuditLog> getAuditLogs(
@Parameter(description = PAGE_SIZE_DESCRIPTION)
@RequestParam int pageSize,

4
application/src/main/java/org/thingsboard/server/controller/AuthController.java

@ -136,7 +136,7 @@ public class AuthController extends BaseController {
"If token is valid, returns '303 See Other' (redirect) response code with the correct address of 'Create Password' page and same 'activateToken' specified in the URL parameters. " +
"If token is not valid, returns '409 Conflict'. " +
"If token is expired, redirects to error page.")
@GetMapping(value = "/noauth/activate", params = {"activateToken"})
@GetMapping(value = "/noauth/activate")
public ResponseEntity<?> checkActivateToken(
@Parameter(description = "The activate token string.")
@RequestParam(value = "activateToken") String activateToken) {
@ -176,7 +176,7 @@ public class AuthController extends BaseController {
"If token is valid, returns '303 See Other' (redirect) response code with the correct address of 'Reset Password' page and same 'resetToken' specified in the URL parameters. " +
"If token is not valid, returns '409 Conflict'. " +
"If token is expired, redirects to error page.")
@GetMapping(value = "/noauth/resetPassword", params = {"resetToken"})
@GetMapping(value = "/noauth/resetPassword")
public ResponseEntity<?> checkResetToken(
@Parameter(description = "The reset token string.")
@RequestParam(value = "resetToken") String resetToken) {

44
application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java

@ -17,7 +17,11 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -163,26 +167,16 @@ public class CalculatedFieldController extends BaseController {
return calculatedField;
}
@ApiOperation(value = "Get Calculated Fields by Entity Id (getCalculatedFieldsByEntityId)",
notes = "Fetch the Calculated Fields based on the provided Entity Id."
)
@Hidden
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@GetMapping(value = "/{entityType}/{entityId}/calculatedFields", params = {"pageSize", "page"})
public PageData<CalculatedField> getCalculatedFieldsByEntityId(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE"))
@PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
public PageData<CalculatedField> getCalculatedFieldsByEntityIdV1(@PathVariable("entityType") String entityType,
@PathVariable("entityId") String entityIdStr,
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@Parameter(description = "Calculated field type. If not specified, all types will be returned.")
@RequestParam(required = false) CalculatedFieldType type,
@Parameter(description = CF_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
@Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name"}))
@RequestParam(required = false) String sortProperty,
@Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
checkParameter("entityId", entityIdStr);
@ -191,8 +185,29 @@ public class CalculatedFieldController extends BaseController {
return checkNotNull(tbCalculatedFieldService.findByTenantIdAndEntityId(getTenantId(), entityId, type, pageLink));
}
@ApiOperation(value = "Get Calculated Fields by Entity Id (getCalculatedFieldsByEntityId)",
notes = "Fetch the Calculated Fields based on the provided Entity Id."
)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@GetMapping(value = "/calculatedField/{entityType}/{entityId}")
public PageData<CalculatedField> getCalculatedFieldsByEntityId(
@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 = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize,
@Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true) @RequestParam int page,
@Parameter(description = "Calculated field type. If not specified, all types will be returned.")
@RequestParam(required = false) CalculatedFieldType type,
@Parameter(description = CF_TEXT_SEARCH_DESCRIPTION) @RequestParam(required = false) String textSearch,
@Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name"})) @RequestParam(required = false) String sortProperty,
@Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"})) @RequestParam(required = false) String sortOrder) throws ThingsboardException {
return getCalculatedFieldsByEntityIdV1(entityType, entityIdStr, pageSize, page, type, textSearch, sortProperty, sortOrder);
}
@ApiOperation(value = "Get calculated fields (getCalculatedFields)",
notes = "Fetch tenant calculated fields based on the filter.")
@Parameters({
@Parameter(name = "name", description = "Repeatable name query parameter", in = ParameterIn.QUERY, required = false, array = @ArraySchema(schema = @Schema(type = "string")))
})
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@GetMapping(value = "/calculatedFields")
public PageData<CalculatedFieldInfo> getCalculatedFields(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@ -205,14 +220,13 @@ public class CalculatedFieldController extends BaseController {
@RequestParam(required = false) EntityType entityType,
@Parameter(description = "Entities filter. If not specified, calculated fields for entity type filter will be returned.")
@RequestParam(required = false) Set<UUID> entities,
@Parameter(description = "Name filter. To specify multiple names, duplicate 'name' parameter for each name, for example '?name=name1&name=name2")
@RequestParam(required = false) String name, // for Swagger only, retrieved from MultiValueMap params (due to issues when name contains comma)
@Parameter(description = CF_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
@Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name"}))
@RequestParam(required = false) String sortProperty,
@Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
@Parameter(hidden = true)
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
SecurityUser user = getCurrentUser();
@ -289,7 +303,7 @@ public class CalculatedFieldController extends BaseController {
notes = TEST_SCRIPT_EXPRESSION + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@PostMapping("/calculatedField/testScript")
public JsonNode testScript(
public JsonNode testCalculatedFieldScript(
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Test calculated field TBEL expression.")
@RequestBody JsonNode inputParams) {
String expression = inputParams.get("expression").asText();

4
application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java

@ -19,6 +19,7 @@ import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@ -81,8 +82,7 @@ public class ComponentDescriptorController extends BaseController {
notes = "Gets the Component Descriptors using coma separated list of rule node types and optional rule chain type request parameters. " +
COMPONENT_DESCRIPTOR_DEFINITION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
@RequestMapping(value = "/components", params = {"componentTypes"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/components")
public List<ComponentDescriptor> getComponentDescriptorsByTypes(
@Parameter(description = "List of types of the Rule Nodes, (ENRICHMENT, FILTER, TRANSFORMATION, ACTION or EXTERNAL)", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("componentTypes") String[] strComponentTypes,

24
application/src/main/java/org/thingsboard/server/controller/CustomerController.java

@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -172,8 +173,7 @@ public class CustomerController extends BaseController {
notes = "Returns a page of customers owned by tenant. " +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customers", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/customers")
public PageData<Customer> getCustomers(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -193,8 +193,7 @@ public class CustomerController extends BaseController {
@ApiOperation(value = "Get Tenant Customer by Customer title (getTenantCustomer)",
notes = "Get the Customer using Customer Title. " + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/customers", params = {"customerTitle"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/tenant/customers")
public Customer getTenantCustomer(
@Parameter(description = "A string value representing the Customer title.")
@RequestParam String customerTitle) throws ThingsboardException {
@ -202,12 +201,10 @@ public class CustomerController extends BaseController {
return checkNotNull(customerService.findCustomerByTenantIdAndTitle(tenantId, customerTitle), "Customer with title [" + customerTitle + "] is not found");
}
@ApiOperation(value = "Get customers by Customer Ids (getCustomersByIds)",
notes = "Returns a list of Customer objects based on the provided ids." +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@Hidden
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@GetMapping(value = "/customers", params = {"customerIds"})
public List<Customer> getCustomersByIds(
public List<Customer> getCustomersByIdsV1(
@Parameter(description = "A list of customer ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("customerIds") Set<UUID> customerUUIDs) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
@ -218,4 +215,15 @@ public class CustomerController extends BaseController {
return customerService.findCustomersByTenantIdAndIds(tenantId, customerIds);
}
@ApiOperation(value = "Get customers by Customer Ids (getCustomersByIds)",
notes = "Returns a list of Customer objects based on the provided ids." +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@GetMapping(value = "/customers/list")
public List<Customer> getCustomersByIds(
@Parameter(description = "A list of customer ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("customerIds") Set<UUID> customerUUIDs) throws ThingsboardException {
return getCustomersByIdsV1(customerUUIDs);
}
}

37
application/src/main/java/org/thingsboard/server/controller/DashboardController.java

@ -17,10 +17,10 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import jakarta.servlet.http.HttpServletResponse;
@ -119,7 +119,7 @@ public class DashboardController extends BaseController {
"Used to adjust view of the dashboards according to the difference between browser and server time.")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/dashboard/serverTime")
@ApiResponse(responseCode = "200", description = "OK", content = @Content(mediaType = "application/json", examples = @ExampleObject(value = "1636023857137")))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(type = "integer", format = "int64", example = "1636023857137")))
public long getServerTime() {
return System.currentTimeMillis();
}
@ -131,7 +131,7 @@ public class DashboardController extends BaseController {
"The actual value of the limit is configurable in the system configuration file.")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/dashboard/maxDatapointsLimit")
@ApiResponse(responseCode = "200", description = "OK", content = @Content(mediaType = "application/json", examples = @ExampleObject(value = "5000")))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(type = "integer", format = "int64", example = "5000")))
public long getMaxDatapointsLimit() {
return maxDatapointsLimit;
}
@ -176,6 +176,8 @@ public class DashboardController extends BaseController {
"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)
@ApiResponse(responseCode = "200", description = "OK",
content = @Content(schema = @Schema(implementation = Dashboard.class)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@PostMapping(value = "/dashboard")
public void saveDashboard(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the dashboard.")
@ -325,12 +327,12 @@ public class DashboardController extends BaseController {
return tbDashboardService.unassignDashboardFromPublicCustomer(dashboard, getCurrentUser());
}
@ApiOperation(value = "Get Tenant Dashboards by System Administrator (getTenantDashboards)",
@ApiOperation(value = "Get Tenant Dashboards by System Administrator (getTenantDashboardsByTenantId)",
notes = "Returns a page of dashboard info objects owned by tenant. " + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS +
SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@GetMapping(value = "/tenant/{tenantId}/dashboards", params = {"pageSize", "page"})
public PageData<DashboardInfo> getTenantDashboards(
@GetMapping(value = "/tenant/{tenantId}/dashboards")
public PageData<DashboardInfo> getTenantDashboardsByTenantId(
@Parameter(description = TENANT_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(TENANT_ID) String strTenantId,
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@ -353,7 +355,7 @@ public class DashboardController extends BaseController {
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)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/tenant/dashboards", params = {"pageSize", "page"})
@GetMapping(value = "/tenant/dashboards")
public PageData<DashboardInfo> getTenantDashboards(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -380,7 +382,7 @@ public class DashboardController extends BaseController {
notes = "Returns a page of dashboard info objects owned by the specified customer. "
+ DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/customer/{customerId}/dashboards", params = {"pageSize", "page"})
@GetMapping(value = "/customer/{customerId}/dashboards")
public PageData<DashboardInfo> getCustomerDashboards(
@Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(CUSTOMER_ID) String strCustomerId,
@ -574,7 +576,7 @@ public class DashboardController extends BaseController {
notes = "Returns a page of dashboard info objects assigned to the specified edge. "
+ DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/edge/{edgeId}/dashboards", params = {"pageSize", "page"})
@GetMapping(value = "/edge/{edgeId}/dashboards")
public PageData<DashboardInfo> getEdgeDashboards(
@Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(EDGE_ID) String strEdgeId,
@ -602,13 +604,10 @@ public class DashboardController extends BaseController {
return checkNotNull(filteredResult);
}
@ApiOperation(value = "Get dashboards by Dashboard Ids (getDashboardsByIds)",
notes = "Returns a list of DashboardInfo objects based on the provided ids. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@Hidden
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/dashboards", params = {"dashboardIds"})
public List<DashboardInfo> getDashboardsByIds(@Parameter(description = "A list of dashboard ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("dashboardIds") Set<UUID> dashboardUUIDs) throws ThingsboardException {
public List<DashboardInfo> getDashboardsByIdsV1(@RequestParam("dashboardIds") Set<UUID> dashboardUUIDs) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
List<DashboardId> dashboardIds = new ArrayList<>();
for (UUID dashboardUUID : dashboardUUIDs) {
@ -618,6 +617,16 @@ public class DashboardController extends BaseController {
return filterDashboardsByReadPermission(dashboards);
}
@ApiOperation(value = "Get dashboards by Dashboard Ids (getDashboardsByIds)",
notes = "Returns a list of DashboardInfo objects based on the provided ids. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/dashboards/list")
public List<DashboardInfo> getDashboardsByIds(@Parameter(description = "A list of dashboard ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("dashboardIds") Set<UUID> dashboardUUIDs) throws ThingsboardException {
return getDashboardsByIdsV1(dashboardUUIDs);
}
private Set<CustomerId> customerIdFromStr(String[] strCustomerIds) {
Set<CustomerId> customerIds = new HashSet<>();
if (strCustomerIds != null) {

28
application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java

@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
@ -75,6 +76,7 @@ public class DeviceConnectivityController extends BaseController {
description = "OK",
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @Schema(implementation = JsonNode.class),
examples = {
@ExampleObject(
name = "http",
@ -105,7 +107,18 @@ public class DeviceConnectivityController extends BaseController {
return deviceConnectivityService.findDevicePublishTelemetryCommands(baseUrl, device);
}
@ApiOperation(value = "Download server certificate using file path defined in device.connectivity properties (downloadServerCertificate)", notes = "Download server certificate.")
@ApiOperation(value = "Download server certificate using file path defined in device.connectivity properties (downloadServerCertificate)",
notes = "Download server certificate.",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(
mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE,
schema = @Schema(type = "string", format = "binary")
)
)
})
@RequestMapping(value = "/device-connectivity/{protocol}/certificate/download", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<org.springframework.core.io.Resource> downloadServerCertificate(@Parameter(description = PROTOCOL_PARAM_DESCRIPTION)
@ -122,7 +135,18 @@ public class DeviceConnectivityController extends BaseController {
.body(pemCert);
}
@ApiOperation(value = "Download generated docker-compose.yml file for gateway (downloadGatewayDockerCompose)", notes = "Download generated docker-compose.yml for gateway.")
@ApiOperation(value = "Download generated docker-compose.yml file for gateway (downloadGatewayDockerCompose)",
notes = "Download generated docker-compose.yml for gateway.",
responses = {
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(
mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE,
schema = @Schema(type = "string", format = "binary")
)
)
})
@RequestMapping(value = "/device-connectivity/gateway-launch/{deviceId}/docker-compose/download", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<org.springframework.core.io.Resource> downloadGatewayDockerCompose(@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)

43
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -19,6 +19,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 io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -29,6 +30,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@ -350,8 +352,7 @@ public class DeviceController extends BaseController {
notes = "Returns a page of devices owned by tenant. " +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/devices", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/tenant/devices")
public PageData<Device> getTenantDevices(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -378,8 +379,7 @@ public class DeviceController extends BaseController {
notes = "Returns a page of devices info objects owned by tenant. " +
PAGE_DATA_PARAMETERS + DEVICE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/deviceInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/tenant/deviceInfos")
public PageData<DeviceInfo> getTenantDeviceInfos(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -411,25 +411,31 @@ public class DeviceController extends BaseController {
return checkNotNull(deviceService.findDeviceInfosByFilter(filter.build(), pageLink));
}
@ApiOperation(value = "Get Tenant Device (getTenantDevice)",
notes = "Requested device must be owned by tenant that the user belongs to. " +
"Device name is an unique property of device. So it can be used to identify the device." + TENANT_AUTHORITY_PARAGRAPH)
@Hidden
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/devices", params = {"deviceName"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/tenant/devices", params = {"deviceName"})
public Device getTenantDevice(
@Parameter(description = DEVICE_NAME_DESCRIPTION)
@RequestParam String deviceName) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(deviceService.findDeviceByTenantIdAndName(tenantId, deviceName));
}
@ApiOperation(value = "Get Tenant Device (getTenantDeviceByName)",
notes = "Requested device must be owned by tenant that the user belongs to. " +
"Device name is an unique property of device. So it can be used to identify the device." + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/tenant/device")
public Device getTenantDeviceByName(
@Parameter(description = DEVICE_NAME_DESCRIPTION)
@RequestParam String deviceName) throws ThingsboardException {
return getTenantDevice(deviceName);
}
@ApiOperation(value = "Get Customer Devices (getCustomerDevices)",
notes = "Returns a page of devices objects assigned to customer. " +
PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/devices", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/customer/{customerId}/devices")
public PageData<Device> getCustomerDevices(
@Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(CUSTOMER_ID) String strCustomerId,
@ -461,8 +467,7 @@ public class DeviceController extends BaseController {
notes = "Returns a page of devices info objects assigned to customer. " +
PAGE_DATA_PARAMETERS + DEVICE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/deviceInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/customer/{customerId}/deviceInfos")
public PageData<DeviceInfo> getCustomerDeviceInfos(
@Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("customerId") String strCustomerId,
@ -502,8 +507,7 @@ public class DeviceController extends BaseController {
@ApiOperation(value = "Get Devices By Ids (getDevicesByIds)",
notes = "Requested devices 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 = "/devices", params = {"deviceIds"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/devices")
public List<Device> getDevicesByIds(
@Parameter(description = "A list of devices ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")))
@RequestParam("deviceIds") String[] strDeviceIds) throws ThingsboardException, ExecutionException, InterruptedException {
@ -524,14 +528,14 @@ public class DeviceController extends BaseController {
return checkNotNull(devices.get());
}
@ApiOperation(value = "Find related devices (findByQuery)",
@ApiOperation(value = "Find related devices (findDevicesByQuery)",
notes = "Returns all devices that are related to the specific entity. " +
"The entity id, relation type, device types, depth of the search, and other query parameters defined using complex 'DeviceSearchQuery' object. " +
"See 'Model' tab of the Parameters for more info." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/devices", method = RequestMethod.POST)
@ResponseBody
public List<Device> findByQuery(
public List<Device> findDevicesByQuery(
@Parameter(description = "The device search query JSON")
@RequestBody DeviceSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException {
checkNotNull(query);
@ -730,8 +734,7 @@ public class DeviceController extends BaseController {
notes = "Returns a page of devices assigned to edge. " +
PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/edge/{edgeId}/devices", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/edge/{edgeId}/devices")
public PageData<DeviceInfo> getEdgeDevices(
@Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(EDGE_ID) String strEdgeId,

30
application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.controller;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -129,7 +130,7 @@ public class DeviceProfileController extends BaseController {
return checkNotNull(deviceProfileService.findDefaultDeviceProfileInfo(getTenantId()));
}
@ApiOperation(value = "Get time series keys (getTimeseriesKeys)",
@ApiOperation(value = "Get time series keys (getDeviceProfileTimeseriesKeys)",
notes = "Get a set of unique time series keys used by devices that belong to specified profile. " +
"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. " +
@ -138,7 +139,7 @@ public class DeviceProfileController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/deviceProfile/devices/keys/timeseries", method = RequestMethod.GET)
@ResponseBody
public List<String> getTimeseriesKeys(
public List<String> getDeviceProfileTimeseriesKeys(
@Parameter(description = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
@RequestParam(name = DEVICE_PROFILE_ID, required = false) String deviceProfileIdStr) throws ThingsboardException {
DeviceProfileId deviceProfileId;
@ -228,8 +229,7 @@ public class DeviceProfileController extends BaseController {
notes = "Returns a page of devices profile objects owned by tenant. " +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/deviceProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/deviceProfiles")
public PageData<DeviceProfile> getDeviceProfiles(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -249,8 +249,7 @@ public class DeviceProfileController extends BaseController {
notes = "Returns a page of devices profile info objects owned by tenant. " +
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
@GetMapping(value = "/deviceProfileInfos")
public PageData<DeviceProfileInfo> getDeviceProfileInfos(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -282,14 +281,10 @@ public class DeviceProfileController extends BaseController {
return checkNotNull(deviceProfileService.findDeviceProfileNamesByTenantId(tenantId, activeOnly));
}
@ApiOperation(value = "Get Device Profile Infos By Ids (getDeviceProfilesByIds)",
notes = "Requested device profiles must be owned by tenant which is performing the request. " +
NEW_LINE)
@Hidden
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/deviceProfileInfos", params = {"deviceProfileIds"})
public List<DeviceProfileInfo> getDeviceProfileInfosByIds(
@Parameter(description = "A list of device profile ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("deviceProfileIds") Set<UUID> deviceProfileUUIDs) throws ThingsboardException {
public List<DeviceProfileInfo> getDeviceProfileInfosByIdsV1(@RequestParam("deviceProfileIds") Set<UUID> deviceProfileUUIDs) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
List<DeviceProfileId> deviceProfileIds = new ArrayList<>();
for (UUID deviceProfileUUID : deviceProfileUUIDs) {
@ -298,4 +293,15 @@ public class DeviceProfileController extends BaseController {
return deviceProfileService.findDeviceProfilesByIds(tenantId, deviceProfileIds);
}
@ApiOperation(value = "Get Device Profile Infos By Ids (getDeviceProfileInfosByIds)",
notes = "Requested device profiles must be owned by tenant which is performing the request. " +
NEW_LINE)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/deviceProfileInfos/list")
public List<DeviceProfileInfo> getDeviceProfileInfosByIds(
@Parameter(description = "A list of device profile ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("deviceProfileIds") Set<UUID> deviceProfileUUIDs) throws ThingsboardException {
return getDeviceProfileInfosByIdsV1(deviceProfileUUIDs);
}
}

6
application/src/main/java/org/thingsboard/server/controller/DomainController.java

@ -81,12 +81,12 @@ public class DomainController extends BaseController {
return tbDomainService.save(domain, getOAuth2ClientIds(ids), getCurrentUser());
}
@ApiOperation(value = "Update oauth2 clients (updateOauth2Clients)",
@ApiOperation(value = "Update oauth2 clients (updateDomainOauth2Clients)",
notes = "Update oauth2 clients for the specified domain. ")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@PutMapping(value = "/domain/{id}/oauth2Clients")
public void updateOauth2Clients(@PathVariable UUID id,
@RequestBody UUID[] clientIds) throws ThingsboardException {
public void updateDomainOauth2Clients(@PathVariable UUID id,
@RequestBody UUID[] clientIds) throws ThingsboardException {
DomainId domainId = new DomainId(id);
Domain domain = checkDomainId(domainId, Operation.WRITE);
List<OAuth2ClientId> oAuth2ClientIds = getOAuth2ClientIds(clientIds);

42
application/src/main/java/org/thingsboard/server/controller/EdgeController.java

@ -16,6 +16,7 @@
package org.thingsboard.server.controller;
import com.google.common.util.concurrent.ListenableFuture;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -277,7 +278,7 @@ public class EdgeController extends BaseController {
notes = "Returns a page of edges info objects owned by tenant. " +
PAGE_DATA_PARAMETERS + EDGE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/tenant/edgeInfos", params = {"pageSize", "page"})
@GetMapping(value = "/tenant/edgeInfos")
public PageData<EdgeInfo> getTenantEdgeInfos(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -300,17 +301,24 @@ 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)
@Hidden
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/tenant/edges", params = {"edgeName"})
public Edge getTenantEdge(@Parameter(description = "Unique name of the edge", required = true)
@RequestParam String edgeName) throws ThingsboardException {
public Edge getTenantEdge(@RequestParam String edgeName) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(edgeService.findEdgeByTenantIdAndName(tenantId, edgeName));
}
@ApiOperation(value = "Get Tenant Edge by name (getTenantEdgeByName)",
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)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/tenant/edge")
public Edge getTenantEdgeByName(@Parameter(description = "Unique name of the edge", required = true)
@RequestParam String edgeName) throws ThingsboardException {
return getTenantEdge(edgeName);
}
@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)
@ -334,7 +342,7 @@ public class EdgeController extends BaseController {
notes = "Returns a page of edges objects assigned to customer. " +
PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/customer/{customerId}/edges", params = {"pageSize", "page"})
@GetMapping(value = "/customer/{customerId}/edges")
public PageData<Edge> getCustomerEdges(
@Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable("customerId") String strCustomerId,
@ -369,7 +377,7 @@ public class EdgeController extends BaseController {
notes = "Returns a page of edges info objects assigned to customer. " +
PAGE_DATA_PARAMETERS + EDGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/customer/{customerId}/edgeInfos", params = {"pageSize", "page"})
@GetMapping(value = "/customer/{customerId}/edgeInfos")
public PageData<EdgeInfo> getCustomerEdgeInfos(
@Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable("customerId") String strCustomerId,
@ -400,12 +408,10 @@ public class EdgeController extends BaseController {
return checkNotNull(result);
}
@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)
@Hidden
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/edges", params = {"edgeIds"})
public List<Edge> getEdgesByIds(
@Parameter(description = "A list of edges ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("edgeIds") String[] strEdgeIds) throws ThingsboardException, ExecutionException, InterruptedException {
checkArrayParameter("edgeIds", strEdgeIds);
SecurityUser user = getCurrentUser();
@ -425,13 +431,23 @@ public class EdgeController extends BaseController {
return checkNotNull(edges);
}
@ApiOperation(value = "Find related edges (findByQuery)",
@ApiOperation(value = "Get Edges By Ids (getEdgeList)",
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')")
@GetMapping(value = "/edges/list")
public List<Edge> getEdgeList(
@Parameter(description = "A list of edges ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("edgeIds") String[] strEdgeIds) throws ThingsboardException, ExecutionException, InterruptedException {
return getEdgesByIds(strEdgeIds);
}
@ApiOperation(value = "Find related edges (findEdgesByQuery)",
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)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@PostMapping(value = "/edges")
public List<Edge> findByQuery(@RequestBody EdgeSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException {
public List<Edge> findEdgesByQuery(@RequestBody EdgeSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException {
checkNotNull(query);
checkNotNull(query.getParameters());
checkNotNull(query.getEdgeTypes());

9
application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java

@ -19,6 +19,7 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
@ -234,7 +235,7 @@ public class EntitiesVersionControlController extends BaseController {
"}" +
MARKDOWN_CODE_BLOCK_END +
TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/version/{entityType}/{externalEntityUuid}", params = {"branch", "pageSize", "page"})
@GetMapping(value = "/version/{entityType}/{externalEntityUuid}")
public DeferredResult<PageData<EntityVersion>> listEntityVersions(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable EntityType entityType,
@Parameter(description = "A string value representing external entity id. This is `externalId` property of an entity, or otherwise if not set - simply id of this entity.")
@ -263,7 +264,7 @@ public class EntitiesVersionControlController extends BaseController {
"If specified branch does not exist - empty page data will be returned. " +
"The response structure is the same as for `listEntityVersions` API method." +
TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/version/{entityType}", params = {"branch", "pageSize", "page"})
@GetMapping(value = "/version/{entityType}")
public DeferredResult<PageData<EntityVersion>> listEntityTypeVersions(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable EntityType entityType,
@Parameter(description = BRANCH_PARAM_DESCRIPTION, required = true)
@ -288,7 +289,7 @@ public class EntitiesVersionControlController extends BaseController {
"If specified branch does not exist - empty page data will be returned. " +
"The response format is the same as for `listEntityVersions` API method." +
TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/version", params = {"branch", "pageSize", "page"})
@GetMapping(value = "/version")
public DeferredResult<PageData<EntityVersion>> listVersions(@Parameter(description = BRANCH_PARAM_DESCRIPTION, required = true)
@RequestParam String branch,
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@ -355,7 +356,7 @@ public class EntitiesVersionControlController extends BaseController {
"Returns an object with current entity data and the one at a specific version. " +
"Entity data structure is the same as stored in a repository. " +
TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/diff/{entityType}/{internalEntityUuid}", params = {"versionId"})
@GetMapping(value = "/diff/{entityType}/{internalEntityUuid}")
public DeferredResult<EntityDataDiff> compareEntityDataToVersion(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable EntityType entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)

163
application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java

@ -15,11 +15,13 @@
*/
package org.thingsboard.server.controller;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@ -71,25 +73,22 @@ public class EntityRelationController extends BaseController {
"If the user has the authority of 'Tenant Administrator', the server checks that the entity is owned by the same tenant. " +
"If the user has the authority of 'Customer User', the server checks that the entity is assigned to the same customer.";
@ApiOperation(value = "Create Relation (saveRelation)",
notes = "Creates or updates a relation between two entities in the platform. " +
"Relations unique key is a combination of from/to entity id and relation type group and relation type. " +
SECURITY_CHECKS_ENTITIES_DESCRIPTION)
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@PostMapping("/relation")
public void saveRelation(@Parameter(description = "A JSON value representing the relation.", required = true)
@PostMapping(value = "/relation")
public void saveRelationV1(@Parameter(description = "A JSON value representing the relation.", required = true)
@RequestBody EntityRelation relation) throws ThingsboardException {
doSave(relation);
}
@ApiOperation(value = "Create Relation (saveRelationV2)",
@ApiOperation(value = "Create Relation (saveRelation)",
notes = "Creates or updates a relation between two entities in the platform. " +
"Relations unique key is a combination of from/to entity id and relation type group and relation type. " +
SECURITY_CHECKS_ENTITIES_DESCRIPTION)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@PostMapping("/v2/relation")
public EntityRelation saveRelationV2(@Parameter(description = "A JSON value representing the relation.", required = true)
@RequestBody EntityRelation relation) throws ThingsboardException {
@PostMapping(value = "/v2/relation")
public EntityRelation saveRelation(@Parameter(description = "A JSON value representing the relation.", required = true)
@RequestBody EntityRelation relation) throws ThingsboardException {
return doSave(relation);
}
@ -106,7 +105,7 @@ public class EntityRelationController extends BaseController {
@ApiOperation(value = "Delete Relation (deleteRelation)",
notes = "Deletes a relation between two entities in the platform. " + SECURITY_CHECKS_ENTITIES_DESCRIPTION)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@DeleteMapping(value = "/relation", params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE})
@DeleteMapping(value = "/relation")
public void deleteRelation(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
@Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
@ -116,16 +115,16 @@ public class EntityRelationController extends BaseController {
doDelete(strFromId, strFromType, strRelationType, strRelationTypeGroup, strToId, strToType);
}
@ApiOperation(value = "Delete Relation (deleteRelationV2)",
@ApiOperation(value = "Delete Relation (deleteRelationAndReturn)",
notes = "Deletes a relation between two entities in the platform. " + SECURITY_CHECKS_ENTITIES_DESCRIPTION)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@DeleteMapping(value = "/v2/relation", params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE})
public EntityRelation deleteRelationV2(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
@Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
@Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType) throws ThingsboardException {
@DeleteMapping(value = "/v2/relation")
public EntityRelation deleteRelationAndReturn(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
@Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
@Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType) throws ThingsboardException {
return doDelete(strFromId, strFromType, strRelationType, strRelationTypeGroup, strToId, strToType);
}
@ -144,11 +143,11 @@ public class EntityRelationController extends BaseController {
return tbEntityRelationService.delete(getTenantId(), getCurrentUser().getCustomerId(), relation, getCurrentUser());
}
@ApiOperation(value = "Delete common relations (deleteCommonRelations)",
@ApiOperation(value = "Delete common relations (deleteRelations)",
notes = "Deletes all the relations ('from' and 'to' direction) for the specified entity and relation type group: 'COMMON'. " +
SECURITY_CHECKS_ENTITY_DESCRIPTION)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')")
@DeleteMapping(value = "/relations", params = {"entityId", "entityType"})
@DeleteMapping(value = "/relations")
public void deleteRelations(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam("entityId") String strId,
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam("entityType") String strType) throws ThingsboardException {
checkParameter("entityId", strId);
@ -161,7 +160,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)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/relation", params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE})
@GetMapping(value = "/relation")
public EntityRelation getRelation(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
@Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
@ -180,14 +179,11 @@ public class EntityRelationController extends BaseController {
return checkNotNull(relationService.getRelation(getTenantId(), fromId, toId, strRelationType, typeGroup));
}
@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)
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/relations", params = {FROM_ID, FROM_TYPE})
public List<EntityRelation> findByFrom(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
@Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
public List<EntityRelation> findByFrom(@RequestParam(FROM_ID) String strFromId,
@RequestParam(FROM_TYPE) String strFromType,
@RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
checkParameter(FROM_ID, strFromId);
checkParameter(FROM_TYPE, strFromType);
@ -197,9 +193,19 @@ public class EntityRelationController extends BaseController {
return checkNotNull(filterRelationsByReadPermission(relationService.findByFrom(getTenantId(), entityId, typeGroup)));
}
@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)
@ApiOperation(value = "Get List of Relations (findEntityRelationsByFrom)",
notes = "Returns list of relation objects for the specified entity by the 'from' direction. " +
SECURITY_CHECKS_ENTITY_DESCRIPTION)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/relations/from/{fromType}/{fromId}")
public List<EntityRelation> findEntityRelationsByFrom(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @PathVariable(FROM_TYPE) String strFromType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable(FROM_ID) String strFromId,
@Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
@RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
return findByFrom(strFromId, strFromType, strRelationTypeGroup);
}
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/relations/info", params = {FROM_ID, FROM_TYPE})
public List<EntityRelationInfo> findInfoByFrom(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
@ -214,15 +220,24 @@ public class EntityRelationController extends BaseController {
return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByFrom(getTenantId(), entityId, typeGroup).get()));
}
@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)
@ApiOperation(value = "Get List of Relation Infos (findEntityRelationInfosByFrom)",
notes = "Returns list of relation info objects for the specified entity by the 'from' direction. " +
SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/relations/info/from/{fromType}/{fromId}")
public List<EntityRelationInfo> findEntityRelationInfosByFrom(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @PathVariable(FROM_TYPE) String strFromType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable(FROM_ID) String strFromId,
@Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
@RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException, ExecutionException, InterruptedException {
return findInfoByFrom(strFromId, strFromType, strRelationTypeGroup);
}
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/relations", params = {FROM_ID, FROM_TYPE, RELATION_TYPE})
public List<EntityRelation> findByFrom(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
@Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
@Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
public List<EntityRelation> findByFrom(@RequestParam(FROM_ID) String strFromId,
@RequestParam(FROM_TYPE) String strFromType,
@RequestParam(RELATION_TYPE) String strRelationType,
@RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
checkParameter(FROM_ID, strFromId);
checkParameter(FROM_TYPE, strFromType);
@ -233,10 +248,21 @@ public class EntityRelationController extends BaseController {
return checkNotNull(filterRelationsByReadPermission(relationService.findByFromAndType(getTenantId(), entityId, strRelationType, typeGroup)));
}
@ApiOperation(value = "Get List of Relations (findByTo)",
notes = "Returns list of relation objects for the specified entity by the 'to' direction. " +
@ApiOperation(value = "Get List of Relations (findEntityRelationsByFromAndRelationType)",
notes = "Returns list of relation objects for the specified entity by the 'from' direction and relation type. " +
SECURITY_CHECKS_ENTITY_DESCRIPTION)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/relations/from/{fromType}/{fromId}/{relationType}")
public List<EntityRelation> findEntityRelationsByFromAndRelationType(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @PathVariable(FROM_TYPE) String strFromType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable(FROM_ID) String strFromId,
@Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @PathVariable(RELATION_TYPE) String strRelationType,
@Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
@RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
return findByFrom(strFromId, strFromType, strRelationType, strRelationTypeGroup);
}
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/relations", params = {TO_ID, TO_TYPE})
public List<EntityRelation> findByTo(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType,
@ -250,9 +276,19 @@ public class EntityRelationController extends BaseController {
return checkNotNull(filterRelationsByReadPermission(relationService.findByTo(getTenantId(), entityId, typeGroup)));
}
@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)
@ApiOperation(value = "Get List of Relations (findEntityRelationsByTo)",
notes = "Returns list of relation objects for the specified entity by the 'to' direction. " +
SECURITY_CHECKS_ENTITY_DESCRIPTION)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/relations/to/{toType}/{toId}")
public List<EntityRelation> findEntityRelationsByTo(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @PathVariable(TO_TYPE) String strToType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable(TO_ID) String strToId,
@Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
@RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
return findByTo(strToId, strToType, strRelationTypeGroup);
}
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/relations/info", params = {TO_ID, TO_TYPE})
public List<EntityRelationInfo> findInfoByTo(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
@ -267,9 +303,19 @@ public class EntityRelationController extends BaseController {
return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByTo(getTenantId(), entityId, typeGroup).get()));
}
@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)
@ApiOperation(value = "Get List of Relation Infos (findEntityRelationInfosByTo)",
notes = "Returns list of relation info objects for the specified entity by the 'to' direction. " +
SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/relations/info/to/{toType}/{toId}")
public List<EntityRelationInfo> findEntityRelationInfosByTo(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @PathVariable(TO_TYPE) String strToType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable(TO_ID) String strToId,
@Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
@RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException, ExecutionException, InterruptedException {
return findInfoByTo(strToId, strToType, strRelationTypeGroup);
}
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/relations", params = {TO_ID, TO_TYPE, RELATION_TYPE})
public List<EntityRelation> findByTo(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
@ -286,28 +332,41 @@ public class EntityRelationController extends BaseController {
return checkNotNull(filterRelationsByReadPermission(relationService.findByToAndType(getTenantId(), entityId, strRelationType, typeGroup)));
}
@ApiOperation(value = "Find related entities (findByQuery)",
@ApiOperation(value = "Get List of Relations (findEntityRelationsByToAndRelationType)",
notes = "Returns list of relation objects for the specified entity by the 'to' direction and relation type. " +
SECURITY_CHECKS_ENTITY_DESCRIPTION)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/relations/to/{toType}/{toId}/{relationType}")
public List<EntityRelation> findEntityRelationsByToAndRelationType(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @PathVariable(TO_TYPE) String strToType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable(TO_ID) String strToId,
@Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @PathVariable(RELATION_TYPE) String strRelationType,
@Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
@RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
return findByTo(strToId, strToType, strRelationType, strRelationTypeGroup);
}
@ApiOperation(value = "Find related entities (findEntityRelationsByQuery)",
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.")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@PostMapping("/relations")
public List<EntityRelation> findByQuery(@Parameter(description = "A JSON value representing the entity relations query object.", required = true)
@RequestBody EntityRelationsQuery query) throws ThingsboardException, ExecutionException, InterruptedException {
public List<EntityRelation> findEntityRelationsByQuery(@Parameter(description = "A JSON value representing the entity relations query object.", required = true)
@RequestBody EntityRelationsQuery query) throws ThingsboardException, ExecutionException, InterruptedException {
checkNotNull(query.getParameters());
checkNotNull(query.getFilters());
checkEntityId(query.getParameters().getEntityId(), Operation.READ);
return checkNotNull(filterRelationsByReadPermission(relationService.findByQuery(getTenantId(), query).get()));
}
@ApiOperation(value = "Find related entity infos (findInfoByQuery)",
@ApiOperation(value = "Find related entity infos (findEntityRelationInfosByQuery)",
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)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@PostMapping("/relations/info")
public List<EntityRelationInfo> findInfoByQuery(@Parameter(description = "A JSON value representing the entity relations query object.", required = true)
@RequestBody EntityRelationsQuery query) throws ThingsboardException, ExecutionException, InterruptedException {
public List<EntityRelationInfo> findEntityRelationInfosByQuery(@Parameter(description = "A JSON value representing the entity relations query object.", required = true)
@RequestBody EntityRelationsQuery query) throws ThingsboardException, ExecutionException, InterruptedException {
checkNotNull(query.getParameters());
checkNotNull(query.getFilters());
checkEntityId(query.getParameters().getEntityId(), Operation.READ);

46
application/src/main/java/org/thingsboard/server/controller/EntityViewController.java

@ -16,6 +16,7 @@
package org.thingsboard.server.controller;
import com.google.common.util.concurrent.ListenableFuture;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -23,7 +24,6 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@ -79,7 +79,6 @@ import static org.thingsboard.server.controller.ControllerConstants.ENTITY_VIEW_
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_VIEW_TYPE;
import static org.thingsboard.server.controller.ControllerConstants.MODEL_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.NAME_CONFLICT_POLICY_DESC;
import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_SEPARATOR_DESC;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
@ -87,6 +86,7 @@ import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_D
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_SEPARATOR_DESC;
import static org.thingsboard.server.controller.ControllerConstants.UNIQUIFY_STRATEGY_DESC;
import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
@ -167,17 +167,25 @@ public class EntityViewController extends BaseController {
tbEntityViewService.delete(entityView, getCurrentUser());
}
@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)
@Hidden
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/tenant/entityViews", params = {"entityViewName"})
public EntityView getTenantEntityView(
@Parameter(description = "Entity View name")
@RequestParam String entityViewName) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(entityViewService.findEntityViewByTenantIdAndName(tenantId, entityViewName));
}
@ApiOperation(value = "Get Entity View by name (getTenantEntityViewByName)",
notes = "Fetch the Entity View object based on the tenant id and entity view name. " + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/tenant/entityView")
public EntityView getTenantEntityViewByName(
@Parameter(description = "Entity View name")
@RequestParam String entityViewName) throws ThingsboardException {
return getTenantEntityView(entityViewName);
}
@ApiOperation(value = "Assign Entity View to customer (assignEntityViewToCustomer)",
notes = "Creates assignment of the Entity View to customer. Customer will be able to query Entity View afterwards." + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@ -222,7 +230,7 @@ public class EntityViewController extends BaseController {
notes = "Returns a page of Entity View objects assigned to customer. " +
PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/customer/{customerId}/entityViews", params = {"pageSize", "page"})
@GetMapping(value = "/customer/{customerId}/entityViews")
public PageData<EntityView> getCustomerEntityViews(
@Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(CUSTOMER_ID) String strCustomerId,
@ -254,7 +262,7 @@ public class EntityViewController extends BaseController {
notes = "Returns a page of Entity View info objects assigned to customer. " + ENTITY_VIEW_DESCRIPTION +
PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/customer/{customerId}/entityViewInfos", params = {"pageSize", "page"})
@GetMapping(value = "/customer/{customerId}/entityViewInfos")
public PageData<EntityViewInfo> getCustomerEntityViewInfos(
@Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(CUSTOMER_ID) String strCustomerId,
@ -286,7 +294,7 @@ public class EntityViewController extends BaseController {
notes = "Returns a page of entity views owned by tenant. " + ENTITY_VIEW_DESCRIPTION +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/tenant/entityViews", params = {"pageSize", "page"})
@GetMapping(value = "/tenant/entityViews")
public PageData<EntityView> getTenantEntityViews(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -314,7 +322,7 @@ public class EntityViewController extends BaseController {
notes = "Returns a page of entity views info owned by tenant. " + ENTITY_VIEW_DESCRIPTION +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/tenant/entityViewInfos", params = {"pageSize", "page"})
@GetMapping(value = "/tenant/entityViewInfos")
public PageData<EntityViewInfo> getTenantEntityViewInfos(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -337,13 +345,13 @@ public class EntityViewController extends BaseController {
}
}
@ApiOperation(value = "Find related entity views (findByQuery)",
@ApiOperation(value = "Find related entity views (findEntityViewsByQuery)",
notes = "Returns all entity views that are related to the specific entity. " +
"The entity id, relation type, entity view types, depth of the search, and other query parameters defined using complex 'EntityViewSearchQuery' object. " +
"See 'Model' tab of the Parameters for more info." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@PostMapping(value = "/entityViews")
public List<EntityView> findByQuery(
public List<EntityView> findEntityViewsByQuery(
@Parameter(description = "The entity view search query JSON")
@RequestBody EntityViewSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException {
checkNotNull(query);
@ -429,7 +437,7 @@ public class EntityViewController extends BaseController {
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/edge/{edgeId}/entityViews", params = {"pageSize", "page"})
@GetMapping(value = "/edge/{edgeId}/entityViews")
public PageData<EntityView> getEdgeEntityViews(
@PathVariable(EDGE_ID) String strEdgeId,
@RequestParam int pageSize,
@ -459,11 +467,10 @@ public class EntityViewController extends BaseController {
return checkNotNull(filteredResult);
}
@ApiOperation(value = "Get Entity Views By Ids (getEntityViewsByIds)",
notes = "Requested entity views must be owned by tenant or assigned to customer which user is performing the request. ")
@Hidden
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/entityViews", params = {"entityViewIds"})
public List<EntityView> getEntityViewsByIds(@Parameter(description = "A list of entity view ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
public List<EntityView> getEntityViewsByIdsV1(@Parameter(description = "A list of entity view ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("entityViewIds") Set<UUID> entityViewUUIDs) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
List<EntityViewId> entityViewIds = new ArrayList<>();
@ -474,6 +481,15 @@ public class EntityViewController extends BaseController {
return filterEntityViewsByReadPermission(entityViews);
}
@ApiOperation(value = "Get Entity Views By Ids (getEntityViewsByIds)",
notes = "Requested entity views must be owned by tenant or assigned to customer which user is performing the request. ")
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/entityViews/list")
public List<EntityView> getEntityViewsByIds(@Parameter(description = "A list of entity view ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("entityViewIds") Set<UUID> entityViewUUIDs) throws ThingsboardException {
return getEntityViewsByIdsV1(entityViewUUIDs);
}
private List<EntityView> filterEntityViewsByReadPermission(List<EntityView> entityViews) {
return entityViews.stream().filter(entityView -> {
try {

19
application/src/main/java/org/thingsboard/server/controller/EventController.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.controller;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.beans.factory.annotation.Autowired;
@ -113,13 +114,13 @@ public class EventController extends BaseController {
@Autowired
private EventService eventService;
@ApiOperation(value = "Get Events by type (getEvents)",
@ApiOperation(value = "Get Events by type (getEventsByType)",
notes = "Returns a page of events for specified entity by specifying event type. " +
PAGE_DATA_PARAMETERS)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}/{eventType}", method = RequestMethod.GET)
@ResponseBody
public PageData<EventInfo> getEvents(
public PageData<EventInfo> getEventsByType(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_TYPE) String strEntityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@ -152,16 +153,12 @@ public class EventController extends BaseController {
return checkNotNull(eventService.findEvents(tenantId, entityId, resolveEventType(eventType), pageLink));
}
@ApiOperation(value = "Get Events (Deprecated)",
notes = "Returns a page of events for specified entity. Deprecated and will be removed in next minor release. " +
"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)
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.GET)
@ResponseBody
public PageData<EventInfo> getEvents(
public PageData<EventInfo> getEventsDeprecated(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_TYPE) String strEntityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@ -194,14 +191,14 @@ public class EventController extends BaseController {
return checkNotNull(eventService.findEvents(tenantId, entityId, EventType.LC_EVENT, pageLink));
}
@ApiOperation(value = "Get Events by event filter (getEvents)",
@ApiOperation(value = "Get Events by event filter (getEventsByFilter)",
notes = "Returns a page of events for the chosen entity by specifying the event filter. " +
PAGE_DATA_PARAMETERS + NEW_LINE +
EVENT_FILTER_DEFINITION)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.POST)
@ResponseBody
public PageData<EventInfo> getEvents(
public PageData<EventInfo> getEventsByFilter(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_TYPE) String strEntityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)

4
application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java

@ -69,11 +69,11 @@ public class Lwm2mController extends BaseController {
return lwM2MService.getServerSecurityInfo(bootstrapServer);
}
@ApiOperation(hidden = true, value = "Save device with credentials (Deprecated)")
@ApiOperation(hidden = true, value = "Save LwM2M device with credentials (saveLwm2mDeviceWithCredentials) (Deprecated)")
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/lwm2m/device-credentials", method = RequestMethod.POST)
@ResponseBody
public Device saveDeviceWithCredentials(@RequestBody Map<Class<?>, Object> deviceWithDeviceCredentials) throws ThingsboardException {
public Device saveLwm2mDeviceWithCredentials(@RequestBody Map<Class<?>, Object> deviceWithDeviceCredentials) throws ThingsboardException {
Device device = checkNotNull(JacksonUtil.convertValue(deviceWithDeviceCredentials.get(Device.class), Device.class));
DeviceCredentials credentials = checkNotNull(JacksonUtil.convertValue(deviceWithDeviceCredentials.get(DeviceCredentials.class), DeviceCredentials.class));
return deviceController.saveDeviceWithCredentials(new SaveDeviceWithCredentialsRequest(device, credentials), DEFAULT.policy(), DEFAULT.separator(), DEFAULT.uniquifyStrategy());

4
application/src/main/java/org/thingsboard/server/controller/MailConfigTemplateController.java

@ -43,12 +43,12 @@ public class MailConfigTemplateController extends BaseController {
private static final String MAIL_CONFIG_TEMPLATE_DEFINITION = "Mail configuration template is set of default smtp settings for mail server that specific provider supports";
private final TbMailConfigTemplateService mailConfigTemplateService;
@ApiOperation(value = "Get the list of all OAuth2 client registration templates (getClientRegistrationTemplates)" + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH,
@ApiOperation(value = "Get the list of all OAuth2 client registration templates (getMailConfigTemplates)" + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH,
notes = MAIL_CONFIG_TEMPLATE_DEFINITION)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public JsonNode getClientRegistrationTemplates() throws ThingsboardException, IOException {
public JsonNode getMailConfigTemplates() throws ThingsboardException, IOException {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
return mailConfigTemplateService.findAllMailConfigTemplates();
}

6
application/src/main/java/org/thingsboard/server/controller/MobileAppBundleController.java

@ -81,12 +81,12 @@ public class MobileAppBundleController extends BaseController {
return tbMobileAppBundleService.save(mobileAppBundle, getOAuth2ClientIds(ids), getCurrentUser());
}
@ApiOperation(value = "Update oauth2 clients (updateOauth2Clients)",
@ApiOperation(value = "Update oauth2 clients (updateMobileAppBundleOauth2Clients)",
notes = "Update oauth2 clients of the specified mobile app bundle." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@PutMapping(value = "/mobile/bundle/{id}/oauth2Clients")
public void updateOauth2Clients(@PathVariable UUID id,
@RequestBody UUID[] clientIds) throws ThingsboardException {
public void updateMobileAppBundleOauth2Clients(@PathVariable UUID id,
@RequestBody UUID[] clientIds) throws ThingsboardException {
MobileAppBundleId mobileAppBundleId = new MobileAppBundleId(id);
MobileAppBundle mobileAppBundle = checkMobileAppBundleId(mobileAppBundleId, Operation.WRITE);
List<OAuth2ClientId> oAuth2ClientIds = getOAuth2ClientIds(clientIds);

4
application/src/main/java/org/thingsboard/server/controller/MobileAppController.java

@ -123,7 +123,7 @@ public class MobileAppController extends BaseController {
return tbMobileAppService.save(mobileApp, getCurrentUser());
}
@ApiOperation(value = "Get mobile app infos (getTenantMobileAppInfos)", notes = SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@ApiOperation(value = "Get mobile app infos (getTenantMobileApps)", notes = SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/mobile/app")
public PageData<MobileApp> getTenantMobileApps(@Parameter(description = "Platform type: ANDROID or IOS")
@ -142,7 +142,7 @@ public class MobileAppController extends BaseController {
return mobileAppService.findMobileAppsByTenantId(getTenantId(), platformType, pageLink);
}
@ApiOperation(value = "Get mobile info by id (getMobileAppInfoById)", notes = SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@ApiOperation(value = "Get mobile info by id (getMobileAppById)", notes = SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/mobile/app/{id}")
public MobileApp getMobileAppById(@PathVariable UUID id) throws ThingsboardException {

41
application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.controller;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -153,12 +154,10 @@ public class NotificationTargetController extends BaseController {
return notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), (PlatformUsersNotificationTargetConfig) notificationTarget.getConfiguration(), pageLink);
}
@ApiOperation(value = "Get notification targets by ids (getNotificationTargetsByIds)",
notes = "Returns the list of notification targets found by provided ids." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@Hidden
@GetMapping(value = "/targets", params = {"ids"})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public List<NotificationTarget> getNotificationTargetsByIds(@Parameter(description = "Comma-separated list of uuids representing targets ids", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
public List<NotificationTarget> getNotificationTargetsByIdsV1(@Parameter(description = "Comma-separated list of uuids representing targets ids", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("ids") UUID[] ids,
@AuthenticationPrincipal SecurityUser user) {
// PE: generic permission
@ -166,6 +165,17 @@ public class NotificationTargetController extends BaseController {
return notificationTargetService.findNotificationTargetsByTenantIdAndIds(user.getTenantId(), targetsIds);
}
@ApiOperation(value = "Get notification targets by ids (getNotificationTargetsByIds)",
notes = "Returns the list of notification targets found by provided ids." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/targets/list")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public List<NotificationTarget> getNotificationTargetsByIds(@Parameter(description = "Comma-separated list of uuids representing targets ids", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("ids") UUID[] ids,
@AuthenticationPrincipal SecurityUser user) {
return getNotificationTargetsByIdsV1(ids, user);
}
@ApiOperation(value = "Get notification targets (getNotificationTargets)",
notes = "Returns the page of notification targets owned by sysadmin or tenant." + NEW_LINE +
PAGE_DATA_PARAMETERS +
@ -188,13 +198,10 @@ public class NotificationTargetController extends BaseController {
return notificationTargetService.findNotificationTargetsByTenantId(user.getTenantId(), pageLink);
}
@ApiOperation(value = "Get notification targets by supported notification type (getNotificationTargetsBySupportedNotificationType)",
notes = "Returns the page of notification targets filtered by notification type that they can be used for." + NEW_LINE +
PAGE_DATA_PARAMETERS +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@Hidden
@GetMapping(value = "/targets", params = "notificationType")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public PageData<NotificationTarget> getNotificationTargetsBySupportedNotificationType(@RequestParam int pageSize,
public PageData<NotificationTarget> getNotificationTargetsBySupportedNotificationTypeV1(@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@ -206,6 +213,22 @@ public class NotificationTargetController extends BaseController {
return notificationTargetService.findNotificationTargetsByTenantIdAndSupportedNotificationType(user.getTenantId(), notificationType, pageLink);
}
@ApiOperation(value = "Get notification targets by supported notification type (getNotificationTargetsBySupportedNotificationType)",
notes = "Returns the page of notification targets filtered by notification type that they can be used for." + NEW_LINE +
PAGE_DATA_PARAMETERS +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/targets/notificationType/{notificationType}")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public PageData<NotificationTarget> getNotificationTargetsBySupportedNotificationType(@PathVariable NotificationType notificationType,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
return getNotificationTargetsBySupportedNotificationTypeV1(pageSize, page, textSearch, sortProperty, sortOrder, notificationType, user);
}
@ApiOperation(value = "Delete notification target by id (deleteNotificationTargetById)",
notes = "Deletes notification target by its id." + NEW_LINE +
"This target cannot be referenced by existing scheduled notification requests or any notification rules." +

4
application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java

@ -71,12 +71,12 @@ public class OAuth2ConfigTemplateController extends BaseController {
oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(clientRegistrationTemplateId);
}
@ApiOperation(value = "Get the list of all OAuth2 client registration templates (getClientRegistrationTemplates)" + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH,
@ApiOperation(value = "Get the list of all OAuth2 client registration templates (getOAuth2ClientRegistrationTemplates)" + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH,
notes = OAUTH2_CLIENT_REGISTRATION_TEMPLATE_DEFINITION)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public List<OAuth2ClientRegistrationTemplate> getClientRegistrationTemplates() throws ThingsboardException {
public List<OAuth2ClientRegistrationTemplate> getOAuth2ClientRegistrationTemplates() throws ThingsboardException {
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.READ);
return oAuth2ConfigTemplateService.findAllClientRegistrationTemplates();
}

15
application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.controller;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -132,15 +133,23 @@ public class OAuth2Controller extends BaseController {
return oAuth2ClientService.findOAuth2ClientInfosByTenantId(getTenantId(), pageLink);
}
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/oauth2/client/infos", params = {"clientIds"})
public List<OAuth2ClientInfo> findTenantOAuth2ClientInfosByIdsV1(
@RequestParam("clientIds") UUID[] clientIds) throws ThingsboardException {
List<OAuth2ClientId> oAuth2ClientIds = getOAuth2ClientIds(clientIds);
return oAuth2ClientService.findOAuth2ClientInfosByIds(getTenantId(), oAuth2ClientIds);
}
@ApiOperation(value = "Get OAuth2 Client infos By Ids (findTenantOAuth2ClientInfosByIds)",
notes = "Fetch OAuth2 Client info objects based on the provided ids. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/oauth2/client/infos", params = {"clientIds"})
@GetMapping(value = "/oauth2/client/list")
public List<OAuth2ClientInfo> findTenantOAuth2ClientInfosByIds(
@Parameter(description = "A list of oauth2 ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("clientIds") UUID[] clientIds) throws ThingsboardException {
List<OAuth2ClientId> oAuth2ClientIds = getOAuth2ClientIds(clientIds);
return oAuth2ClientService.findOAuth2ClientInfosByIds(getTenantId(), oAuth2ClientIds);
return findTenantOAuth2ClientInfosByIdsV1(clientIds);
}
@ApiOperation(value = "Get OAuth2 Client by id (getOAuth2ClientById)", notes = SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)

30
application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java

@ -181,25 +181,25 @@ public class OtaPackageController extends BaseController {
return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantId(getTenantId(), pageLink));
}
@ApiOperation(value = "Get OTA Package Infos (getOtaPackages)",
@ApiOperation(value = "Get OTA Package Infos by device profile and type (getOtaPackagesByDeviceProfileAndType)",
notes = "Returns a page of OTA Package Info objects owned by tenant. " +
PAGE_DATA_PARAMETERS + OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/otaPackages/{deviceProfileId}/{type}")
public PageData<OtaPackageInfo> getOtaPackages(@Parameter(description = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
@PathVariable("deviceProfileId") String strDeviceProfileId,
@Parameter(description = "OTA Package type.", schema = @Schema(allowableValues = {"FIRMWARE", "SOFTWARE"}))
@PathVariable("type") String strType,
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@Parameter(description = OTA_PACKAGE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
@Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "type", "title", "version", "tag", "url", "fileName", "dataSize", "checksum"}))
@RequestParam(required = false) String sortProperty,
@Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
public PageData<OtaPackageInfo> getOtaPackagesByDeviceProfileAndType(@Parameter(description = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
@PathVariable("deviceProfileId") String strDeviceProfileId,
@Parameter(description = "OTA Package type.", schema = @Schema(allowableValues = {"FIRMWARE", "SOFTWARE"}))
@PathVariable("type") String strType,
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@Parameter(description = OTA_PACKAGE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
@Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "type", "title", "version", "tag", "url", "fileName", "dataSize", "checksum"}))
@RequestParam(required = false) String sortProperty,
@Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("deviceProfileId", strDeviceProfileId);
checkParameter("type", strType);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);

2
application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java

@ -129,7 +129,7 @@ public class QrCodeSettingsController extends BaseController {
return qrCodeSettingService.saveQrCodeSettings(currentUser.getTenantId(), qrCodeSettings);
}
@ApiOperation(value = "Get Mobile application settings (getMobileAppSettings)",
@ApiOperation(value = "Get Mobile application settings (getQrCodeSettings)",
notes = "The response payload contains configuration for android/iOS applications and platform qr code widget settings." + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/api/mobile/qr/settings")

8
application/src/main/java/org/thingsboard/server/controller/QueueController.java

@ -19,7 +19,9 @@ import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@ -65,8 +67,7 @@ public class QueueController extends BaseController {
notes = "Returns a page of queues registered in the platform. " +
PAGE_DATA_PARAMETERS + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/queues", params = {"serviceType", "pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@GetMapping(value = "/queues")
public PageData<Queue> getTenantQueuesByServiceType(@Parameter(description = QUEUE_SERVICE_TYPE_DESCRIPTION, schema = @Schema(allowableValues = {"TB-RULE-ENGINE", "TB-CORE", "TB-TRANSPORT", "JS-EXECUTOR"}, requiredMode = Schema.RequiredMode.REQUIRED))
@RequestParam String serviceType,
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@ -122,8 +123,7 @@ public class QueueController extends BaseController {
"Remove 'id', 'tenantId' from the request body example (below) to create new Queue entity. " +
SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/queues", params = {"serviceType"}, method = RequestMethod.POST)
@ResponseBody
@PostMapping(value = "/queues")
public Queue saveQueue(@Parameter(description = "A JSON value representing the queue.")
@RequestBody Queue queue,
@Parameter(description = QUEUE_SERVICE_TYPE_DESCRIPTION, schema = @Schema(allowableValues = {"TB-RULE-ENGINE", "TB-CORE", "TB-TRANSPORT", "JS-EXECUTOR"}, requiredMode = Schema.RequiredMode.REQUIRED))

17
application/src/main/java/org/thingsboard/server/controller/QueueStatsController.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.controller;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -85,12 +86,10 @@ public class QueueStatsController extends BaseController {
return checkNotNull(queueStatsService.findQueueStatsById(getTenantId(), queueStatsId));
}
@ApiOperation(value = "Get QueueStats By Ids (getQueueStatsByIds)",
notes = "Fetch the Queue stats objects based on the provided ids. ")
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/queueStats", params = {"queueStatsIds"})
public List<QueueStats> getQueueStatsByIds(
@Parameter(description = "A list of queue stats ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
public List<QueueStats> getQueueStatsByIdsV1(
@RequestParam("queueStatsIds") String[] strQueueStatsIds) throws ThingsboardException {
checkArrayParameter("queueStatsIds", strQueueStatsIds);
List<QueueStatsId> queueStatsIds = new ArrayList<>();
@ -99,4 +98,14 @@ public class QueueStatsController extends BaseController {
}
return queueStatsService.findQueueStatsByIds(getTenantId(), queueStatsIds);
}
@ApiOperation(value = "Get QueueStats By Ids (getQueueStatsByIds)",
notes = "Fetch the Queue stats objects based on the provided ids. ")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/queueStats/list")
public List<QueueStats> getQueueStatsByIds(
@Parameter(description = "A list of queue stats ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("queueStatsIds") String[] strQueueStatsIds) throws ThingsboardException {
return getQueueStatsByIdsV1(strQueueStatsIds);
}
}

8
application/src/main/java/org/thingsboard/server/controller/RpcV1Controller.java

@ -43,11 +43,11 @@ import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CU
@Slf4j
public class RpcV1Controller extends AbstractRpcController {
@ApiOperation(value = "Send one-way RPC request (handleOneWayDeviceRPCRequest)", notes = "Deprecated. See 'Rpc V 2 Controller' instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@ApiOperation(value = "Send one-way RPC request (handleOneWayDeviceRPCRequestV1)", notes = "Deprecated. See 'Rpc V 2 Controller' instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(
public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequestV1(
@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable("deviceId") String deviceIdStr,
@Parameter(description = "A JSON value representing the RPC request.")
@ -55,11 +55,11 @@ public class RpcV1Controller extends AbstractRpcController {
return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.REQUEST_TIMEOUT, HttpStatus.CONFLICT);
}
@ApiOperation(value = "Send two-way RPC request (handleTwoWayDeviceRPCRequest)", notes = "Deprecated. See 'Rpc V 2 Controller' instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@ApiOperation(value = "Send two-way RPC request (handleTwoWayDeviceRPCRequestV1)", notes = "Deprecated. See 'Rpc V 2 Controller' instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(
public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequestV1(
@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable("deviceId") String deviceIdStr,
@Parameter(description = "A JSON value representing the RPC request.")

8
application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java

@ -113,7 +113,7 @@ public class RpcV2Controller extends AbstractRpcController {
private static final String TWO_WAY_RPC_REQUEST_DESCRIPTION = "Sends the two-way remote-procedure call (RPC) request to device. " + RPC_REQUEST_DESCRIPTION + TWO_WAY_RPC_RESULT + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH;
@ApiOperation(value = "Send one-way RPC request", notes = ONE_WAY_RPC_REQUEST_DESCRIPTION)
@ApiOperation(value = "Send one-way RPC request (handleOneWayDeviceRPCRequestV2)", notes = ONE_WAY_RPC_REQUEST_DESCRIPTION)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Persistent RPC request was saved to the database or lightweight RPC request was sent to the device."),
@ApiResponse(responseCode = "400", description = "Invalid structure of the request."),
@ -124,7 +124,7 @@ public class RpcV2Controller extends AbstractRpcController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(
public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequestV2(
@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable("deviceId") String deviceIdStr,
@Parameter(description = "A JSON value representing the RPC request.")
@ -132,7 +132,7 @@ public class RpcV2Controller extends AbstractRpcController {
return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.GATEWAY_TIMEOUT, HttpStatus.GATEWAY_TIMEOUT);
}
@ApiOperation(value = "Send two-way RPC request", notes = TWO_WAY_RPC_REQUEST_DESCRIPTION)
@ApiOperation(value = "Send two-way RPC request (handleTwoWayDeviceRPCRequestV2)", notes = TWO_WAY_RPC_REQUEST_DESCRIPTION)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Persistent RPC request was saved to the database or lightweight RPC response received."),
@ApiResponse(responseCode = "400", description = "Invalid structure of the request."),
@ -143,7 +143,7 @@ public class RpcV2Controller extends AbstractRpcController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(
public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequestV2(
@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String deviceIdStr,
@Parameter(description = "A JSON value representing the RPC request.")

32
application/src/main/java/org/thingsboard/server/controller/RuleChainController.java

@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -229,12 +230,12 @@ public class RuleChainController extends BaseController {
return tbRuleChainService.save(ruleChain, getCurrentUser());
}
@ApiOperation(value = "Create Default Rule Chain",
@ApiOperation(value = "Create Default Rule Chain (setDeviceDefaultRuleChain)",
notes = "Create rule chain from template, based on the specified name in the request. " +
"Creates the rule chain based on the template that is used to create root rule chain. " + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@PostMapping("/ruleChain/device/default")
public RuleChain saveRuleChain(
public RuleChain setDeviceDefaultRuleChain(
@Parameter(description = "A JSON value representing the request.")
@RequestBody DefaultRuleChainCreateRequest request) throws Exception {
checkNotNull(request);
@ -281,7 +282,7 @@ public class RuleChainController extends BaseController {
@ApiOperation(value = "Get Rule Chains (getRuleChains)",
notes = "Returns a page of Rule Chains owned by tenant. " + RULE_CHAIN_DESCRIPTION + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/ruleChains", params = {"pageSize", "page"})
@GetMapping(value = "/ruleChains")
public PageData<RuleChain> getRuleChains(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -347,7 +348,7 @@ public class RuleChainController extends BaseController {
notes = TEST_SCRIPT_FUNCTION + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@PostMapping("/ruleChain/testScript")
public JsonNode testScript(
public JsonNode testRuleChainScript(
@Parameter(description = "Script language: JS or TBEL")
@RequestParam(required = false) ScriptLanguage scriptLang,
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Test JS request. See API call description above.")
@ -409,7 +410,7 @@ public class RuleChainController extends BaseController {
@ApiOperation(value = "Export Rule Chains", notes = "Exports all tenant rule chains as one JSON." + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/ruleChains/export", params = {"limit"})
@GetMapping(value = "/ruleChains/export")
public RuleChainData exportRuleChains(
@Parameter(description = "A limit of rule chains to export.", required = true)
@RequestParam("limit") int limit) throws ThingsboardException {
@ -505,7 +506,7 @@ public class RuleChainController extends BaseController {
@ApiOperation(value = "Get Edge Rule Chains (getEdgeRuleChains)",
notes = "Returns a page of Rule Chains assigned to the specified edge. " + RULE_CHAIN_DESCRIPTION + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/edge/{edgeId}/ruleChains", params = {"pageSize", "page"})
@GetMapping(value = "/edge/{edgeId}/ruleChains")
public PageData<RuleChain> getEdgeRuleChains(
@Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(EDGE_ID) String strEdgeId,
@ -582,14 +583,10 @@ public class RuleChainController extends BaseController {
return checkNotNull(result);
}
@ApiOperation(value = "Get Rule Chains By Ids (getRuleChainsByIds)",
notes = "Requested rule chains must be owned by tenant which is performing the request. " +
NEW_LINE)
@Hidden
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/ruleChains", params = {"ruleChainIds"})
public List<RuleChain> getRuleChainsByIds(
@Parameter(description = "A list of rule chain ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("ruleChainIds") Set<UUID> ruleChainUUIDs) throws Exception {
public List<RuleChain> getRuleChainsByIdsV1(@RequestParam("ruleChainIds") Set<UUID> ruleChainUUIDs) throws Exception {
TenantId tenantId = getCurrentUser().getTenantId();
List<RuleChainId> ruleChainIds = new ArrayList<>();
for (UUID ruleChainUUID : ruleChainUUIDs) {
@ -598,4 +595,15 @@ public class RuleChainController extends BaseController {
return ruleChainService.findRuleChainsByIds(tenantId, ruleChainIds);
}
@ApiOperation(value = "Get Rule Chains By Ids (getRuleChainsByIds)",
notes = "Requested rule chains must be owned by tenant which is performing the request. " +
NEW_LINE)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/ruleChains/list")
public List<RuleChain> getRuleChainsByIds(
@Parameter(description = "A list of rule chain ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("ruleChainIds") Set<UUID> ruleChainUUIDs) throws Exception {
return getRuleChainsByIdsV1(ruleChainUUIDs);
}
}

26
application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java

@ -77,7 +77,7 @@ public class RuleEngineController extends BaseController {
@Autowired
private AccessValidator accessValidator;
@ApiOperation(value = "Push user message to the rule engine (handleRuleEngineRequest)",
@ApiOperation(value = "Push user message to the rule engine (handleRuleEngineRequestForUser)",
notes = MSG_DESCRIPTION_PREFIX +
"Uses current User Id ( the one which credentials is used to perform the request) as the Rule Engine message originator. " +
MSG_DESCRIPTION +
@ -86,13 +86,13 @@ public class RuleEngineController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/", method = RequestMethod.POST)
@ResponseBody
public DeferredResult<ResponseEntity> handleRuleEngineRequest(
public DeferredResult<ResponseEntity> handleRuleEngineRequestForUser(
@Parameter(description = "A JSON value representing the message.", required = true)
@RequestBody String requestBody) throws ThingsboardException {
return handleRuleEngineRequest(null, null, null, defaultResponseTimeout, requestBody);
return handleRuleEngineRequestForEntityWithQueueAndTimeout(null, null, null, defaultResponseTimeout, requestBody);
}
@ApiOperation(value = "Push entity message to the rule engine (handleRuleEngineRequest)",
@ApiOperation(value = "Push entity message to the rule engine (handleRuleEngineRequestForEntity)",
notes = MSG_DESCRIPTION_PREFIX +
"Uses specified Entity Id as the Rule Engine message originator. " +
MSG_DESCRIPTION +
@ -101,17 +101,17 @@ public class RuleEngineController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult<ResponseEntity> handleRuleEngineRequest(
public DeferredResult<ResponseEntity> handleRuleEngineRequestForEntity(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("entityId") String entityIdStr,
@Parameter(description = "A JSON value representing the message.", required = true)
@RequestBody String requestBody) throws ThingsboardException {
return handleRuleEngineRequest(entityType, entityIdStr, null, defaultResponseTimeout, requestBody);
return handleRuleEngineRequestForEntityWithQueueAndTimeout(entityType, entityIdStr, null, defaultResponseTimeout, requestBody);
}
@ApiOperation(value = "Push entity message with timeout to the rule engine (handleRuleEngineRequest)",
@ApiOperation(value = "Push entity message with timeout to the rule engine (handleRuleEngineRequestForEntityWithTimeout)",
notes = MSG_DESCRIPTION_PREFIX +
"Uses specified Entity Id as the Rule Engine message originator. " +
MSG_DESCRIPTION +
@ -120,7 +120,7 @@ public class RuleEngineController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/{timeout}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult<ResponseEntity> handleRuleEngineRequest(
public DeferredResult<ResponseEntity> handleRuleEngineRequestForEntityWithTimeout(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@ -129,10 +129,10 @@ public class RuleEngineController extends BaseController {
@PathVariable("timeout") int timeout,
@Parameter(description = "A JSON value representing the message.", required = true)
@RequestBody String requestBody) throws ThingsboardException {
return handleRuleEngineRequest(entityType, entityIdStr, null, timeout, requestBody);
return handleRuleEngineRequestForEntityWithQueueAndTimeout(entityType, entityIdStr, null, timeout, requestBody);
}
@ApiOperation(value = "Push entity message with timeout and specified queue to the rule engine (handleRuleEngineRequest)",
@ApiOperation(value = "Push entity message with timeout and specified queue to the rule engine (handleRuleEngineRequestForEntityWithQueueAndTimeout)",
notes = MSG_DESCRIPTION_PREFIX +
"Uses specified Entity Id as the Rule Engine message originator. " +
MSG_DESCRIPTION +
@ -142,7 +142,7 @@ public class RuleEngineController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/{queueName}/{timeout}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult<ResponseEntity> handleRuleEngineRequest(
public DeferredResult<ResponseEntity> handleRuleEngineRequestForEntityWithQueueAndTimeout(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@ -244,5 +244,7 @@ public class RuleEngineController extends BaseController {
response != null ? response.getData() : "");
}
private record LocalRequestMetaData(TbMsg request, SecurityUser user, DeferredResult<ResponseEntity> responseWriter) {}
private record LocalRequestMetaData(TbMsg request, SecurityUser user,
DeferredResult<ResponseEntity> responseWriter) {
}
}

19
application/src/main/java/org/thingsboard/server/controller/TbResourceController.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.controller;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -118,7 +119,7 @@ public class TbResourceController extends BaseController {
.body(resource);
}
@ApiOperation(value = "Download resource (downloadResource)",
@ApiOperation(value = "Download resource (downloadResourceIfChanged)",
notes = "Download resource with a given type and key for the given scope" + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/resource/{resourceType}/{scope}/{key}")
@ -336,11 +337,10 @@ public class TbResourceController extends BaseController {
}
}
@ApiOperation(value = "Get Resource Infos by ids (getSystemOrTenantResourcesByIds)")
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/resource", params = {"resourceIds"})
public List<TbResourceInfo> getSystemOrTenantResourcesByIds(
@Parameter(description = "A list of resource ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")))
public List<TbResourceInfo> getSystemOrTenantResourcesByIdsV1(
@RequestParam("resourceIds") Set<UUID> resourceUuids) throws ThingsboardException {
SecurityUser user = getCurrentUser();
List<TbResourceId> resourceIds = new ArrayList<>();
@ -350,7 +350,16 @@ public class TbResourceController extends BaseController {
return resourceService.findSystemOrTenantResourcesByIds(user.getTenantId(), resourceIds);
}
@ApiOperation(value = "Get All Resource Infos (getAllResources)",
@ApiOperation(value = "Get Resource Infos by ids (getSystemOrTenantResourcesByIds)")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/resource/list")
public List<TbResourceInfo> getSystemOrTenantResourcesByIds(
@Parameter(description = "A list of resource ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")))
@RequestParam("resourceIds") Set<UUID> resourceUuids) throws ThingsboardException {
return getSystemOrTenantResourcesByIdsV1(resourceUuids);
}
@ApiOperation(value = "Get All Resource Infos (getTenantResources)",
notes = "Returns a page of Resource Info objects owned by tenant. " +
PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")

92
application/src/main/java/org/thingsboard/server/controller/TelemetryController.java

@ -24,7 +24,12 @@ import com.google.common.util.concurrent.MoreExecutors;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ -166,7 +171,8 @@ 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)
+ "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
responses = @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(type = "string")))))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/{entityType}/{entityId}/keys/attributes")
public DeferredResult<ResponseEntity> getAttributeKeys(
@ -180,7 +186,8 @@ 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)
+ "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
responses = @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(type = "string")))))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/{entityType}/{entityId}/keys/attributes/{scope}")
public DeferredResult<ResponseEntity> getAttributeKeysByScope(
@ -197,13 +204,18 @@ 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)
+ "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
responses = @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = AttributeData.class)))))
@Parameters({
@Parameter(name = "key", description = "Repeatable key query parameter (alternative to comma-separated 'keys')", in = ParameterIn.QUERY, required = false, array = @ArraySchema(schema = @Schema(type = "string")))
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/{entityType}/{entityId}/values/attributes")
public DeferredResult<ResponseEntity> getAttributes(
@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_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
@Parameter(hidden = true)
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
List<String> keys = getKeys(keysStr, params);
SecurityUser user = getCurrentUser();
@ -220,7 +232,11 @@ 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)
+ "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
responses = @ApiResponse(content = @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = AttributeData.class)))))
@Parameters({
@Parameter(name = "key", description = "Repeatable key query parameter (alternative to comma-separated 'keys')", in = ParameterIn.QUERY, required = false, array = @ArraySchema(schema = @Schema(type = "string")))
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/{entityType}/{entityId}/values/attributes/{scope}")
public DeferredResult<ResponseEntity> getAttributesByScope(
@ -228,6 +244,7 @@ public class TelemetryController extends BaseController {
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope") AttributeScope scope,
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
@Parameter(hidden = true)
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
List<String> keys = getKeys(keysStr, params);
SecurityUser user = getCurrentUser();
@ -237,7 +254,8 @@ 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)
"\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
responses = @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(type = "string")))))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/{entityType}/{entityId}/keys/timeseries")
public DeferredResult<ResponseEntity> getTimeseriesKeys(
@ -258,7 +276,11 @@ 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)
+ "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
responses = @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(type = "object", additionalPropertiesSchema = TsData[].class))))
@Parameters({
@Parameter(name = "key", description = "Repeatable key query parameter (alternative to comma-separated 'keys')", in = ParameterIn.QUERY, required = false, array = @ArraySchema(schema = @Schema(type = "string")))
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/{entityType}/{entityId}/values/timeseries")
public DeferredResult<ResponseEntity> getLatestTimeseries(
@ -267,6 +289,7 @@ public class TelemetryController extends BaseController {
@Parameter(description = TELEMETRY_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
@Parameter(description = STRICT_DATA_TYPES_DESCRIPTION)
@RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes,
@Parameter(hidden = true)
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
List<String> keys = getKeys(keysStr, params);
SecurityUser user = getCurrentUser();
@ -274,7 +297,32 @@ public class TelemetryController extends BaseController {
(result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keys, useStrictDataTypes));
}
@ApiOperation(value = "Get time series data (getTimeseries)",
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/{entityType}/{entityId}/values/timeseries", params = {"startTs", "endTs"})
public DeferredResult<ResponseEntity> getTimeseries(
@PathVariable("entityType") String entityType,
@PathVariable("entityId") String entityIdStr,
@RequestParam(name = "keys", required = false) String keysStr,
@RequestParam(name = "startTs") Long startTs,
@RequestParam(name = "endTs") Long endTs,
@RequestParam(name = "intervalType", required = false) IntervalType intervalType,
@RequestParam(name = "interval", defaultValue = "0") Long interval,
@RequestParam(name = "timeZone", required = false) String timeZone,
@RequestParam(name = "limit", defaultValue = "100") Integer limit,
@RequestParam(name = "agg", defaultValue = "NONE") String aggStr,
@RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy,
@RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes,
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
List<String> keys = getKeys(keysStr, params);
DeferredResult<ResponseEntity> response = new DeferredResult<>();
Futures.addCallback(tbTelemetryService.getTimeseries(EntityIdFactory.getByTypeAndId(entityType, entityIdStr), keys, startTs, endTs,
intervalType, interval, timeZone, limit, Aggregation.valueOf(aggStr), orderBy, useStrictDataTypes, getCurrentUser()),
getTsKvListCallback(response, useStrictDataTypes), MoreExecutors.directExecutor());
return response;
}
@ApiOperation(value = "Get time series data (getTimeseriesHistory)",
notes = "Returns a range of time series values for specified entity. " +
"Returns not aggregated data by default. " +
"Use aggregation function ('agg') and aggregation interval ('interval') to enable aggregation of the results on the database / server side. " +
@ -282,10 +330,14 @@ 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)
+ "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
responses = @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(type = "object", additionalPropertiesSchema = TsData[].class))))
@Parameters({
@Parameter(name = "key", description = "Repeatable key query parameter (alternative to comma-separated 'keys')", in = ParameterIn.QUERY, required = false, array = @ArraySchema(schema = @Schema(type = "string")))
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/{entityType}/{entityId}/values/timeseries", params = {"startTs", "endTs"})
public DeferredResult<ResponseEntity> getTimeseries(
@GetMapping(value = "/{entityType}/{entityId}/values/timeseries/history")
public DeferredResult<ResponseEntity> getTimeseriesHistory(
@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_KEYS_BASE_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
@ -310,13 +362,9 @@ public class TelemetryController extends BaseController {
@RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy,
@Parameter(description = STRICT_DATA_TYPES_DESCRIPTION)
@RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes,
@Parameter(hidden = true)
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
List<String> keys = getKeys(keysStr, params);
DeferredResult<ResponseEntity> response = new DeferredResult<>();
Futures.addCallback(tbTelemetryService.getTimeseries(EntityIdFactory.getByTypeAndId(entityType, entityIdStr), keys, startTs, endTs,
intervalType, interval, timeZone, limit, Aggregation.valueOf(aggStr), orderBy, useStrictDataTypes, getCurrentUser()),
getTsKvListCallback(response, useStrictDataTypes), MoreExecutors.directExecutor());
return response;
return getTimeseries(entityType, entityIdStr, keysStr, startTs, endTs, intervalType, interval, timeZone, limit, aggStr, orderBy, useStrictDataTypes, params);
}
@ApiOperation(value = "Save device attributes (saveDeviceAttributes)",
@ -449,6 +497,9 @@ public class TelemetryController extends BaseController {
" 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)
@Parameters({
@Parameter(name = "key", description = "Repeatable key query parameter (alternative to comma-separated 'keys')", in = ParameterIn.QUERY, required = false, array = @ArraySchema(schema = @Schema(type = "string")))
})
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Time series for the selected keys in the request was removed. " +
"Platform creates an audit log event about entity time series removal with action type 'TIMESERIES_DELETED'."),
@ -473,6 +524,7 @@ public class TelemetryController extends BaseController {
@RequestParam(name = "deleteLatest", required = false, defaultValue = "true") boolean deleteLatest,
@Parameter(description = "If the parameter is set to true, the latest telemetry will be rewritten in case that current latest value was removed, otherwise, in case that parameter is set to false the new latest value will not set.")
@RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted,
@Parameter(hidden = true)
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
List<String> keys = getKeys(keysStr, params);
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
@ -530,6 +582,9 @@ 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)
@Parameters({
@Parameter(name = "key", description = "Repeatable key query parameter (alternative to comma-separated 'keys')", in = ParameterIn.QUERY, required = false, array = @ArraySchema(schema = @Schema(type = "string")))
})
@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'."),
@ -544,6 +599,7 @@ public class TelemetryController extends BaseController {
@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(DEVICE_ID) String deviceIdStr,
@Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope") AttributeScope scope,
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
@Parameter(hidden = true)
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
List<String> keys = getKeys(keysStr, params);
EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
@ -553,6 +609,9 @@ 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)
@Parameters({
@Parameter(name = "key", description = "Repeatable key query parameter (alternative to comma-separated 'keys')", in = ParameterIn.QUERY, required = false, array = @ArraySchema(schema = @Schema(type = "string")))
})
@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'."),
@ -568,6 +627,7 @@ public class TelemetryController extends BaseController {
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"})) @PathVariable("scope") AttributeScope scope,
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
@Parameter(hidden = true)
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
List<String> keys = getKeys(keysStr, params);
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);

18
application/src/main/java/org/thingsboard/server/controller/TenantController.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.controller;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -22,7 +23,6 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@ -133,7 +133,7 @@ public class TenantController extends BaseController {
@ApiOperation(value = "Get Tenants (getTenants)", notes = "Returns a page of tenants registered in the platform. " + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@GetMapping(value = "/tenants", params = {"pageSize", "page"})
@GetMapping(value = "/tenants")
public PageData<Tenant> getTenants(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -152,7 +152,7 @@ public class TenantController extends BaseController {
@ApiOperation(value = "Get Tenants Info (getTenants)", notes = "Returns a page of tenant info objects registered in the platform. "
+ TENANT_INFO_DESCRIPTION + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@GetMapping(value = "/tenantInfos", params = {"pageSize", "page"})
@GetMapping(value = "/tenantInfos")
public PageData<TenantInfo> getTenantInfos(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -169,9 +169,10 @@ public class TenantController extends BaseController {
return checkNotNull(tenantService.findTenantInfos(pageLink));
}
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/tenants", params = {"tenantIds"})
public List<Tenant> getTenantsByIds(
public List<Tenant> getTenantsByIdsV1(
@Parameter(description = "A list of tenant ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")))
@RequestParam("tenantIds") Set<UUID> tenantUUIDs) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
@ -183,6 +184,15 @@ public class TenantController extends BaseController {
return filterTenantsByReadPermission(tenants);
}
@ApiOperation(value = "Get Tenants list (getTenantsByIds)")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/tenants/list")
public List<Tenant> getTenantsByIds(
@Parameter(description = "A list of tenant ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")))
@RequestParam("tenantIds") Set<UUID> tenantUUIDs) throws ThingsboardException {
return getTenantsByIdsV1(tenantUUIDs);
}
private List<Tenant> filterTenantsByReadPermission(List<Tenant> tenants) {
return tenants.stream().filter(tenant -> {
try {

15
application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.controller;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -242,7 +243,7 @@ public class TenantProfileController extends BaseController {
@ApiOperation(value = "Get Tenant Profiles Info (getTenantProfileInfos)", notes = "Returns a page of tenant profile info objects registered in the platform. "
+ TENANT_PROFILE_INFO_DESCRIPTION + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@GetMapping(value = "/tenantProfileInfos", params = {"pageSize", "page"})
@GetMapping(value = "/tenantProfileInfos")
public PageData<EntityInfo> getTenantProfileInfos(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -258,11 +259,19 @@ public class TenantProfileController extends BaseController {
return checkNotNull(tenantProfileService.findTenantProfileInfos(getTenantId(), pageLink));
}
@Hidden
@GetMapping(value = "/tenantProfiles", params = {"ids"})
@PreAuthorize("hasAuthority('SYS_ADMIN')")
public List<TenantProfile> getTenantProfilesByIds(@Parameter(description = "Comma-separated list of tenant profile ids", array = @ArraySchema(schema = @Schema(type = "string")))
@RequestParam("ids") UUID[] ids) {
public List<TenantProfile> getTenantProfilesByIds(@RequestParam("ids") UUID[] ids) {
return tenantProfileService.findTenantProfilesByIds(TenantId.SYS_TENANT_ID, ids);
}
@ApiOperation(value = "Get Tenant Profile list (getTenantProfileList)")
@GetMapping(value = "/tenantProfiles/list")
@PreAuthorize("hasAuthority('SYS_ADMIN')")
public List<TenantProfile> getTenantProfileList(@Parameter(description = "Comma-separated list of tenant profile ids", array = @ArraySchema(schema = @Schema(type = "string")))
@RequestParam("ids") UUID[] ids) {
return getTenantProfilesByIds(ids);
}
}

9
application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java

@ -16,7 +16,11 @@
package org.thingsboard.server.controller;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import lombok.Data;
import lombok.RequiredArgsConstructor;
@ -187,7 +191,7 @@ public class TwoFactorAuthConfigController extends BaseController {
return twoFaConfigManager.deleteTwoFaAccountConfig(user.getTenantId(), user, providerType);
}
@ApiOperation(value = "Get available 2FA providers (getAvailableTwoFaProviders)", notes =
@ApiOperation(value = "Get available 2FA providers (getAvailableTwoFaProviderTypes)", notes =
"Get the list of provider types available for user to use (the ones configured by tenant or sysadmin).\n" +
"Example of response:\n" +
"```\n[\n \"TOTP\",\n \"EMAIL\",\n \"SMS\"\n]\n```" +
@ -195,7 +199,7 @@ public class TwoFactorAuthConfigController extends BaseController {
)
@GetMapping("/providers")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER', 'MFA_CONFIGURATION_TOKEN')")
public List<TwoFaProviderType> getAvailableTwoFaProviders() throws ThingsboardException {
public List<TwoFaProviderType> getAvailableTwoFaProviderTypes() throws ThingsboardException {
return twoFaConfigManager.getPlatformTwoFaSettings(getTenantId(), true)
.map(PlatformTwoFaSettings::getProviders).orElse(Collections.emptyList()).stream()
.map(TwoFaProviderConfig::getProviderType)
@ -261,6 +265,7 @@ public class TwoFactorAuthConfigController extends BaseController {
}
@Data
@Schema
public static class TwoFaAccountConfigUpdateRequest {
private boolean useByDefault;
}

4
application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthController.java

@ -99,7 +99,7 @@ public class TwoFactorAuthController extends BaseController {
}
}
@ApiOperation(value = "Get available 2FA providers (getAvailableTwoFaProviders)", notes =
@ApiOperation(value = "Get available 2FA providers (getAvailableTwoFaProviderInfos)", notes =
"Get the list of 2FA provider infos available for user to use. Example:\n" +
"```\n[\n" +
" {\n \"type\": \"EMAIL\",\n \"default\": true,\n \"contact\": \"ab*****ko@gmail.com\"\n },\n" +
@ -108,7 +108,7 @@ public class TwoFactorAuthController extends BaseController {
"]\n```")
@GetMapping("/providers")
@PreAuthorize("hasAuthority('PRE_VERIFICATION_TOKEN')")
public List<TwoFaProviderInfo> getAvailableTwoFaProviders() throws ThingsboardException {
public List<TwoFaProviderInfo> getAvailableTwoFaProviderInfos() throws ThingsboardException {
SecurityUser user = getCurrentUser();
Optional<PlatformTwoFaSettings> platformTwoFaSettings = twoFaConfigManager.getPlatformTwoFaSettings(user.getTenantId(), true);
return twoFaConfigManager.getAccountTwoFaSettings(user.getTenantId(), user)

71
application/src/main/java/org/thingsboard/server/controller/UserController.java

@ -16,6 +16,7 @@
package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -273,7 +274,7 @@ public class UserController extends BaseController {
notes = "Returns a page of users owned by tenant or customer. The scope depends on authority of the user that performs the request." +
PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/users", params = {"pageSize", "page"})
@GetMapping(value = "/users")
public PageData<User> getUsers(
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -334,7 +335,7 @@ public class UserController extends BaseController {
@ApiOperation(value = "Get Tenant Users (getTenantAdmins)",
notes = "Returns a page of users owned by tenant. " + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@GetMapping(value = "/tenant/{tenantId}/users", params = {"pageSize", "page"})
@GetMapping(value = "/tenant/{tenantId}/users")
public PageData<User> getTenantAdmins(
@Parameter(description = TENANT_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(TENANT_ID) String strTenantId,
@ -357,7 +358,7 @@ public class UserController extends BaseController {
@ApiOperation(value = "Get Customer Users (getCustomerUsers)",
notes = "Returns a page of users owned by customer. " + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@GetMapping(value = "/customer/{customerId}/users", params = {"pageSize", "page"})
@GetMapping(value = "/customer/{customerId}/users")
public PageData<User> getCustomerUsers(
@Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(CUSTOMER_ID) String strCustomerId,
@ -404,7 +405,7 @@ public class UserController extends BaseController {
"Search is been executed by email, firstName and lastName fields. " +
PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/users/assign/{alarmId}", params = {"pageSize", "page"})
@GetMapping(value = "/users/assign/{alarmId}")
public PageData<UserEmailInfo> getUsersForAssign(
@Parameter(description = ALARM_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("alarmId") String strAlarmId,
@ -456,10 +457,7 @@ public class UserController extends BaseController {
return userSettingsService.saveUserSettings(currentUser.getTenantId(), userSettings).getSettings();
}
@ApiOperation(value = "Update user settings (saveUserSettings)",
notes = "Update user settings for authorized user. Only specified json elements will be updated." +
"Example: you have such settings: {A:5, B:{C:10, D:20}}. Updating it with {B:{C:10, D:30}} will result in" +
"{A:5, B:{C:10, D:30}}. The same could be achieved by putting {B.D:30}")
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@PutMapping(value = "/user/settings")
public void putUserSettings(@RequestBody JsonNode settings) throws ThingsboardException {
@ -467,8 +465,17 @@ public class UserController extends BaseController {
userSettingsService.updateUserSettings(currentUser.getTenantId(), currentUser.getId(), UserSettingsType.GENERAL, settings);
}
@ApiOperation(value = "Get user settings (getUserSettings)",
notes = "Fetch the User settings based on authorized user. ")
@ApiOperation(value = "Update user settings (putGeneralUserSettings)",
notes = "Update user settings for authorized user. Only specified json elements will be updated." +
"Example: you have such settings: {A:5, B:{C:10, D:20}}. Updating it with {B:{C:10, D:30}} will result in" +
"{A:5, B:{C:10, D:30}}. The same could be achieved by putting {B.D:30}")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@PutMapping(value = "/user/settings/general")
public void putGeneralUserSettings(@RequestBody JsonNode settings) throws ThingsboardException {
putUserSettings(settings);
}
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/user/settings")
public JsonNode getUserSettings() throws ThingsboardException {
@ -478,20 +485,28 @@ public class UserController extends BaseController {
return userSettings == null ? JacksonUtil.newObjectNode() : userSettings.getSettings();
}
@ApiOperation(value = "Delete user settings (deleteUserSettings)",
@ApiOperation(value = "Get user settings (getGeneralUserSettings)",
notes = "Fetch the User settings based on authorized user. ")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/user/settings/general")
public JsonNode getGeneralUserSettings() throws ThingsboardException {
return getUserSettings();
}
@ApiOperation(value = "Delete user settings (deleteGeneralUserSettings)",
notes = "Delete user settings by specifying list of json element xpaths. \n " +
"Example: to delete B and C element in { \"A\": {\"B\": 5}, \"C\": 15} send A.B,C in jsonPaths request parameter")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@DeleteMapping(value = "/user/settings/{paths}")
public void deleteUserSettings(@Parameter(description = PATHS)
@PathVariable(PATHS) String paths) throws ThingsboardException {
public void deleteGeneralUserSettings(@Parameter(description = PATHS)
@PathVariable(PATHS) String paths) throws ThingsboardException {
checkParameter(USER_ID, paths);
SecurityUser currentUser = getCurrentUser();
userSettingsService.deleteUserSettings(currentUser.getTenantId(), currentUser.getId(), UserSettingsType.GENERAL, Arrays.asList(paths.split(",")));
}
@ApiOperation(value = "Update user settings (saveUserSettings)",
@ApiOperation(value = "Update user settings (putUserSettings)",
notes = "Update user settings for authorized user. Only specified json elements will be updated." +
"Example: you have such settings: {A:5, B:{C:10, D:20}}. Updating it with {B:{C:10, D:30}} will result in" +
"{A:5, B:{C:10, D:30}}. The same could be achieved by putting {B.D:30}")
@ -518,15 +533,15 @@ public class UserController extends BaseController {
return userSettings == null ? JacksonUtil.newObjectNode() : userSettings.getSettings();
}
@ApiOperation(value = "Delete user settings (deleteUserSettings)",
@ApiOperation(value = "Delete user settings by type (deleteUserSettingsByType)",
notes = "Delete user settings by specifying list of json element xpaths. \n " +
"Example: to delete B and C element in { \"A\": {\"B\": 5}, \"C\": 15} send A.B,C in jsonPaths request parameter")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@DeleteMapping(value = "/user/settings/{type}/{paths}")
public void deleteUserSettings(@Parameter(description = PATHS)
@PathVariable(PATHS) String paths,
@Parameter(description = "Settings type, case insensitive, one of: \"general\", \"quick_links\", \"doc_links\" or \"dashboards\".")
@PathVariable("type") String strType) throws ThingsboardException {
public void deleteUserSettingsByType(@Parameter(description = PATHS)
@PathVariable(PATHS) String paths,
@Parameter(description = "Settings type, case insensitive, one of: \"general\", \"quick_links\", \"doc_links\" or \"dashboards\".")
@PathVariable("type") String strType) throws ThingsboardException {
checkParameter(USER_ID, paths);
UserSettingsType type = checkEnumParameter("Settings type", strType, UserSettingsType::valueOf);
checkNotReserved(strType, type);
@ -534,7 +549,7 @@ public class UserController extends BaseController {
userSettingsService.deleteUserSettings(currentUser.getTenantId(), currentUser.getId(), type, Arrays.asList(paths.split(",")));
}
@ApiOperation(value = "Get information about last visited and starred dashboards (getLastVisitedDashboards)",
@ApiOperation(value = "Get information about last visited and starred dashboards (getUserDashboardsInfo)",
notes = "Fetch the list of last visited and starred dashboards. Both lists are limited to 10 items." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/user/dashboards")
@ -583,12 +598,10 @@ public class UserController extends BaseController {
userService.removeMobileSession(user.getTenantId(), mobileToken);
}
@ApiOperation(value = "Get Users By Ids (getUsersByIds)",
notes = "Requested users must be owned by tenant or assigned to customer which user is performing the request. ")
@Hidden
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/users", params = {"userIds"})
public List<User> getUsersByIds(
@Parameter(description = "A list of user ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
public List<User> getUsersByIdsV1(
@RequestParam("userIds") Set<UUID> userUUIDs) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
List<UserId> userIds = new ArrayList<>();
@ -599,6 +612,16 @@ public class UserController extends BaseController {
return filterUsersByReadPermission(users);
}
@ApiOperation(value = "Get Users By Ids (getUsersByIds)",
notes = "Requested users must be owned by tenant or assigned to customer which user is performing the request. ")
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/users/list")
public List<User> getUsersByIds(
@Parameter(description = "A list of user ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("userIds") Set<UUID> userUUIDs) throws ThingsboardException {
return getUsersByIdsV1(userUUIDs);
}
private List<User> filterUsersByReadPermission(List<User> users) {
return users.stream().filter(user -> {
try {

30
application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.controller;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
@ -209,8 +210,7 @@ public class WidgetTypeController extends AutoCommitController {
}
}
@ApiOperation(value = "Get all Widget types for specified Bundle (getBundleWidgetTypesByBundleAlias) (Deprecated)",
notes = "Returns an array of Widget Type objects that belong to specified Widget Bundle." + WIDGET_TYPE_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/widgetTypes", params = {"isSystem", "bundleAlias"})
@Deprecated
@ -229,19 +229,27 @@ public class WidgetTypeController extends AutoCommitController {
return checkNotNull(widgetTypeService.findWidgetTypesByWidgetsBundleId(getTenantId(), widgetsBundle.getId()));
}
@ApiOperation(value = "Get all Widget types for specified Bundle (getBundleWidgetTypes)",
notes = "Returns an array of Widget Type objects that belong to specified Widget Bundle." + WIDGET_TYPE_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/widgetTypes", params = {"widgetsBundleId"})
public List<WidgetType> getBundleWidgetTypes(
public List<WidgetType> getBundleWidgetTypesV1(
@Parameter(description = "Widget Bundle Id", required = true)
@RequestParam("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
return checkNotNull(widgetTypeService.findWidgetTypesByWidgetsBundleId(getTenantId(), widgetsBundleId));
}
@ApiOperation(value = "Get all Widget types details for specified Bundle (getBundleWidgetTypesDetailsByBundleAlias) (Deprecated)",
notes = "Returns an array of Widget Type Details objects that belong to specified Widget Bundle." + WIDGET_TYPE_DETAILS_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@ApiOperation(value = "Get all Widget types for specified Bundle (getBundleWidgetTypes)",
notes = "Returns an array of Widget Type objects that belong to specified Widget Bundle." + WIDGET_TYPE_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/widgetsBundles/{widgetsBundleId}/widgetTypes")
public List<WidgetType> getBundleWidgetTypes(
@Parameter(description = "Widget Bundle Id", required = true)
@PathVariable("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException {
return getBundleWidgetTypesV1(strWidgetsBundleId);
}
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/widgetTypesDetails", params = {"isSystem", "bundleAlias"})
@Deprecated
@ -284,7 +292,7 @@ public class WidgetTypeController extends AutoCommitController {
@ApiOperation(value = "Get all Widget type fqns for specified Bundle (getBundleWidgetTypeFqns)",
notes = "Returns an array of Widget Type fqns that belong to specified Widget Bundle." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/widgetTypeFqns", params = {"widgetsBundleId"})
@GetMapping(value = "/widgetTypeFqns")
public List<String> getBundleWidgetTypeFqns(
@Parameter(description = "Widget Bundle Id", required = true)
@RequestParam("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException {
@ -292,8 +300,7 @@ public class WidgetTypeController extends AutoCommitController {
return checkNotNull(widgetTypeService.findWidgetFqnsByWidgetsBundleId(getTenantId(), widgetsBundleId));
}
@ApiOperation(value = "Get Widget Type Info objects (getBundleWidgetTypesInfosByBundleAlias) (Deprecated)",
notes = "Get the Widget Type Info objects based on the provided parameters. " + WIDGET_TYPE_INFO_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/widgetTypesInfos", params = {"isSystem", "bundleAlias"})
@Deprecated
@ -344,8 +351,7 @@ public class WidgetTypeController extends AutoCommitController {
widgetTypeDeprecatedFilter, widgetTypes, pageLink));
}
@ApiOperation(value = "Get Widget Type (getWidgetTypeByBundleAliasAndTypeAlias) (Deprecated)",
notes = "Get the Widget Type based on the provided parameters. " + WIDGET_TYPE_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/widgetType", params = {"isSystem", "bundleAlias", "alias"})
@Deprecated

31
application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java

@ -15,13 +15,13 @@
*/
package org.thingsboard.server.controller;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@ -222,11 +222,10 @@ public class WidgetsBundleController extends BaseController {
}
}
@ApiOperation(value = "Get all Widget Bundles (getWidgetsBundles)",
notes = "Returns an array of Widget Bundle objects that are available for current user." + WIDGET_BUNDLE_DESCRIPTION + " " + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/widgetsBundles")
public List<WidgetsBundle> getWidgetsBundles() throws ThingsboardException {
public List<WidgetsBundle> getWidgetsBundlesV1() throws ThingsboardException {
if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) {
return checkNotNull(widgetsBundleService.findSystemWidgetsBundles(getTenantId()));
} else {
@ -235,13 +234,18 @@ public class WidgetsBundleController extends BaseController {
}
}
@ApiOperation(value = "Get Widgets Bundles By Ids (getWidgetsBundlesByIds)",
notes = "Requested widgets bundles must be system level or owned by tenant of the user which is performing the request. " +
NEW_LINE)
@ApiOperation(value = "Get all Widget Bundles (getAllWidgetsBundles)",
notes = "Returns an array of Widget Bundle objects that are available for current user." + WIDGET_BUNDLE_DESCRIPTION + " " + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/widgetsBundles/all")
public List<WidgetsBundle> getAllWidgetsBundles() throws ThingsboardException {
return getWidgetsBundlesV1();
}
@Hidden
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/widgetsBundles", params = {"widgetsBundleIds"})
public List<WidgetsBundle> getWidgetsBundlesByIds(
@Parameter(description = "A list of widgets bundle ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("widgetsBundleIds") Set<UUID> widgetsBundleUUIDs) throws ThingsboardException {
List<WidgetsBundleId> widgetsBundleIds = new ArrayList<>();
for (UUID widgetsBundleUUID : widgetsBundleUUIDs) {
@ -250,4 +254,15 @@ public class WidgetsBundleController extends BaseController {
return widgetsBundleService.findSystemOrTenantWidgetsBundlesByIds(getTenantId(), widgetsBundleIds);
}
@ApiOperation(value = "Get Widgets Bundles By Ids (getWidgetsBundlesList)",
notes = "Requested widgets bundles must be system level or owned by tenant of the user which is performing the request. " +
NEW_LINE)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/widgetsBundles/list", params = {"widgetsBundleIds"})
public List<WidgetsBundle> getWidgetsBundlesList(
@Parameter(description = "A list of widgets bundle ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam("widgetsBundleIds") Set<UUID> widgetsBundleUUIDs) throws ThingsboardException {
return getWidgetsBundlesByIds(widgetsBundleUUIDs);
}
}

1
application/src/test/java/org/thingsboard/server/edge/NotificationEdgeTest.java

@ -266,7 +266,6 @@ public class NotificationEdgeTest extends AbstractEdgeTest {
notificationRule.setTriggerConfig(triggerConfig);
EscalatedNotificationRuleRecipientsConfig recipientsConfig = new EscalatedNotificationRuleRecipientsConfig();
recipientsConfig.setTriggerType(NotificationRuleTriggerType.ALARM);
Map<Integer, List<UUID>> escalationTable = new HashMap<>();
escalationTable.put(Integer.valueOf("1"), new ArrayList<>());
recipientsConfig.setEscalationTable(escalationTable);

1
application/src/test/java/org/thingsboard/server/service/sync/vc/VersionControlTest.java

@ -942,7 +942,6 @@ public class VersionControlTest extends AbstractControllerTest {
}
return result.getResult().stream().collect(Collectors.toMap(EntityTypeLoadResult::getEntityType, r -> r));
}
private List<EntityVersion> listVersions() throws Exception {
PageData<EntityVersion> versions = doGetAsyncTyped("/api/entities/vc/version?branch=" + branch + "&pageSize=100&page=0&sortProperty=timestamp&sortOrder=DESC", new TypeReference<PageData<EntityVersion>>() {});
return versions.getData();

3
common/data/src/main/java/org/thingsboard/server/common/data/BaseDataWithAdditionalInfo.java

@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.validation.NoXss;
@ -35,9 +36,11 @@ import java.util.function.Supplier;
* Created by ashvayka on 19.02.18.
*/
@Slf4j
@Schema
public abstract class BaseDataWithAdditionalInfo<I extends UUIDBased> extends BaseData<I> implements HasAdditionalInfo {
@NoXss
@Schema
private transient JsonNode additionalInfo;
@JsonIgnore
private byte[] additionalInfoBytes;

2
common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java

@ -70,7 +70,7 @@ public class Dashboard extends DashboardInfo implements ExportableEntity<Dashboa
@Schema(description = "JSON object with main configuration of the dashboard: layouts, widgets, aliases, etc. " +
"The JSON structure of the dashboard configuration is quite complex. " +
"The easiest way to learn it is to export existing dashboard to JSON."
, implementation = com.fasterxml.jackson.databind.JsonNode.class)
, implementation = JsonNode.class)
public JsonNode getConfiguration() {
return configuration;
}

10
common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java

@ -43,17 +43,17 @@ public class OtaPackageInfo extends BaseDataWithAdditionalInfo<OtaPackageId> imp
@Schema(description = "JSON object with Tenant Id. Tenant Id of the ota package can't be changed.", accessMode = Schema.AccessMode.READ_ONLY)
private TenantId tenantId;
@Schema(description = "JSON object with Device Profile Id. Device Profile Id of the ota package can't be changed.", accessMode = Schema.AccessMode.READ_ONLY)
@Schema(description = "JSON object with Device Profile Id. Device Profile Id of the ota package can't be changed.")
private DeviceProfileId deviceProfileId;
@Schema(description = "OTA Package type.", example = "FIRMWARE", accessMode = Schema.AccessMode.READ_ONLY)
@Schema(description = "OTA Package type.", example = "FIRMWARE")
private OtaPackageType type;
@Length(fieldName = "title")
@NoXss
@Schema(description = "OTA Package title.", example = "fw", accessMode = Schema.AccessMode.READ_ONLY)
@Schema(description = "OTA Package title.", example = "fw")
private String title;
@Length(fieldName = "version")
@NoXss
@Schema(description = "OTA Package version.", example = "1.0", accessMode = Schema.AccessMode.READ_ONLY)
@Schema(description = "OTA Package version.", example = "1.0")
private String version;
@Length(fieldName = "tag")
@NoXss
@ -61,7 +61,7 @@ public class OtaPackageInfo extends BaseDataWithAdditionalInfo<OtaPackageId> imp
private String tag;
@Length(fieldName = "url")
@NoXss
@Schema(description = "OTA Package url.", example = "http://thingsboard.org/fw/1", accessMode = Schema.AccessMode.READ_ONLY)
@Schema(description = "OTA Package url.", example = "http://thingsboard.org/fw/1")
private String url;
@Schema(description = "Indicates OTA Package 'has data'. Field is returned from DB ('true' if data exists or url is set). If OTA Package 'has data' is 'false' we can not assign the OTA Package to the Device or Device Profile.", example = "true", accessMode = Schema.AccessMode.READ_ONLY)
private boolean hasData;

1
common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java

@ -28,6 +28,7 @@ import java.io.Serial;
import java.util.Base64;
import java.util.Optional;
@Schema
@Slf4j
@Data
@EqualsAndHashCode(callSuper = true)

12
common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java

@ -46,15 +46,17 @@ public class TbResourceInfo extends BaseData<TbResourceId> implements HasName, H
@Length(fieldName = "title")
@Schema(description = "Resource title.", example = "BinaryAppDataContainer id=19 v1.0")
private String title;
@Schema(description = "Resource type.", example = "LWM2M_MODEL", accessMode = Schema.AccessMode.READ_ONLY)
@Schema(description = "Resource type.", example = "LWM2M_MODEL")
private ResourceType resourceType;
@Schema(description = "Resource sub type.", example = "IOT_SVG", accessMode = Schema.AccessMode.READ_ONLY)
@Schema(description = "Resource sub type.", example = "IOT_SVG")
private ResourceSubType resourceSubType;
@NoXss
@Length(fieldName = "resourceKey")
@Schema(description = "Resource key.", example = "19_1.0", accessMode = Schema.AccessMode.READ_ONLY)
@Schema(description = "Resource key.", example = "19_1.0")
private String resourceKey;
@Schema(description = "Whether the resource is public.", example = "false")
private boolean isPublic;
@Schema(description = "Public resource key.")
private String publicResourceKey;
@Schema(description = "Resource search text.", example = "19_1.0:binaryappdatacontainer", accessMode = Schema.AccessMode.READ_ONLY)
private String searchText;
@ -63,10 +65,12 @@ public class TbResourceInfo extends BaseData<TbResourceId> implements HasName, H
private String etag;
@NoXss
@Length(fieldName = "file name")
@Schema(description = "Resource file name.", example = "19.xml", accessMode = Schema.AccessMode.READ_ONLY)
@Schema(description = "Resource file name.", example = "19.xml")
private String fileName;
@Schema(description = "Resource descriptor.")
private JsonNode descriptor;
@Schema(description = "External resource Id used for import/export.")
private TbResourceId externalId;
public TbResourceInfo() {

4
common/data/src/main/java/org/thingsboard/server/common/data/User.java

@ -91,7 +91,7 @@ public class User extends BaseDataWithAdditionalInfo<UserId> implements HasName,
return super.getCreatedTime();
}
@Schema(description = "JSON object with the Tenant Id.", accessMode = Schema.AccessMode.READ_ONLY)
@Schema(description = "JSON object with the Tenant Id.")
public TenantId getTenantId() {
return tenantId;
}
@ -100,7 +100,7 @@ public class User extends BaseDataWithAdditionalInfo<UserId> implements HasName,
this.tenantId = tenantId;
}
@Schema(description = "JSON object with the Customer Id.", accessMode = Schema.AccessMode.READ_ONLY)
@Schema(description = "JSON object with the Customer Id.")
public CustomerId getCustomerId() {
return customerId;
}

12
common/data/src/main/java/org/thingsboard/server/common/data/ai/dto/TbChatResponse.java

@ -17,8 +17,17 @@ package org.thingsboard.server.common.data.ai.dto;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.swagger.v3.oas.annotations.media.DiscriminatorMapping;
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(
description = "Response from chat API",
discriminatorProperty = "status",
discriminatorMapping = {
@DiscriminatorMapping(value = "SUCCESS", schema = TbChatResponse.Success.class),
@DiscriminatorMapping(value = "FAILURE", schema = TbChatResponse.Failure.class)
}
)
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "status",
@ -33,7 +42,8 @@ public sealed interface TbChatResponse permits TbChatResponse.Success, TbChatRes
@Schema(
description = "Indicates whether the request was successful or not",
example = "SUCCESS"
example = "SUCCESS",
requiredMode = Schema.RequiredMode.REQUIRED
)
String getStatus();

10
common/data/src/main/java/org/thingsboard/server/common/data/ai/dto/TbContent.java

@ -19,11 +19,18 @@ import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import dev.langchain4j.data.message.Content;
import dev.langchain4j.data.message.TextContent;
import io.swagger.v3.oas.annotations.media.DiscriminatorMapping;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import static org.thingsboard.server.common.data.ai.dto.TbContent.TbTextContent;
@Schema(
discriminatorProperty = "contentType",
discriminatorMapping = {
@DiscriminatorMapping(value = "TEXT", schema = TbTextContent.class)
}
)
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
@ -46,7 +53,8 @@ public sealed interface TbContent permits TbTextContent {
}
@Schema(
description = "Text-based content part of a user's prompt"
description = "Text-based content part of a user's prompt",
allOf = TbContent.class
)
record TbTextContent(
@NotBlank

10
common/data/src/main/java/org/thingsboard/server/common/data/ai/dto/TbUserMessage.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.common.data.ai.dto;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
@ -24,9 +25,12 @@ import java.util.List;
public record TbUserMessage(
@NotEmpty
@Valid
@Schema(
requiredMode = Schema.RequiredMode.REQUIRED,
description = "A list of content parts that make up the complete user prompt"
@ArraySchema(
arraySchema = @Schema(
requiredMode = Schema.RequiredMode.REQUIRED,
description = "A list of content parts that make up the complete user prompt"
),
schema = @Schema(ref = "#/components/schemas/TbContent")
)
List<TbContent> contents
) {}

23
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/AiModelConfig.java

@ -17,6 +17,8 @@ package org.thingsboard.server.common.data.ai.model;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.swagger.v3.oas.annotations.media.DiscriminatorMapping;
import io.swagger.v3.oas.annotations.media.Schema;
import org.thingsboard.server.common.data.ai.model.chat.AmazonBedrockChatModelConfig;
import org.thingsboard.server.common.data.ai.model.chat.AnthropicChatModelConfig;
import org.thingsboard.server.common.data.ai.model.chat.AzureOpenAiChatModelConfig;
@ -55,10 +57,31 @@ import org.thingsboard.server.common.data.ai.provider.OpenAiProviderConfig;
@JsonSubTypes.Type(value = GitHubModelsChatModelConfig.class, name = "GITHUB_MODELS"),
@JsonSubTypes.Type(value = OllamaChatModelConfig.class, name = "OLLAMA")
})
@Schema(
name = "AiModelConfig",
description = "Root configuration for AI models",
discriminatorProperty = "provider",
discriminatorMapping = {
@DiscriminatorMapping(value = "OPENAI", schema = OpenAiChatModelConfig.class),
@DiscriminatorMapping(value = "AZURE_OPENAI", schema = AzureOpenAiChatModelConfig.class),
@DiscriminatorMapping(value = "GOOGLE_AI_GEMINI", schema = GoogleAiGeminiChatModelConfig.class),
@DiscriminatorMapping(value = "GOOGLE_VERTEX_AI_GEMINI", schema = GoogleVertexAiGeminiChatModelConfig.class),
@DiscriminatorMapping(value = "MISTRAL_AI", schema = MistralAiChatModelConfig.class),
@DiscriminatorMapping(value = "ANTHROPIC", schema = AnthropicChatModelConfig.class),
@DiscriminatorMapping(value = "AMAZON_BEDROCK", schema = AmazonBedrockChatModelConfig.class),
@DiscriminatorMapping(value = "GITHUB_MODELS", schema = GitHubModelsChatModelConfig.class),
@DiscriminatorMapping(value = "OLLAMA", schema = OllamaChatModelConfig.class)
}
)
public interface AiModelConfig {
@Schema(
description = "AI Provider",
requiredMode = Schema.RequiredMode.REQUIRED
)
AiProvider provider();
@Schema(hidden = true)
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,

3
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AmazonBedrockChatModelConfig.java

@ -16,6 +16,7 @@
package org.thingsboard.server.common.data.ai.model.chat;
import dev.langchain4j.model.chat.ChatModel;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotBlank;
@ -27,8 +28,10 @@ import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.AmazonBedrockProviderConfig;
@Schema
@Builder
public record AmazonBedrockChatModelConfig(
@Schema(ref = "#/components/schemas/AmazonBedrockProviderConfig")
@NotNull @Valid AmazonBedrockProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,

3
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AnthropicChatModelConfig.java

@ -16,6 +16,7 @@
package org.thingsboard.server.common.data.ai.model.chat;
import dev.langchain4j.model.chat.ChatModel;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotBlank;
@ -27,8 +28,10 @@ import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.AnthropicProviderConfig;
@Schema
@Builder
public record AnthropicChatModelConfig(
@Schema(ref = "#/components/schemas/AnthropicProviderConfig")
@NotNull @Valid AnthropicProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,

3
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AzureOpenAiChatModelConfig.java

@ -16,6 +16,7 @@
package org.thingsboard.server.common.data.ai.model.chat;
import dev.langchain4j.model.chat.ChatModel;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotBlank;
@ -27,8 +28,10 @@ import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.AzureOpenAiProviderConfig;
@Schema
@Builder
public record AzureOpenAiChatModelConfig(
@Schema(ref = "#/components/schemas/AzureOpenAiProviderConfig")
@NotNull @Valid AzureOpenAiProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,

3
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GitHubModelsChatModelConfig.java

@ -16,6 +16,7 @@
package org.thingsboard.server.common.data.ai.model.chat;
import dev.langchain4j.model.chat.ChatModel;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotBlank;
@ -27,8 +28,10 @@ import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.GitHubModelsProviderConfig;
@Schema
@Builder
public record GitHubModelsChatModelConfig(
@Schema(ref = "#/components/schemas/GitHubModelsProviderConfig")
@NotNull @Valid GitHubModelsProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,

3
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GoogleAiGeminiChatModelConfig.java

@ -16,6 +16,7 @@
package org.thingsboard.server.common.data.ai.model.chat;
import dev.langchain4j.model.chat.ChatModel;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotBlank;
@ -27,8 +28,10 @@ import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.GoogleAiGeminiProviderConfig;
@Schema
@Builder
public record GoogleAiGeminiChatModelConfig(
@Schema(ref = "#/components/schemas/GoogleAiGeminiProviderConfig")
@NotNull @Valid GoogleAiGeminiProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,

3
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GoogleVertexAiGeminiChatModelConfig.java

@ -16,6 +16,7 @@
package org.thingsboard.server.common.data.ai.model.chat;
import dev.langchain4j.model.chat.ChatModel;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotBlank;
@ -27,8 +28,10 @@ import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.GoogleVertexAiGeminiProviderConfig;
@Schema
@Builder
public record GoogleVertexAiGeminiChatModelConfig(
@Schema(ref = "#/components/schemas/GoogleVertexAiGeminiProviderConfig")
@NotNull @Valid GoogleVertexAiGeminiProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,

3
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/MistralAiChatModelConfig.java

@ -16,6 +16,7 @@
package org.thingsboard.server.common.data.ai.model.chat;
import dev.langchain4j.model.chat.ChatModel;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotBlank;
@ -27,8 +28,10 @@ import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.MistralAiProviderConfig;
@Schema
@Builder
public record MistralAiChatModelConfig(
@Schema(ref = "#/components/schemas/MistralAiProviderConfig")
@NotNull @Valid MistralAiProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,

3
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/OllamaChatModelConfig.java

@ -16,6 +16,7 @@
package org.thingsboard.server.common.data.ai.model.chat;
import dev.langchain4j.model.chat.ChatModel;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotBlank;
@ -27,8 +28,10 @@ import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.OllamaProviderConfig;
@Schema
@Builder
public record OllamaChatModelConfig(
@Schema(ref = "#/components/schemas/OllamaProviderConfig")
@NotNull @Valid OllamaProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,

3
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/OpenAiChatModelConfig.java

@ -16,6 +16,7 @@
package org.thingsboard.server.common.data.ai.model.chat;
import dev.langchain4j.model.chat.ChatModel;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotBlank;
@ -27,8 +28,10 @@ import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.OpenAiProviderConfig;
@Schema
@Builder
public record OpenAiChatModelConfig(
@Schema(ref = "#/components/schemas/OpenAiProviderConfig")
@NotNull @Valid OpenAiProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AmazonBedrockProviderConfig.java

@ -15,8 +15,10 @@
*/
package org.thingsboard.server.common.data.ai.provider;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@Schema
public record AmazonBedrockProviderConfig(
@NotNull String region,
@NotNull String accessKeyId,

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AnthropicProviderConfig.java

@ -15,8 +15,10 @@
*/
package org.thingsboard.server.common.data.ai.provider;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@Schema
public record AnthropicProviderConfig(
@NotNull String apiKey
) implements AiProviderConfig {}

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AzureOpenAiProviderConfig.java

@ -15,8 +15,10 @@
*/
package org.thingsboard.server.common.data.ai.provider;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@Schema
public record AzureOpenAiProviderConfig(
@NotNull String endpoint,
String serviceVersion,

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GitHubModelsProviderConfig.java

@ -15,8 +15,10 @@
*/
package org.thingsboard.server.common.data.ai.provider;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@Schema
public record GitHubModelsProviderConfig(
@NotNull String personalAccessToken
) implements AiProviderConfig {}

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GoogleAiGeminiProviderConfig.java

@ -15,8 +15,10 @@
*/
package org.thingsboard.server.common.data.ai.provider;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@Schema
public record GoogleAiGeminiProviderConfig(
@NotNull String apiKey
) implements AiProviderConfig {}

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GoogleVertexAiGeminiProviderConfig.java

@ -15,9 +15,11 @@
*/
package org.thingsboard.server.common.data.ai.provider;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
@Schema
public record GoogleVertexAiGeminiProviderConfig(
@NotBlank String fileName, // not used on BE, but needed for UI
@NotNull String projectId,

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/MistralAiProviderConfig.java

@ -15,8 +15,10 @@
*/
package org.thingsboard.server.common.data.ai.provider;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@Schema
public record MistralAiProviderConfig(
@NotNull String apiKey
) implements AiProviderConfig {}

12
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/OllamaProviderConfig.java

@ -17,14 +17,26 @@ package org.thingsboard.server.common.data.ai.provider;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.swagger.v3.oas.annotations.media.DiscriminatorMapping;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
@Schema
public record OllamaProviderConfig(
@NotNull String baseUrl,
@NotNull @Valid OllamaAuth auth
) implements AiProviderConfig {
@Schema(
description = "Ollama authentication schemes",
discriminatorProperty = "type",
discriminatorMapping = {
@DiscriminatorMapping(value = "NONE", schema = OllamaAuth.None.class),
@DiscriminatorMapping(value = "BASIC", schema = OllamaAuth.Basic.class),
@DiscriminatorMapping(value = "TOKEN", schema = OllamaAuth.Token.class)
}
)
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/OpenAiProviderConfig.java

@ -16,12 +16,14 @@
package org.thingsboard.server.common.data.ai.provider;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import lombok.Builder;
import org.apache.commons.lang3.StringUtils;
import java.util.Objects;
@Schema
@Builder
public record OpenAiProviderConfig(
String baseUrl,

4
common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java

@ -45,7 +45,7 @@ public class AlarmComment extends BaseData<AlarmCommentId> implements HasName {
private AlarmId alarmId;
@Schema(description = "JSON object with User id.", accessMode = Schema.AccessMode.READ_ONLY)
private UserId userId;
@Schema(description = "Defines origination of comment. System type means comment was created by TB. OTHER type means comment was created by user.", example = "SYSTEM/OTHER", accessMode = Schema.AccessMode.READ_ONLY)
@Schema(description = "Defines origination of comment. System type means comment was created by TB. OTHER type means comment was created by user.", example = "SYSTEM/OTHER")
private AlarmCommentType type;
@Schema(description = "JSON object with text of comment.")
@NoXss
@ -56,7 +56,7 @@ public class AlarmComment extends BaseData<AlarmCommentId> implements HasName {
@Schema(description = "JSON object with the alarm comment Id. " +
"Specify this field to update the alarm comment. " +
"Referencing non-existing alarm Id will cause error. " +
"Omit this field to create new alarm.", accessMode = Schema.AccessMode.READ_ONLY)
"Omit this field to create new alarm.")
@Override
public AlarmCommentId getId() {
return super.getId();

2
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/AlarmRule.java

@ -22,8 +22,10 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.alarm.rule.condition.AlarmCondition;
import io.swagger.v3.oas.annotations.media.Schema;
import org.thingsboard.server.common.data.id.DashboardId;
@Schema(name = "AlarmRuleDefinition")
@Data
@AllArgsConstructor
@NoArgsConstructor

11
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/AlarmCondition.java

@ -20,6 +20,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.swagger.v3.oas.annotations.media.DiscriminatorMapping;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull;
@ -29,6 +31,15 @@ import org.thingsboard.server.common.data.alarm.rule.condition.expression.AlarmC
import org.thingsboard.server.common.data.alarm.rule.condition.schedule.AlarmSchedule;
import org.thingsboard.server.common.data.alarm.rule.condition.schedule.AnyTimeSchedule;
@Schema(
name = "AlarmRuleCondition",
discriminatorProperty = "type",
discriminatorMapping = {
@DiscriminatorMapping(value = "SIMPLE", schema = SimpleAlarmCondition.class),
@DiscriminatorMapping(value = "DURATION", schema = DurationAlarmCondition.class),
@DiscriminatorMapping(value = "REPEATING", schema = RepeatingAlarmCondition.class)
}
)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({

3
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/DurationAlarmCondition.java

@ -21,8 +21,11 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.concurrent.TimeUnit;
@Schema(name = "AlarmRuleDurationCondition")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)

2
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/RepeatingAlarmCondition.java

@ -15,12 +15,14 @@
*/
package org.thingsboard.server.common.data.alarm.rule.condition;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(name = "AlarmRuleRepeatingCondition")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)

3
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/SimpleAlarmCondition.java

@ -15,6 +15,9 @@
*/
package org.thingsboard.server.common.data.alarm.rule.condition;
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(name = "AlarmRuleSimpleCondition")
public class SimpleAlarmCondition extends AlarmCondition {
@Override

9
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/AlarmConditionExpression.java

@ -20,7 +20,16 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.swagger.v3.oas.annotations.media.DiscriminatorMapping;
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(
discriminatorProperty = "type",
discriminatorMapping = {
@DiscriminatorMapping(value = "SIMPLE", schema = SimpleAlarmConditionExpression.class),
@DiscriminatorMapping(value = "TBEL", schema = TbelAlarmConditionExpression.class)
}
)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({

5
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/AlarmConditionFilter.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.common.data.alarm.rule.condition.expression;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
@ -25,9 +26,12 @@ import org.thingsboard.server.common.data.alarm.rule.condition.expression.predic
import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.KeyFilterPredicate;
import org.thingsboard.server.common.data.query.EntityKeyValueType;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.List;
@Schema(name = "AlarmRuleConditionFilter")
@Data
public class AlarmConditionFilter implements Serializable {
@ -36,6 +40,7 @@ public class AlarmConditionFilter implements Serializable {
@NotNull
private EntityKeyValueType valueType;
private ComplexOperation operation;
@ArraySchema(schema = @Schema(ref = "#/components/schemas/AlarmRuleKeyFilterPredicate"))
@Valid
@NotEmpty
private List<KeyFilterPredicate> predicates;

1
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/SimpleAlarmConditionExpression.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.common.data.alarm.rule.condition.expression;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import lombok.AllArgsConstructor;

3
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/BooleanFilterPredicate.java

@ -18,8 +18,10 @@ package org.thingsboard.server.common.data.alarm.rule.condition.expression.predi
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import io.swagger.v3.oas.annotations.media.Schema;
import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue;
@Schema(name = "AlarmRuleBooleanFilterPredicate")
@Data
public class BooleanFilterPredicate implements SimpleKeyFilterPredicate<Boolean> {
@ -29,6 +31,7 @@ public class BooleanFilterPredicate implements SimpleKeyFilterPredicate<Boolean>
@NotNull
private AlarmConditionValue<Boolean> value;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, ref = "#/components/schemas/AlarmRuleFilterPredicateType")
@Override
public FilterPredicateType getType() {
return FilterPredicateType.BOOLEAN;

5
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/ComplexFilterPredicate.java

@ -15,17 +15,22 @@
*/
package org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.ComplexOperation;
import java.util.List;
@Schema(name = "AlarmRuleComplexFilterPredicate")
@Data
public class ComplexFilterPredicate implements KeyFilterPredicate {
private ComplexOperation operation;
@ArraySchema(schema = @Schema(ref = "#/components/schemas/AlarmRuleKeyFilterPredicate"))
private List<KeyFilterPredicate> predicates;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, ref = "#/components/schemas/AlarmRuleFilterPredicateType")
@Override
public FilterPredicateType getType() {
return FilterPredicateType.COMPLEX;

3
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/FilterPredicateType.java

@ -15,6 +15,9 @@
*/
package org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate;
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(name = "AlarmRuleFilterPredicateType")
public enum FilterPredicateType {
STRING,
NUMERIC,

14
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/KeyFilterPredicate.java

@ -19,9 +19,23 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.swagger.v3.oas.annotations.media.DiscriminatorMapping;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
@Schema(
name = "AlarmRuleKeyFilterPredicate",
description = "Filter predicate for alarm rule key-based filtering",
discriminatorProperty = "type",
discriminatorMapping = {
@DiscriminatorMapping(value = "STRING", schema = StringFilterPredicate.class),
@DiscriminatorMapping(value = "NUMERIC", schema = NumericFilterPredicate.class),
@DiscriminatorMapping(value = "BOOLEAN", schema = BooleanFilterPredicate.class),
@DiscriminatorMapping(value = "NO_DATA", schema = NoDataFilterPredicate.class),
@DiscriminatorMapping(value = "COMPLEX", schema = ComplexFilterPredicate.class)
}
)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@Type(value = StringFilterPredicate.class, name = "STRING"),

3
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/NoDataFilterPredicate.java

@ -20,10 +20,12 @@ import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import io.swagger.v3.oas.annotations.media.Schema;
import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue;
import java.util.concurrent.TimeUnit;
@Schema(name = "AlarmRuleNoDataFilterPredicate")
@Data
@AllArgsConstructor
@NoArgsConstructor
@ -35,6 +37,7 @@ public class NoDataFilterPredicate implements KeyFilterPredicate {
@NotNull
private AlarmConditionValue<Long> duration;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, ref = "#/components/schemas/AlarmRuleFilterPredicateType")
@Override
public FilterPredicateType getType() {
return FilterPredicateType.NO_DATA;

3
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/NumericFilterPredicate.java

@ -20,8 +20,10 @@ import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import io.swagger.v3.oas.annotations.media.Schema;
import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue;
@Schema(name = "AlarmRuleNumericFilterPredicate")
@Data
@AllArgsConstructor
@NoArgsConstructor
@ -33,6 +35,7 @@ public class NumericFilterPredicate implements SimpleKeyFilterPredicate<Double>
@NotNull
private AlarmConditionValue<Double> value;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, ref = "#/components/schemas/AlarmRuleFilterPredicateType")
@Override
public FilterPredicateType getType() {
return FilterPredicateType.NUMERIC;

2
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/SimpleKeyFilterPredicate.java

@ -15,8 +15,10 @@
*/
package org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate;
import io.swagger.v3.oas.annotations.media.Schema;
import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue;
@Schema(name = "AlarmRuleSimpleKeyFilterPredicate")
public interface SimpleKeyFilterPredicate<T> extends KeyFilterPredicate {
AlarmConditionValue<T> getValue();

3
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/StringFilterPredicate.java

@ -18,8 +18,10 @@ package org.thingsboard.server.common.data.alarm.rule.condition.expression.predi
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import io.swagger.v3.oas.annotations.media.Schema;
import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue;
@Schema(name = "AlarmRuleStringFilterPredicate")
@Data
public class StringFilterPredicate implements SimpleKeyFilterPredicate<String> {
@ -30,6 +32,7 @@ public class StringFilterPredicate implements SimpleKeyFilterPredicate<String> {
private AlarmConditionValue<String> value;
private boolean ignoreCase;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, ref = "#/components/schemas/AlarmRuleFilterPredicateType")
@Override
public FilterPredicateType getType() {
return FilterPredicateType.STRING;

12
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/AlarmSchedule.java

@ -21,8 +21,20 @@ import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.swagger.v3.oas.annotations.media.DiscriminatorMapping;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
@Schema(
name = "AlarmRuleSchedule",
discriminatorProperty = "type",
discriminatorMapping = {
@DiscriminatorMapping(value = "ANY_TIME", schema = AnyTimeSchedule.class),
@DiscriminatorMapping(value = "SPECIFIC_TIME", schema = SpecificTimeSchedule.class),
@DiscriminatorMapping(value = "CUSTOM", schema = CustomTimeSchedule.class)
}
)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({

3
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/AlarmScheduleType.java

@ -15,6 +15,9 @@
*/
package org.thingsboard.server.common.data.alarm.rule.condition.schedule;
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(name = "AlarmRuleScheduleType")
public enum AlarmScheduleType {
ANY_TIME,
SPECIFIC_TIME,

3
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/AnyTimeSchedule.java

@ -15,6 +15,9 @@
*/
package org.thingsboard.server.common.data.alarm.rule.condition.schedule;
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(name = "AlarmRuleAnyTimeSchedule")
public class AnyTimeSchedule implements AlarmSchedule {
@Override

2
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/CustomTimeSchedule.java

@ -15,10 +15,12 @@
*/
package org.thingsboard.server.common.data.alarm.rule.condition.schedule;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(name = "AlarmRuleCustomTimeSchedule")
@Data
public class CustomTimeSchedule implements AlarmSchedule {

3
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/CustomTimeScheduleItem.java

@ -17,8 +17,11 @@ package org.thingsboard.server.common.data.alarm.rule.condition.schedule;
import lombok.Data;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
@Schema(name = "AlarmRuleCustomTimeScheduleItem")
@Data
public class CustomTimeScheduleItem implements Serializable {

2
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/schedule/SpecificTimeSchedule.java

@ -15,10 +15,12 @@
*/
package org.thingsboard.server.common.data.alarm.rule.condition.schedule;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Set;
@Schema(name = "AlarmRuleSpecificTimeSchedule")
@Data
public class SpecificTimeSchedule implements AlarmSchedule {

2
common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedField.java

@ -86,7 +86,7 @@ public class CalculatedField extends BaseData<CalculatedFieldId> implements HasN
private DebugSettings debugSettings;
@Schema(description = "Version of calculated field configuration.", example = "0")
private int configurationVersion;
@Schema(implementation = SimpleCalculatedFieldConfiguration.class)
@Schema(implementation = CalculatedFieldConfiguration.class)
@Valid
@NotNull
private CalculatedFieldConfiguration configuration;

2
common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AlarmCalculatedFieldConfiguration.java

@ -16,6 +16,7 @@
package org.thingsboard.server.common.data.cf.configuration;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
@ -34,6 +35,7 @@ import java.util.stream.Stream;
import static java.util.Map.Entry.comparingByKey;
@Schema
@Data
public class AlarmCalculatedFieldConfiguration implements ArgumentsBasedCalculatedFieldConfiguration {

2
common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/Argument.java

@ -16,10 +16,12 @@
package org.thingsboard.server.common.data.cf.configuration;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.lang.Nullable;
import org.thingsboard.server.common.data.id.EntityId;
@Schema
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Argument {

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save