Browse Source

Merge branch 'thingsboard:master' into response-topic-exp-required

pull/10981/head
Max Petrov 2 years ago
committed by GitHub
parent
commit
00b771017b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 13
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  2. 3
      application/src/main/java/org/thingsboard/server/controller/AssetController.java
  3. 3
      application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
  4. 3
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  5. 3
      application/src/main/java/org/thingsboard/server/controller/EdgeController.java
  6. 4
      application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java
  7. 4
      application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java
  8. 3
      application/src/main/java/org/thingsboard/server/controller/QueueStatsController.java
  9. 3
      application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
  10. 2
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  11. 4
      application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java
  12. 5
      application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
  13. 2
      application/src/main/java/org/thingsboard/server/service/notification/channels/EmailNotificationChannel.java
  14. 2
      application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java
  15. 11
      application/src/main/java/org/thingsboard/server/service/notification/rule/cache/DefaultNotificationRulesCache.java
  16. 20
      dao/src/main/java/org/thingsboard/server/dao/eventsourcing/ActionCause.java
  17. 4
      dao/src/main/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEvent.java
  18. 3
      dao/src/main/java/org/thingsboard/server/dao/housekeeper/CleanUpService.java
  19. 20
      dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java
  20. 5
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java
  21. 11
      dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java
  22. 88
      dao/src/test/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidatorTest.java
  23. 2
      ui-ngx/src/app/modules/home/components/alias/entity-alias-dialog.component.ts
  24. 2
      ui-ngx/src/app/modules/home/components/relation/relation-filters.component.html
  25. 11
      ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts
  26. 8
      ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts
  27. 19
      ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts
  28. 2
      ui-ngx/src/app/shared/models/limited-api.models.ts

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

@ -16,6 +16,7 @@
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;
@ -350,11 +351,11 @@ public class AlarmController extends BaseController {
@PathVariable(ENTITY_TYPE) String strEntityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_ID) String strEntityId,
@Parameter(description = ALARM_QUERY_SEARCH_STATUS_ARRAY_DESCRIPTION, schema = @Schema(allowableValues = {"ANY", "ACTIVE", "CLEARED", "ACK", "UNACK"}))
@Parameter(description = ALARM_QUERY_SEARCH_STATUS_ARRAY_DESCRIPTION, array = @ArraySchema(schema = @Schema(type = "string", allowableValues = {"ANY", "ACTIVE", "CLEARED", "ACK", "UNACK"})))
@RequestParam(required = false) String[] statusList,
@Parameter(description = ALARM_QUERY_SEVERITY_ARRAY_DESCRIPTION, schema = @Schema(allowableValues = {"CRITICAL", "MAJOR", "MINOR", "WARNING", "INDETERMINATE"}))
@Parameter(description = ALARM_QUERY_SEVERITY_ARRAY_DESCRIPTION, array = @ArraySchema(schema = @Schema(type = "string", allowableValues = {"CRITICAL", "MAJOR", "MINOR", "WARNING", "INDETERMINATE"})))
@RequestParam(required = false) String[] severityList,
@Parameter(description = ALARM_QUERY_TYPE_ARRAY_DESCRIPTION)
@Parameter(description = ALARM_QUERY_TYPE_ARRAY_DESCRIPTION, array = @ArraySchema(schema = @Schema(type = "string")))
@RequestParam(required = false) String[] typeList,
@Parameter(description = ALARM_QUERY_ASSIGNEE_DESCRIPTION)
@RequestParam(required = false) String assigneeId,
@ -412,11 +413,11 @@ public class AlarmController extends BaseController {
@RequestMapping(value = "/v2/alarms", method = RequestMethod.GET)
@ResponseBody
public PageData<AlarmInfo> getAllAlarmsV2(
@Parameter(description = ALARM_QUERY_SEARCH_STATUS_ARRAY_DESCRIPTION, schema = @Schema(allowableValues = {"ANY", "ACTIVE", "CLEARED", "ACK", "UNACK"}))
@Parameter(description = ALARM_QUERY_SEARCH_STATUS_ARRAY_DESCRIPTION, array = @ArraySchema(schema = @Schema(type = "string", allowableValues = {"ANY", "ACTIVE", "CLEARED", "ACK", "UNACK"})))
@RequestParam(required = false) String[] statusList,
@Parameter(description = ALARM_QUERY_SEVERITY_ARRAY_DESCRIPTION, schema = @Schema(allowableValues = {"CRITICAL", "MAJOR", "MINOR", "WARNING", "INDETERMINATE"}))
@Parameter(description = ALARM_QUERY_SEVERITY_ARRAY_DESCRIPTION, array = @ArraySchema(schema = @Schema(type = "string", allowableValues = {"CRITICAL", "MAJOR", "MINOR", "WARNING", "INDETERMINATE"})))
@RequestParam(required = false) String[] severityList,
@Parameter(description = ALARM_QUERY_TYPE_ARRAY_DESCRIPTION)
@Parameter(description = ALARM_QUERY_TYPE_ARRAY_DESCRIPTION, array = @ArraySchema(schema = @Schema(type = "string")))
@RequestParam(required = false) String[] typeList,
@Parameter(description = ALARM_QUERY_ASSIGNEE_DESCRIPTION)
@RequestParam(required = false) String assigneeId,

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

@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
import com.google.common.util.concurrent.ListenableFuture;
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;
@ -354,7 +355,7 @@ public class AssetController extends BaseController {
@RequestMapping(value = "/assets", params = {"assetIds"}, method = RequestMethod.GET)
@ResponseBody
public List<Asset> getAssetsByIds(
@Parameter(description = "A list of assets ids, separated by comma ','")
@Parameter(description = "A list of assets ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")))
@RequestParam("assetIds") String[] strAssetIds) throws ThingsboardException, ExecutionException, InterruptedException {
checkArrayParameter("assetIds", strAssetIds);
SecurityUser user = getCurrentUser();

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

@ -16,6 +16,7 @@
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.Schema;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
@ -83,7 +84,7 @@ public class ComponentDescriptorController extends BaseController {
@RequestMapping(value = "/components", params = {"componentTypes"}, method = RequestMethod.GET)
@ResponseBody
public List<ComponentDescriptor> getComponentDescriptorsByTypes(
@Parameter(description = "List of types of the Rule Nodes, (ENRICHMENT, FILTER, TRANSFORMATION, ACTION or EXTERNAL)", required = true)
@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,
@Parameter(description = "Type of the Rule Chain", schema = @Schema(allowableValues = {"CORE", "EDGE"}))
@RequestParam(value = "ruleChainType", required = false) String strRuleChainType) throws ThingsboardException {

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

@ -20,6 +20,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.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;
@ -488,7 +489,7 @@ public class DeviceController extends BaseController {
@RequestMapping(value = "/devices", params = {"deviceIds"}, method = RequestMethod.GET)
@ResponseBody
public List<Device> getDevicesByIds(
@Parameter(description = "A list of devices ids, separated by comma ','")
@Parameter(description = "A list of devices ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")))
@RequestParam("deviceIds") String[] strDeviceIds) throws ThingsboardException, ExecutionException, InterruptedException {
checkArrayParameter("deviceIds", strDeviceIds);
SecurityUser user = getCurrentUser();

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

@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
import com.google.common.util.concurrent.ListenableFuture;
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;
@ -426,7 +427,7 @@ public class EdgeController extends BaseController {
@RequestMapping(value = "/edges", params = {"edgeIds"}, method = RequestMethod.GET)
@ResponseBody
public List<Edge> getEdgesByIds(
@Parameter(description = "A list of edges ids, separated by comma ','", required = true)
@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();

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

@ -16,6 +16,8 @@
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.Schema;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -156,7 +158,7 @@ public class NotificationTargetController extends BaseController {
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@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", required = true)
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) {
// PE: generic permission

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

@ -16,6 +16,8 @@
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.Schema;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
@ -147,7 +149,7 @@ public class NotificationTemplateController extends BaseController {
@RequestParam(required = false) String sortProperty,
@Parameter(description = SORT_ORDER_DESCRIPTION)
@RequestParam(required = false) String sortOrder,
@Parameter(description = "Comma-separated list of notification types to filter the templates")
@Parameter(description = "Comma-separated list of notification types to filter the templates", array = @ArraySchema(schema = @Schema(type = "string")))
@RequestParam(required = false) NotificationType[] notificationTypes,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
// PE: generic permission

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

@ -16,6 +16,7 @@
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.Schema;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
@ -89,7 +90,7 @@ public class QueueStatsController extends BaseController {
@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 ','", required = true)
@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 {
checkArrayParameter("queueStatsIds", strQueueStatsIds);
List<QueueStatsId> queueStatsIds = new ArrayList<>();

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

@ -16,6 +16,7 @@
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;
@ -272,7 +273,7 @@ public class TbResourceController extends BaseController {
@RequestParam String sortOrder,
@Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"id", "name"}, requiredMode = Schema.RequiredMode.REQUIRED))
@RequestParam String sortProperty,
@Parameter(description = "LwM2M Object ids.", required = true)
@Parameter(description = "LwM2M Object ids.", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@RequestParam(required = false) String[] objectIds) throws ThingsboardException {
return checkNotNull(tbResourceService.findLwM2mObject(getTenantId(), sortOrder, sortProperty, objectIds));
}

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

@ -447,7 +447,7 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> saveEntityTelemetryWithTTL(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")AttributeScope scope,
@Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")String scope,
@Parameter(description = "A long value representing TTL (Time to Live) parameter.", required = true)@PathVariable("ttl")Long ttl,
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);

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

@ -16,6 +16,7 @@
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.Schema;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -251,7 +252,8 @@ public class TenantProfileController extends BaseController {
@GetMapping(value = "/tenantProfiles", params = {"ids"})
@PreAuthorize("hasAuthority('SYS_ADMIN')")
public List<TenantProfile> getTenantProfilesByIds(@RequestParam("ids") UUID[] ids) {
public List<TenantProfile> getTenantProfilesByIds(@Parameter(description = "Comma-separated list of tenant profile ids", array = @ArraySchema(schema = @Schema(type = "string")))
@RequestParam("ids") UUID[] ids) {
return tenantProfileService.findTenantProfilesByIds(TenantId.SYS_TENANT_ID, ids);
}

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

@ -16,6 +16,7 @@
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.Schema;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -185,7 +186,7 @@ public class WidgetTypeController extends AutoCommitController {
@RequestParam(required = false) Boolean fullSearch,
@Parameter(description = DEPRECATED_FILTER_PARAM_DESCRIPTION, schema = @Schema(allowableValues = {"ALL", "ACTUAL", "DEPRECATED"}))
@RequestParam(required = false) String deprecatedFilter,
@Parameter(description = WIDGET_TYPE_ARRAY_DESCRIPTION, schema = @Schema(allowableValues = {"timeseries", "latest", "control", "alarm", "static"}))
@Parameter(description = WIDGET_TYPE_ARRAY_DESCRIPTION, array = @ArraySchema(schema = @Schema(type = "string", allowableValues = {"timeseries", "latest", "control", "alarm", "static"})))
@RequestParam(required = false) String[] widgetTypeList) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
List<String> widgetTypes = widgetTypeList != null ? Arrays.asList(widgetTypeList) : Collections.emptyList();
@ -331,7 +332,7 @@ public class WidgetTypeController extends AutoCommitController {
@RequestParam(required = false) Boolean fullSearch,
@Parameter(description = DEPRECATED_FILTER_PARAM_DESCRIPTION, schema = @Schema(allowableValues = {"ALL", "ACTUAL", "DEPRECATED"}))
@RequestParam(required = false) String deprecatedFilter,
@Parameter(description = WIDGET_TYPE_ARRAY_DESCRIPTION, schema = @Schema(allowableValues = {"timeseries", "latest", "control", "alarm", "static"}))
@Parameter(description = WIDGET_TYPE_ARRAY_DESCRIPTION, array = @ArraySchema(schema = @Schema(allowableValues = {"timeseries", "latest", "control", "alarm", "static"})))
@RequestParam(required = false) String[] widgetTypeList) throws ThingsboardException {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);

2
application/src/main/java/org/thingsboard/server/service/notification/channels/EmailNotificationChannel.java

@ -33,7 +33,7 @@ public class EmailNotificationChannel implements NotificationChannel<User, Email
@Override
public void sendNotification(User recipient, EmailDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) throws Exception {
mailService.send(recipient.getTenantId(), null, TbEmail.builder()
mailService.send(ctx.getTenantId(), null, TbEmail.builder()
.to(recipient.getEmail())
.subject(processedTemplate.getSubject())
.body(processedTemplate.getBody())

2
application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java

@ -38,7 +38,7 @@ public class SmsNotificationChannel implements NotificationChannel<User, SmsDeli
throw new RuntimeException("User does not have phone number");
}
smsService.sendSms(recipient.getTenantId(), recipient.getCustomerId(), new String[]{phone}, processedTemplate.getBody());
smsService.sendSms(ctx.getTenantId(), null, new String[]{phone}, processedTemplate.getBody());
}
@Override

11
application/src/main/java/org/thingsboard/server/service/notification/rule/cache/DefaultNotificationRulesCache.java

@ -17,6 +17,7 @@ package org.thingsboard.server.service.notification.rule.cache;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
@ -29,7 +30,6 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.dao.notification.NotificationRuleService;
import jakarta.annotation.PostConstruct;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -71,7 +71,14 @@ public class DefaultNotificationRulesCache implements NotificationRulesCache {
if (event.getEvent() == ComponentLifecycleEvent.DELETED) {
lock.writeLock().lock(); // locking in case rules for tenant are fetched while evicting
try {
evict(event.getTenantId());
for (var triggerType : NotificationRuleTriggerType.values()) {
String key = key(event.getTenantId(), triggerType);
/*
* temporarily putting empty value because right after tenant deletion
* the rules are still in the db, we don't want them to be fetched
* */
cache.put(key, Collections.emptyList());
}
} finally {
lock.writeLock().unlock();
}

20
dao/src/main/java/org/thingsboard/server/dao/eventsourcing/ActionCause.java

@ -0,0 +1,20 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.eventsourcing;
public enum ActionCause {
TENANT_DELETION
}

4
dao/src/main/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEvent.java

@ -17,18 +17,20 @@ package org.thingsboard.server.dao.eventsourcing;
import lombok.Builder;
import lombok.Data;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
@Builder
@Data
public class DeleteEntityEvent<T> {
private final TenantId tenantId;
private final EntityId entityId;
private final T entity;
private final String body;
private final ActionCause cause;
@Builder.Default
private final long ts = System.currentTimeMillis();
}

3
dao/src/main/java/org/thingsboard/server/dao/housekeeper/CleanUpService.java

@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.housekeeper.HousekeeperTask;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.housekeeper.HousekeeperClient;
import org.thingsboard.server.dao.eventsourcing.ActionCause;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.relation.RelationService;
@ -59,7 +60,7 @@ public class CleanUpService {
if (!skippedEntities.contains(entityType)) {
cleanUpRelatedData(tenantId, entityId);
}
if (entityType == EntityType.USER) {
if (entityType == EntityType.USER && event.getCause() != ActionCause.TENANT_DELETION) {
submitTask(HousekeeperTask.unassignAlarms((User) event.getEntity()));
}
} catch (Throwable e) {

20
dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java

@ -327,12 +327,22 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
if (!isBootstrapServerUpdateEnable && serverConfig.isBootstrapServerIs()) {
throw new DeviceCredentialsValidationException("Bootstrap config must not include \"Bootstrap Server\". \"Include Bootstrap Server updates\" is " + isBootstrapServerUpdateEnable + ".");
}
if (!serverConfig.isBootstrapServerIs() && (serverConfig.getShortServerId() < 1 || serverConfig.getShortServerId() > 65534)) {
throw new DeviceCredentialsValidationException("LwM2M Server ShortServerId must not be less than 1 and more than 65534!");
}
if (serverConfig.isBootstrapServerIs() && !(serverConfig.getShortServerId() == null || serverConfig.getShortServerId() ==0)) {
throw new DeviceCredentialsValidationException("Bootstrap Server ShortServerId must be null or '0'!");
if (serverConfig.getShortServerId() != null) {
if (serverConfig.isBootstrapServerIs()){
if(serverConfig.getShortServerId() < 0 || serverConfig.getShortServerId() > 65535){
throw new DeviceCredentialsValidationException("Bootstrap Server ShortServerId must be in range [0 - 65535]!");
}
} else {
if (serverConfig.getShortServerId() < 1 || serverConfig.getShortServerId() > 65534) {
throw new DeviceCredentialsValidationException("LwM2M Server ShortServerId must be in range [1 - 65534]!");
}
}
} else {
String serverName = serverConfig.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server";
throw new DeviceCredentialsValidationException(serverName + " ShortServerId must not be null!");
}
String server = serverConfig.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server";
if (!shortServerIds.add(serverConfig.getShortServerId())) {
throw new DeviceCredentialsValidationException(server + " \"Short server Id\" value = " + serverConfig.getShortServerId() + ". This value must be a unique value for all servers!");

5
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java

@ -39,8 +39,6 @@ import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.mobile.MobileAppSettingsService;
import org.thingsboard.server.dao.notification.NotificationRequestService;
import org.thingsboard.server.dao.notification.NotificationRuleService;
import org.thingsboard.server.dao.notification.NotificationSettingsService;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.Validator;
@ -168,8 +166,10 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantId, Ten
adminSettingsService.deleteAdminSettingsByTenantId(tenantId);
mobileAppSettingsService.deleteByTenantId(tenantId);
notificationSettingsService.deleteNotificationSettings(tenantId);
tenantDao.removeById(tenantId, tenantId.getId());
publishEvictEvent(new TenantEvictEvent(tenantId, true));
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(tenantId).entity(tenant).build());
cleanUpService.removeTenantEntities(tenantId, // don't forget to implement deleteEntity from EntityDaoService when adding entity type to this list
EntityType.ENTITY_VIEW, EntityType.WIDGETS_BUNDLE, EntityType.WIDGET_TYPE,
@ -179,7 +179,6 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantId, Ten
EntityType.NOTIFICATION_REQUEST, EntityType.NOTIFICATION_RULE, EntityType.NOTIFICATION_TEMPLATE,
EntityType.NOTIFICATION_TARGET, EntityType.QUEUE_STATS, EntityType.CUSTOMER
);
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(tenantId).entity(tenant).build());
}
@Override

11
dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java

@ -53,6 +53,7 @@ import org.thingsboard.server.common.data.settings.UserSettings;
import org.thingsboard.server.common.data.settings.UserSettingsType;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.entity.EntityCountService;
import org.thingsboard.server.dao.eventsourcing.ActionCause;
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
@ -293,6 +294,10 @@ public class UserServiceImpl extends AbstractCachedEntityService<UserCacheKey, U
@Override
@Transactional
public void deleteUser(TenantId tenantId, User user) {
deleteUser(tenantId, user, null);
}
private void deleteUser(TenantId tenantId, User user, ActionCause cause) {
Objects.requireNonNull(user, "User is null");
UserId userId = user.getId();
log.trace("[{}] Executing deleteUser [{}]", tenantId, userId);
@ -307,7 +312,9 @@ public class UserServiceImpl extends AbstractCachedEntityService<UserCacheKey, U
eventPublisher.publishEvent(DeleteEntityEvent.builder()
.tenantId(tenantId)
.entityId(userId)
.entity(user).build());
.entity(user)
.cause(cause)
.build());
}
@Override
@ -564,7 +571,7 @@ public class UserServiceImpl extends AbstractCachedEntityService<UserCacheKey, U
@Override
protected void removeEntity(TenantId tenantId, User user) {
deleteUser(tenantId, user);
deleteUser(tenantId, user, ActionCause.TENANT_DELETION);
}
};

88
dao/src/test/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidatorTest.java

@ -20,11 +20,18 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.lwm2m.OtherConfiguration;
import org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryMappingConfiguration;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.AbstractLwM2MBootstrapServerCredential;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.NoSecLwM2MBootstrapServerCredential;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceDao;
@ -34,6 +41,8 @@ import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.mockito.BDDMockito.willReturn;
@ -42,6 +51,34 @@ import static org.mockito.Mockito.verify;
@SpringBootTest(classes = DeviceProfileDataValidator.class)
class DeviceProfileDataValidatorTest {
private static final String OBSERVE_ATTRIBUTES_WITHOUT_PARAMS =
" {\n" +
" \"keyName\": {},\n" +
" \"observe\": [],\n" +
" \"attribute\": [],\n" +
" \"telemetry\": [],\n" +
" \"attributeLwm2m\": {}\n" +
" }";
public static final String CLIENT_LWM2M_SETTINGS =
" {\n" +
" \"edrxCycle\": null,\n" +
" \"powerMode\": \"DRX\",\n" +
" \"fwUpdateResource\": null,\n" +
" \"fwUpdateStrategy\": 1,\n" +
" \"psmActivityTimer\": null,\n" +
" \"swUpdateResource\": null,\n" +
" \"swUpdateStrategy\": 1,\n" +
" \"pagingTransmissionWindow\": null,\n" +
" \"clientOnlyObserveAfterConnect\": 1\n" +
" }";
private static final String host = "localhost";
private static final String hostBs = "localhost";
private static final int port = 5685;
private static final int portBs = 5687;
@MockBean
DeviceProfileDao deviceProfileDao;
@MockBean
@ -79,5 +116,56 @@ class DeviceProfileDataValidatorTest {
validator.validateDataImpl(tenantId, deviceProfile);
verify(validator).validateString("Device profile name", deviceProfile.getName());
}
@Test
void testValidateDeviceProfile_Lwm2mBootstrap_ShortServerId_Ok() {
Integer shortServerId = 123;
Integer shortServerIdBs = 0;
Lwm2mDeviceProfileTransportConfiguration transportConfiguration =
getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsNoSec(shortServerId, shortServerIdBs));
DeviceProfile deviceProfile = getDeviceProfile(transportConfiguration);
validator.validateDataImpl(tenantId, deviceProfile);
verify(validator).validateString("Device profile name", deviceProfile.getName());
}
private DeviceProfile getDeviceProfile(Lwm2mDeviceProfileTransportConfiguration transportConfiguration) {
DeviceProfile deviceProfile = new DeviceProfile();
deviceProfile.setName("default");
deviceProfile.setType(DeviceProfileType.DEFAULT);
deviceProfile.setTransportType(DeviceTransportType.LWM2M);
DeviceProfileData data = new DeviceProfileData();
data.setTransportConfiguration(transportConfiguration);
deviceProfile.setProfileData(data);
deviceProfile.setTenantId(tenantId);
return deviceProfile;
}
private Lwm2mDeviceProfileTransportConfiguration getTransportConfiguration(String observeAttr, List<LwM2MBootstrapServerCredential> bootstrapServerCredentials) {
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = new Lwm2mDeviceProfileTransportConfiguration();
TelemetryMappingConfiguration observeAttrConfiguration = JacksonUtil.fromString(observeAttr, TelemetryMappingConfiguration.class);
OtherConfiguration clientLwM2mSettings = JacksonUtil.fromString(CLIENT_LWM2M_SETTINGS, OtherConfiguration.class);
transportConfiguration.setBootstrapServerUpdateEnable(true);
transportConfiguration.setObserveAttr(observeAttrConfiguration);
transportConfiguration.setClientLwM2mSettings(clientLwM2mSettings);
transportConfiguration.setBootstrap(bootstrapServerCredentials);
return transportConfiguration;
}
private List<LwM2MBootstrapServerCredential> getBootstrapServerCredentialsNoSec(Integer shortServerId, Integer shortServerIdBs){
List<LwM2MBootstrapServerCredential> bootstrap = new ArrayList<>();
bootstrap.add(getBootstrapServerCredentialNoSec(false, shortServerId, shortServerIdBs));
bootstrap.add(getBootstrapServerCredentialNoSec(true, shortServerId, shortServerIdBs));
return bootstrap;
}
private AbstractLwM2MBootstrapServerCredential getBootstrapServerCredentialNoSec(boolean isBootstrap, Integer shortServerId, Integer shortServerIdBs) {
AbstractLwM2MBootstrapServerCredential bootstrapServerCredential = new NoSecLwM2MBootstrapServerCredential();
bootstrapServerCredential.setServerPublicKey("");
bootstrapServerCredential.setShortServerId(isBootstrap ? shortServerIdBs : shortServerId);
bootstrapServerCredential.setBootstrapServerIs(isBootstrap);
bootstrapServerCredential.setHost(isBootstrap ? hostBs : host);
bootstrapServerCredential.setPort(isBootstrap ? portBs : port);
return bootstrapServerCredential;
}
}

2
ui-ngx/src/app/modules/home/components/alias/entity-alias-dialog.component.ts

@ -145,7 +145,7 @@ export class EntityAliasDialogComponent extends DialogComponent<EntityAliasDialo
if (!isEmpty(this.alias.filter?.filters)) {
this.alias.filter.filters = this.alias.filter.filters.filter((value, index, self) =>
self.findIndex(v => v.relationType === value.relationType && isEqual(v.entityTypes, value.entityTypes)) === index &&
(value.relationType || value.entityTypes.length)
(value.relationType || value.entityTypes?.length)
);
}
if (this.alias.filter.type) {

2
ui-ngx/src/app/modules/home/components/relation/relation-filters.component.html

@ -27,7 +27,7 @@
<div class="tb-form-table-row align-start"
*ngFor="let relationFilterControl of relationFiltersFormArray.controls; let $index = index">
<mat-chip-listbox *ngIf="enableNotOption" class="flex-18 center-stretch" [formControl]="relationFilterControl.get('negate')">
<mat-chip-option color="primary" [value]="true">{{ 'relation.not' | translate}}</mat-chip-option>
<mat-chip-option color="primary" [value]="true">{{ 'relation.not' | translate }}</mat-chip-option>
</mat-chip-listbox>
<tb-relation-type-autocomplete subscriptSizing="dynamic"
class="flex-50" showLabel="false"

11
ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts

@ -128,7 +128,16 @@ export class RelationFiltersComponent extends PageComponent implements ControlVa
entityTypes: [filter ? filter.entityTypes : []]
});
if (this.enableNotOption) {
formGroup.addControl('negate', this.fb.control(filter ? filter.negate : false));
formGroup.addControl('negate', this.fb.control({value: filter ? filter.negate : false, disabled: true}));
formGroup.get('relationType').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(value => {
if (value) {
formGroup.get('negate').enable({emitEvent: false});
} else {
formGroup.get('negate').disable({emitEvent: false});
}
});
}
return formGroup;
}

8
ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts

@ -238,10 +238,10 @@ export class CanvasDigitalGauge extends BaseGauge {
}
if (options.neonGlowBrightness) {
options.neonColorTitle = tinycolor(options.colorTitle).brighten(options.neonGlowBrightness).toHexString();
options.neonColorLabel = tinycolor(options.colorLabel).brighten(options.neonGlowBrightness).toHexString();
options.neonColorValue = tinycolor(options.colorValue).brighten(options.neonGlowBrightness).toHexString();
options.neonColorMinMax = tinycolor(options.colorMinMax).brighten(options.neonGlowBrightness).toHexString();
options.neonColorTitle = tinycolor(options.colorTitle).brighten(options.neonGlowBrightness).toRgbString();
options.neonColorLabel = tinycolor(options.colorLabel).brighten(options.neonGlowBrightness).toRgbString();
options.neonColorValue = tinycolor(options.colorValue).brighten(options.neonGlowBrightness).toRgbString();
options.neonColorMinMax = tinycolor(options.colorMinMax).brighten(options.neonGlowBrightness).toRgbString();
}
return options;

19
ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts

@ -95,9 +95,10 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co
branding: false
};
private propagateChange = (v: any) => { };
private propagateChange = null;
private readonly destroy$ = new Subject<void>();
private expendedBlocks: NotificationDeliveryMethod[];
private propagateChangePending = false;
constructor(private fb: FormBuilder,
private translate: TranslateService) {
@ -105,7 +106,7 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co
this.templateConfigurationForm.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((value) => {
this.propagateChange(value);
this.updateModel(value);
});
}
@ -129,6 +130,12 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co
registerOnChange(fn: any): void {
this.propagateChange = fn;
if (this.propagateChangePending) {
this.propagateChangePending = false;
Promise.resolve().then(() => {
this.templateConfigurationForm.updateValueAndValidity();
});
}
}
registerOnTouched(fn: any): void {
@ -164,6 +171,14 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co
return this.expendedBlocks.includes(name);
}
private updateModel(value: Partial<DeliveryMethodsTemplates>) {
if (this.propagateChange) {
this.propagateChange(value);
} else {
this.propagateChangePending = true;
}
}
private updateExpandedForm() {
this.expendedBlocks = [];
Object.keys(this.templateConfigurationForm.controls).forEach((name: NotificationDeliveryMethod) => {

2
ui-ngx/src/app/shared/models/limited-api.models.ts

@ -46,7 +46,7 @@ export const LimitedApiTranslationMap = new Map<LimitedApi, string>(
[LimitedApi.TRANSPORT_MESSAGES_PER_TENANT, 'api-limit.transport-messages'],
[LimitedApi.TRANSPORT_MESSAGES_PER_DEVICE, 'api-limit.transport-messages-per-device'],
[LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY, 'api-limit.transport-messages-per-gateway'],
[LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY_DEVICE, 'api-limit.transport-messages-per-gateway_device'],
[LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY_DEVICE, 'api-limit.transport-messages-per-gateway-device'],
[LimitedApi.EDGE_EVENTS, 'api-limit.edge-events'],
[LimitedApi.EDGE_EVENTS_PER_EDGE, 'api-limit.edge-events-per-edge'],
[LimitedApi.EDGE_UPLINK_MESSAGES, 'api-limit.edge-uplink-messages'],

Loading…
Cancel
Save