From 34304250869b9d2b07c6c6b8f7b54dae5a7713a5 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 12 Dec 2022 16:40:43 +0100 Subject: [PATCH 01/11] fixed device bulk import with empty credentials --- .../server/service/device/DeviceBulkImportService.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java index 7337497194..9f8d58b566 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java @@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfilePr 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.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; @@ -104,7 +105,7 @@ public class DeviceBulkImportService extends AbstractBulkImportService { protected Device saveEntity(SecurityUser user, Device entity, Map fields) { DeviceCredentials deviceCredentials; try { - deviceCredentials = createDeviceCredentials(fields); + deviceCredentials = createDeviceCredentials(entity.getTenantId(), entity.getId(), fields); deviceCredentialsService.formatCredentials(deviceCredentials); } catch (Exception e) { throw new DeviceCredentialsValidationException("Invalid device credentials: " + e.getMessage()); @@ -136,7 +137,7 @@ public class DeviceBulkImportService extends AbstractBulkImportService { } @SneakyThrows - private DeviceCredentials createDeviceCredentials(Map fields) { + private DeviceCredentials createDeviceCredentials(TenantId tenantId, DeviceId deviceId, Map fields) { DeviceCredentials credentials = new DeviceCredentials(); if (fields.containsKey(BulkImportColumnType.LWM2M_CLIENT_ENDPOINT)) { credentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS); @@ -147,7 +148,9 @@ public class DeviceBulkImportService extends AbstractBulkImportService { } else if (CollectionUtils.containsAny(fields.keySet(), EnumSet.of(BulkImportColumnType.MQTT_CLIENT_ID, BulkImportColumnType.MQTT_USER_NAME, BulkImportColumnType.MQTT_PASSWORD))) { credentials.setCredentialsType(DeviceCredentialsType.MQTT_BASIC); setUpBasicMqttCredentials(fields, credentials); - } else { + } else if (deviceId != null && !fields.containsKey(BulkImportColumnType.ACCESS_TOKEN)) { + credentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, deviceId); + } else { credentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); setUpAccessTokenCredentials(fields, credentials); } From d2233e51578b0b4eaba9594db1c4dc966105dd83 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 12 Dec 2022 18:40:50 +0100 Subject: [PATCH 02/11] added bulk import device test --- .../controller/BaseDeviceControllerTest.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java index b97b346bd9..a8456206f7 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -55,6 +55,9 @@ import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportColumnType; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportRequest; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult; import org.thingsboard.server.dao.device.DeviceDao; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; @@ -1235,6 +1238,66 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { testEntityDaoWithRelationsTransactionalException(deviceDao, savedTenant.getId(), deviceId, "/api/device/" + deviceId); } + @Test + public void testBulkImportDeviceWithoutCredentials() throws Exception { + String deviceName = "some_device"; + String deviceType = "some_type"; + BulkImportRequest request = new BulkImportRequest(); + request.setFile(String.format("NAME,TYPE\n%s,%s", deviceName, deviceType)); + BulkImportRequest.Mapping mapping = new BulkImportRequest.Mapping(); + BulkImportRequest.ColumnMapping name = new BulkImportRequest.ColumnMapping(); + name.setType(BulkImportColumnType.NAME); + BulkImportRequest.ColumnMapping type = new BulkImportRequest.ColumnMapping(); + type.setType(BulkImportColumnType.TYPE); + List columns = new ArrayList<>(); + columns.add(name); + columns.add(type); + + mapping.setColumns(columns); + mapping.setDelimiter(','); + mapping.setUpdate(true); + mapping.setHeader(true); + request.setMapping(mapping); + + BulkImportResult deviceBulkImportResult = doPostWithTypedResponse("/api/device/bulk_import", request, new TypeReference<>() {}); + + Assert.assertEquals(1, deviceBulkImportResult.getCreated().get()); + Assert.assertEquals(0, deviceBulkImportResult.getErrors().get()); + Assert.assertEquals(0, deviceBulkImportResult.getUpdated().get()); + Assert.assertTrue(deviceBulkImportResult.getErrorsList().isEmpty()); + + Device savedDevice = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class); + + Assert.assertNotNull(savedDevice); + Assert.assertEquals(deviceName, savedDevice.getName()); + Assert.assertEquals(deviceType, savedDevice.getType()); + + DeviceCredentials savedCredentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + Assert.assertNotNull(savedCredentials); + Assert.assertNotNull(savedCredentials.getId()); + Assert.assertEquals(savedDevice.getId(), savedCredentials.getDeviceId()); + Assert.assertEquals(DeviceCredentialsType.ACCESS_TOKEN, savedCredentials.getCredentialsType()); + Assert.assertNotNull(savedCredentials.getCredentialsId()); + Assert.assertEquals(20, savedCredentials.getCredentialsId().length()); + + deviceBulkImportResult = doPostWithTypedResponse("/api/device/bulk_import", request, new TypeReference<>() {}); + + Assert.assertEquals(0, deviceBulkImportResult.getCreated().get()); + Assert.assertEquals(0, deviceBulkImportResult.getErrors().get()); + Assert.assertEquals(1, deviceBulkImportResult.getUpdated().get()); + Assert.assertTrue(deviceBulkImportResult.getErrorsList().isEmpty()); + + Device updatedDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class); + Assert.assertEquals(savedDevice, updatedDevice); + + DeviceCredentials updatedCredentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + Assert.assertEquals(savedCredentials, updatedCredentials); + } + private Device createDevice(String name) { Device device = new Device(); device.setName(name); From 139e5d28fb7c1fbde33062cd631d66c0adf8987c Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 13 Dec 2022 10:57:29 +0200 Subject: [PATCH 03/11] Fix handleGetTsCmd: add to lastTsMap --- .../DefaultTbEntityDataSubscriptionService.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java index 796a4bfea1..3d9077cb92 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java @@ -564,13 +564,8 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc for (ReadTsKvQueryResult queryResult : queryResults) { String queryKey = queriesKeys.get(queryResult.getQueryId()); if (queryKey != null) { - TsValue[] tsValues = entityData.getTimeseries().get(queryKey); - if (tsValues == null) { - tsValues = queryResult.toTsValues(); - } else { - tsValues = ArrayUtils.addAll(tsValues, queryResult.toTsValues()); - } - entityData.getTimeseries().put(queryKey, tsValues); + entityData.getTimeseries().merge(queryKey, queryResult.toTsValues(), ArrayUtils::addAll); + lastTsMap.merge(queryKey, queryResult.getLastEntryTs(), Math::max); } else { log.warn("ReadTsKvQueryResult for {} {} has queryId not matching the initial query", entityData.getEntityId().getEntityType(), entityData.getEntityId()); From 7cb9166677d6c9f2efdddefbb5c394c9be82a183 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 13 Dec 2022 10:58:34 +0100 Subject: [PATCH 04/11] fixed saveDeviceWithCredentials notifications --- .../device/DefaultTbDeviceService.java | 9 ++-- .../controller/BaseDeviceControllerTest.java | 53 +++++++++++++++++++ 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java index f72092bedf..25053676fd 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java @@ -76,17 +76,18 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T @Override public Device saveDeviceWithCredentials(Device device, DeviceCredentials credentials, User user) throws ThingsboardException { - ActionType actionType = device.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + boolean isCreate = device.getId() == null; + ActionType actionType = isCreate ? ActionType.ADDED : ActionType.UPDATED; TenantId tenantId = device.getTenantId(); try { + Device oldDevice = isCreate ? null : deviceService.findDeviceById(tenantId, device.getId()); Device savedDevice = checkNotNull(deviceService.saveDeviceWithCredentials(device, credentials)); notificationEntityService.notifyCreateOrUpdateDevice(tenantId, savedDevice.getId(), savedDevice.getCustomerId(), - savedDevice, device, actionType, user); + savedDevice, oldDevice, actionType, user); return savedDevice; } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE), device, - actionType, user, e); + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE), device, actionType, user, e); throw e; } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java index b97b346bd9..22edb56eab 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest; import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; @@ -180,6 +181,58 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Assert.assertEquals(foundDevice.getName(), savedDevice.getName()); } + @Test + public void testSaveDeviceWithCredentials() throws Exception { + String testToken = "TEST_TOKEN"; + + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + + DeviceCredentials deviceCredentials = new DeviceCredentials(); + deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); + deviceCredentials.setCredentialsId(testToken); + + SaveDeviceWithCredentialsRequest saveRequest = new SaveDeviceWithCredentialsRequest(device, deviceCredentials); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + Device savedDevice = readResponse(doPost("/api/device-with-credentials", saveRequest).andExpect(status().isOk()), Device.class); + + Device oldDevice = new Device(savedDevice); + + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED); + testNotificationUpdateGatewayNever(); + + Assert.assertNotNull(savedDevice); + Assert.assertNotNull(savedDevice.getId()); + Assert.assertTrue(savedDevice.getCreatedTime() > 0); + Assert.assertEquals(savedTenant.getId(), savedDevice.getTenantId()); + Assert.assertNotNull(savedDevice.getCustomerId()); + Assert.assertEquals(NULL_UUID, savedDevice.getCustomerId().getId()); + Assert.assertEquals(device.getName(), savedDevice.getName()); + + DeviceCredentials foundDeviceCredentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + Assert.assertNotNull(foundDeviceCredentials); + Assert.assertNotNull(foundDeviceCredentials.getId()); + Assert.assertEquals(savedDevice.getId(), foundDeviceCredentials.getDeviceId()); + Assert.assertEquals(DeviceCredentialsType.ACCESS_TOKEN, foundDeviceCredentials.getCredentialsType()); + Assert.assertEquals(testToken, foundDeviceCredentials.getCredentialsId()); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + savedDevice.setName("My new device"); + doPost("/api/device", savedDevice, Device.class); + + testNotifyEntityAllOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UPDATED); + testNotificationUpdateGatewayOneTime(savedDevice, oldDevice); + } + @Test public void saveDeviceWithViolationOfValidation() throws Exception { Device device = new Device(); From 4a834a0442cb7131a74549d1c7294cf8e01817e8 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 13 Dec 2022 15:06:29 +0200 Subject: [PATCH 05/11] Fix NPE on WS subscription for sysadmin --- .../controller/plugin/TbWebSocketHandler.java | 47 ++++++++++++------- .../DefaultTelemetryWebSocketService.java | 12 ++++- .../controller/BaseWebsocketApiTest.java | 38 ++++++++++++++- .../controller/TbTestWebSocketClient.java | 19 ++++++++ 4 files changed, 95 insertions(+), 21 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index 432b35fd80..3a429add9b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -28,10 +28,12 @@ import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.adapter.NativeWebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.msg.tools.TbRateLimits; import org.thingsboard.server.config.WebSocketConfiguration; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; @@ -50,6 +52,7 @@ import javax.websocket.Session; import java.io.IOException; import java.net.URI; import java.security.InvalidParameterException; +import java.util.Optional; import java.util.Queue; import java.util.Set; import java.util.UUID; @@ -136,8 +139,9 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr if (!checkLimits(session, sessionRef)) { return; } - var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration(); - internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef, tenantProfileConfiguration.getWsMsgQueueLimitPerSession() > 0 ? + var tenantProfileConfiguration = getTenantProfileConfiguration(sessionRef); + internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef, + tenantProfileConfiguration != null && tenantProfileConfiguration.getWsMsgQueueLimitPerSession() > 0 ? tenantProfileConfiguration.getWsMsgQueueLimitPerSession() : 500)); externalSessionMap.put(externalSessionId, internalSessionId); @@ -316,22 +320,24 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr if (internalId != null) { SessionMetaData sessionMd = internalSessionMap.get(internalId); if (sessionMd != null) { - var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration(); - if (StringUtils.isNotEmpty(tenantProfileConfiguration.getWsUpdatesPerSessionRateLimit())) { - TbRateLimits rateLimits = perSessionUpdateLimits.computeIfAbsent(sessionRef.getSessionId(), sid -> new TbRateLimits(tenantProfileConfiguration.getWsUpdatesPerSessionRateLimit())); - if (!rateLimits.tryConsume()) { - if (blacklistedSessions.putIfAbsent(externalId, sessionRef) == null) { - log.info("[{}][{}][{}] Failed to process session update. Max session updates limit reached" - , sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), externalId); - sessionMd.sendMsg("{\"subscriptionId\":" + subscriptionId + ", \"errorCode\":" + ThingsboardErrorCode.TOO_MANY_UPDATES.getErrorCode() + ", \"errorMsg\":\"Too many updates!\"}"); + var tenantProfileConfiguration = getTenantProfileConfiguration(sessionRef); + if (tenantProfileConfiguration != null) { + if (StringUtils.isNotEmpty(tenantProfileConfiguration.getWsUpdatesPerSessionRateLimit())) { + TbRateLimits rateLimits = perSessionUpdateLimits.computeIfAbsent(sessionRef.getSessionId(), sid -> new TbRateLimits(tenantProfileConfiguration.getWsUpdatesPerSessionRateLimit())); + if (!rateLimits.tryConsume()) { + if (blacklistedSessions.putIfAbsent(externalId, sessionRef) == null) { + log.info("[{}][{}][{}] Failed to process session update. Max session updates limit reached" + , sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), externalId); + sessionMd.sendMsg("{\"subscriptionId\":" + subscriptionId + ", \"errorCode\":" + ThingsboardErrorCode.TOO_MANY_UPDATES.getErrorCode() + ", \"errorMsg\":\"Too many updates!\"}"); + } + return; + } else { + log.debug("[{}][{}][{}] Session is no longer blacklisted.", sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), externalId); + blacklistedSessions.remove(externalId); } - return; } else { - log.debug("[{}][{}][{}] Session is no longer blacklisted.", sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), externalId); - blacklistedSessions.remove(externalId); + perSessionUpdateLimits.remove(sessionRef.getSessionId()); } - } else { - perSessionUpdateLimits.remove(sessionRef.getSessionId()); } sessionMd.sendMsg(msg); } else { @@ -376,8 +382,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } private boolean checkLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) throws Exception { - var tenantProfileConfiguration = - tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration(); + var tenantProfileConfiguration = getTenantProfileConfiguration(sessionRef); if (tenantProfileConfiguration == null) { return true; } @@ -444,7 +449,8 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } private void cleanupLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) { - var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration(); + var tenantProfileConfiguration = getTenantProfileConfiguration(sessionRef); + if (tenantProfileConfiguration == null) return; String sessionId = session.getId(); perSessionUpdateLimits.remove(sessionRef.getSessionId()); @@ -477,4 +483,9 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } } + private DefaultTenantProfileConfiguration getTenantProfileConfiguration(TelemetryWebSocketSessionRef sessionRef) { + return Optional.ofNullable(tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId())) + .map(TenantProfile::getDefaultProfileConfiguration).orElse(null); + } + } \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java index d9beb1ed33..06de7a5fe7 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java @@ -31,6 +31,7 @@ import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; @@ -316,7 +317,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } private void processSessionClose(TelemetryWebSocketSessionRef sessionRef) { - var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration(); + var tenantProfileConfiguration = getTenantProfileConfiguration(sessionRef); if (tenantProfileConfiguration != null) { String sessionId = "[" + sessionRef.getSessionId() + "]"; @@ -350,7 +351,8 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } private boolean processSubscription(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd) { - var tenantProfileConfiguration = (DefaultTenantProfileConfiguration) tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration(); + var tenantProfileConfiguration = getTenantProfileConfiguration(sessionRef); + if (tenantProfileConfiguration == null) return true; String subId = "[" + sessionRef.getSessionId() + "]:[" + cmd.getCmdId() + "]"; try { @@ -932,4 +934,10 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi private int getLimit(int limit) { return limit == 0 ? DEFAULT_LIMIT : limit; } + + private DefaultTenantProfileConfiguration getTenantProfileConfiguration(TelemetryWebSocketSessionRef sessionRef) { + return Optional.ofNullable(tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId())) + .map(TenantProfile::getDefaultProfileConfiguration).orElse(null); + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java index f7838d9689..5f133658af 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.FutureCallback; import lombok.extern.slf4j.Slf4j; import org.checkerframework.checker.nullness.qual.Nullable; @@ -23,11 +24,15 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.query.DeviceTypeFilter; @@ -41,12 +46,14 @@ import org.thingsboard.server.common.data.query.EntityKeyValueType; import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.data.query.KeyFilter; import org.thingsboard.server.common.data.query.NumericFilterPredicate; +import org.thingsboard.server.common.data.query.SingleEntityFilter; import org.thingsboard.server.common.data.query.TsValue; import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUpdate; import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; +import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; import java.util.Arrays; import java.util.Collections; @@ -54,6 +61,8 @@ import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @Slf4j @@ -78,6 +87,7 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { @After public void tearDown() throws Exception { + loginTenantAdmin(); doDelete("/api/device/" + device.getId().getId()) .andExpect(status().isOk()); } @@ -532,6 +542,28 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { Assert.assertEquals(new TsValue(dataPoint5.getLastUpdateTs(), dataPoint5.getValueAsString()), attrValue); } + @Test + public void testAttributesSubscription_sysAdmin() throws Exception { + loginSysAdmin(); + SingleEntityFilter entityFilter = new SingleEntityFilter(); + entityFilter.setSingleEntity(tenantId); + + assertThatNoException().isThrownBy(() -> { + JsonNode update = getWsClient().subscribeForAttributes(tenantId, TbAttributeSubscriptionScope.SERVER_SCOPE.name(), List.of("attr")); + assertThat(update.get("errorMsg").isNull()).isTrue(); + assertThat(update.get("errorCode").asInt()).isEqualTo(SubscriptionErrorCode.NO_ERROR.getCode()); + }); + + getWsClient().registerWaitForUpdate(); + String expectedAttrValue = "42"; + sendAttributes(TenantId.SYS_TENANT_ID, tenantId, TbAttributeSubscriptionScope.SERVER_SCOPE, List.of( + new BaseAttributeKvEntry(System.currentTimeMillis(), new StringDataEntry("attr", expectedAttrValue)) + )); + JsonNode update = JacksonUtil.toJsonNode(getWsClient().waitForUpdate()); + assertThat(update).isNotNull(); + assertThat(update.get("data").get("attr").get(0).get(1).asText()).isEqualTo(expectedAttrValue); + } + private void sendTelemetry(Device device, List tsData) throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); tsService.saveAndNotify(device.getTenantId(), null, device.getId(), tsData, 0, new FutureCallback() { @@ -549,8 +581,12 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { } private void sendAttributes(Device device, TbAttributeSubscriptionScope scope, List attrData) throws InterruptedException { + sendAttributes(device.getTenantId(), device.getId(), scope, attrData); + } + + private void sendAttributes(TenantId tenantId, EntityId entityId, TbAttributeSubscriptionScope scope, List attrData) throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); - tsService.saveAndNotify(device.getTenantId(), device.getId(), scope.name(), attrData, new FutureCallback() { + tsService.saveAndNotify(tenantId, entityId, scope.name(), attrData, new FutureCallback() { @Override public void onSuccess(@Nullable Void result) { latch.countDown(); diff --git a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java index be7973d517..efd2336ac1 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java @@ -15,16 +15,20 @@ */ package org.thingsboard.server.controller; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.kv.Aggregation; import org.thingsboard.server.common.data.query.EntityDataPageLink; import org.thingsboard.server.common.data.query.EntityDataQuery; import org.thingsboard.server.common.data.query.EntityFilter; import org.thingsboard.server.common.data.query.EntityKey; import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; +import org.thingsboard.server.service.telemetry.cmd.v1.AttributesSubscriptionCmd; import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUpdate; import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; @@ -177,6 +181,21 @@ public class TbTestWebSocketClient extends WebSocketClient { return parseDataReply(waitForReply()); } + public JsonNode subscribeForAttributes(EntityId entityId, String scope, List keys) { + AttributesSubscriptionCmd cmd = new AttributesSubscriptionCmd(); + cmd.setCmdId(1); + cmd.setEntityType(entityId.getEntityType().toString()); + cmd.setEntityId(entityId.getId().toString()); + cmd.setScope(scope); + cmd.setKeys(String.join(",", keys)); + TelemetryPluginCmdsWrapper cmdsWrapper = new TelemetryPluginCmdsWrapper(); + cmdsWrapper.setAttrSubCmds(List.of(cmd)); + JsonNode msg = JacksonUtil.valueToTree(cmdsWrapper); + ((ObjectNode) msg.get("attrSubCmds").get(0)).remove("type"); + send(msg.toString()); + return JacksonUtil.toJsonNode(waitForReply()); + } + public EntityDataUpdate sendHistoryCmd(List keys, long startTs, long timeWindow) { return sendHistoryCmd(keys, startTs, timeWindow, (EntityDataQuery) null); } From 5645527a3b6bf18c65952eaaee193f812b88436f Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 13 Dec 2022 15:20:04 +0200 Subject: [PATCH 06/11] Update TBEL version to 1.0.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f1c3adbad8..3d51b748ce 100755 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ 3.5.5 3.21.9 1.42.1 - 1.0.3 + 1.0.4 1.18.18 1.2.4 4.1.75.Final From 603f71bbe2de5edc9982dac7055a6b96f5baca68 Mon Sep 17 00:00:00 2001 From: Yuriy Lytvynchuk Date: Tue, 13 Dec 2022 16:09:36 +0200 Subject: [PATCH 07/11] fix empty array & nodeDetails --- .../rule/engine/transform/TbSplitArrayMsgNode.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java index 2dc27b6f92..d018b86922 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java @@ -40,7 +40,7 @@ import java.util.concurrent.ExecutionException; name = "split array msg", configClazz = EmptyNodeConfiguration.class, nodeDescription = "Split array message into several msgs", - nodeDetails = "Split the array fetched from the msg body. If the msg data is not a JSON object returns the " + nodeDetails = "Split the array fetched from the msg body. If the msg data is not a JSON array returns the " + "incoming message as outbound message with Failure chain, otherwise returns " + "inner objects of the extracted array as separate messages via Success chain.", uiResources = {"static/rulenode/rulenode-core-config.js"}, @@ -61,7 +61,9 @@ public class TbSplitArrayMsgNode implements TbNode { JsonNode jsonNode = JacksonUtil.toJsonNode(msg.getData()); if (jsonNode.isArray()) { ArrayNode data = (ArrayNode) jsonNode; - if (data.size() == 1) { + if (data.isEmpty()) { + ctx.ack(msg); + } else if (data.size() == 1) { ctx.tellSuccess(TbMsg.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), JacksonUtil.toString(data.get(0)))); } else { TbMsgCallbackWrapper wrapper = new MultipleTbMsgsCallbackWrapper(data.size(), new TbMsgCallback() { From a6f2d114f161d4b4ad94bf3393826faade5c350e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 13 Dec 2022 16:28:30 +0200 Subject: [PATCH 08/11] UI navigation improvements --- .../entity/entities-table.component.html | 3 ++- .../entity/entities-table-config.models.ts | 15 +++++++++++ .../dashboards-table-config.resolver.ts | 27 +++++++++++++------ .../rulechains-table-config.resolver.ts | 24 ++++++++++++----- .../widget/widget-library.component.html | 3 ++- .../widget/widget-library.component.scss | 8 ++++++ .../pages/widget/widget-library.component.ts | 1 + .../widgets-bundles-table-config.resolver.ts | 23 +++++++++++----- ui-ngx/src/styles.scss | 3 +++ 9 files changed, 85 insertions(+), 22 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html index 5f023b2efb..ed26721661 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html @@ -251,7 +251,8 @@ , P extends PageLink = P entitiesDeleteEnabled = true; detailsPanelEnabled = true; hideDetailsTabsOnEdit = true; + rowPointer = false; actionsColumnTitle = null; entityTranslations: EntityTypeTranslation; entityResources: EntityTypeResource; @@ -220,6 +221,20 @@ export class EntityTableConfig, P extends PageLink = P } } + toggleEntityDetails($event: Event, entity: T) { + if (this.table) { + this.table.toggleEntityDetails($event, entity); + } + } + + isDetailsOpen(): boolean { + if (this.table) { + return this.table.isDetailsOpen; + } else { + return false; + } + } + getActivatedRoute(): ActivatedRoute { if (this.table) { return this.table.route; diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts index fddc9e94ce..ea22fa590a 100644 --- a/ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts @@ -94,6 +94,8 @@ export class DashboardsTableConfigResolver implements Resolve this.translate.instant('dashboard.delete-dashboard-title', {dashboardTitle: dashboard.title}); this.config.deleteEntityContent = () => this.translate.instant('dashboard.delete-dashboard-text'); @@ -107,6 +109,15 @@ export class DashboardsTableConfigResolver implements Resolve this.onDashboardAction(action); this.config.detailsReadonly = () => (this.config.componentsData.dashboardScope === 'customer_user' || this.config.componentsData.dashboardScope === 'edge_customer_user'); + + this.config.handleRowClick = ($event, dashboard) => { + if (this.config.isDetailsOpen()) { + this.config.toggleEntityDetails($event, dashboard); + } else { + this.openDashboard($event, dashboard); + } + return true; + }; } resolve(route: ActivatedRouteSnapshot): Observable> { @@ -197,14 +208,6 @@ export class DashboardsTableConfigResolver implements Resolve> { const actions: Array> = []; - actions.push( - { - name: this.translate.instant('dashboard.open-dashboard'), - icon: 'dashboard', - isEnabled: () => true, - onAction: ($event, entity) => this.openDashboard($event, entity) - } - ); if (dashboardScope === 'tenant') { actions.push( { @@ -271,6 +274,14 @@ export class DashboardsTableConfigResolver implements Resolve true, + onAction: ($event, entity) => this.config.toggleEntityDetails($event, entity) + } + ); return actions; } diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts index a3ff812359..b414f2ff21 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts @@ -74,6 +74,8 @@ export class RuleChainsTableConfigResolver implements Resolve this.translate.instant('rulechain.delete-rulechain-title', {ruleChainName: ruleChain.name}); this.config.deleteEntityContent = () => this.translate.instant('rulechain.delete-rulechain-text'); @@ -83,6 +85,14 @@ export class RuleChainsTableConfigResolver implements Resolve this.saveRuleChain(ruleChain); this.config.deleteEntity = id => this.ruleChainService.deleteRuleChain(id.id); this.config.onEntityAction = action => this.onRuleChainAction(action); + this.config.handleRowClick = ($event, ruleChain) => { + if (this.config.isDetailsOpen()) { + this.config.toggleEntityDetails($event, ruleChain); + } else { + this.openRuleChain($event, ruleChain); + } + return true; + }; } resolve(route: ActivatedRouteSnapshot): EntityTableConfig { @@ -214,12 +224,6 @@ export class RuleChainsTableConfigResolver implements Resolve> { const actions: Array> = []; actions.push( - { - name: this.translate.instant('rulechain.open-rulechain'), - icon: 'settings_ethernet', - isEnabled: () => true, - onAction: ($event, entity) => this.openRuleChain($event, entity) - }, { name: this.translate.instant('rulechain.export'), icon: 'file_download', @@ -275,6 +279,14 @@ export class RuleChainsTableConfigResolver implements Resolve true, + onAction: ($event, entity) => this.config.toggleEntityDetails($event, entity) + } + ); return actions; } diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-library.component.html b/ui-ngx/src/app/modules/home/pages/widget/widget-library.component.html index 5ce4f3f1cf..4c74e68f62 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-library.component.html +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-library.component.html @@ -27,7 +27,8 @@ style="display: flex;" class="mat-headline tb-absolute-fill">widgets-bundle.empty - widgetsBundle ? widgetsBundle.title : ''; @@ -89,17 +91,17 @@ export class WidgetsBundlesTableConfigResolver implements Resolve true, - onAction: ($event, entity) => this.openWidgetsBundle($event, entity) - }, { name: this.translate.instant('widgets-bundle.export'), icon: 'file_download', isEnabled: () => true, onAction: ($event, entity) => this.exportWidgetsBundle($event, entity) + }, + { + name: this.translate.instant('widgets-bundle.widgets-bundle-details'), + icon: 'edit', + isEnabled: () => true, + onAction: ($event, entity) => this.config.toggleEntityDetails($event, entity) } ); @@ -114,6 +116,15 @@ export class WidgetsBundlesTableConfigResolver implements Resolve this.widgetsService.saveWidgetsBundle(widgetsBundle); this.config.deleteEntity = id => this.widgetsService.deleteWidgetsBundle(id.id); this.config.onEntityAction = action => this.onWidgetsBundleAction(action); + + this.config.handleRowClick = ($event, widgetsBundle) => { + if (this.config.isDetailsOpen()) { + this.config.toggleEntityDetails($event, widgetsBundle); + } else { + this.openWidgetsBundle($event, widgetsBundle); + } + return true; + }; } resolve(): EntityTableConfig { diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index 8c6fffabf1..44480b8e1b 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -1011,6 +1011,9 @@ mat-label { &.tb-current-entity { background-color: #e9e9e9; } + &.tb-pointer { + cursor: pointer; + } } .mat-row:not(.mat-row-select), .mat-header-row:not(.mat-row-select) { From b56734148146e2c81bceba1d30ec16185cfa2520 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 15 Dec 2022 12:36:23 +0200 Subject: [PATCH 09/11] added DeviceProfileService and AssetProfileService to TBContext --- .../server/actors/ActorSystemContext.java | 10 ++++++++++ .../server/actors/ruleChain/DefaultTbContext.java | 12 ++++++++++++ .../org/thingsboard/rule/engine/api/TbContext.java | 6 ++++++ 3 files changed, 28 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index e1a41fe401..8b19a6954c 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -50,6 +50,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.tools.TbRateLimits; import org.thingsboard.server.common.stats.TbApiUsageReportClient; +import org.thingsboard.server.dao.asset.AssetProfileService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.audit.AuditLogService; @@ -58,6 +59,7 @@ import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.ClaimDevicesService; import org.thingsboard.server.dao.device.DeviceCredentialsService; +import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; @@ -176,6 +178,14 @@ public class ActorSystemContext { @Getter private DeviceService deviceService; + @Autowired + @Getter + private DeviceProfileService deviceProfileService; + + @Autowired + @Getter + private AssetProfileService assetProfileService; + @Autowired @Getter private DeviceCredentialsService deviceCredentialsService; diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index ef2de35912..ba1c4e9796 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -71,12 +71,14 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgProcessingStackItem; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.dao.asset.AssetProfileService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.cassandra.CassandraCluster; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceCredentialsService; +import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; @@ -559,6 +561,16 @@ class DefaultTbContext implements TbContext { return mainCtx.getDeviceService(); } + @Override + public DeviceProfileService getDeviceProfileService() { + return mainCtx.getDeviceProfileService(); + } + + @Override + public AssetProfileService getAssetProfileService() { + return mainCtx.getAssetProfileService(); + } + @Override public DeviceCredentialsService getDeviceCredentialsService() { return mainCtx.getDeviceCredentialsService(); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index 9851eead80..1786fd08be 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -42,12 +42,14 @@ import org.thingsboard.server.common.data.rule.RuleNodeState; import org.thingsboard.server.common.data.script.ScriptLanguage; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.asset.AssetProfileService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.cassandra.CassandraCluster; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceCredentialsService; +import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; @@ -223,6 +225,10 @@ public interface TbContext { DeviceService getDeviceService(); + DeviceProfileService getDeviceProfileService(); + + AssetProfileService getAssetProfileService(); + DeviceCredentialsService getDeviceCredentialsService(); TbClusterService getClusterService(); From bb79dbf273418ac63cbd78cc0566767eef6e80c6 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 15 Dec 2022 14:18:20 +0200 Subject: [PATCH 10/11] Release notes autogeneration --- .github/release.yml | 88 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 .github/release.yml diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000000..01ba7d863e --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,88 @@ +# +# Copyright © 2016-2022 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. +# + +changelog: + exclude: + labels: + - Ignore for release + categories: + - title: 'Major Core & Rule Engine' + labels: + - 'Major Core' + - 'Major Rule Engine' + exclude: + labels: + - 'Bug' + - title: 'Major UI' + labels: + - 'Major UI' + exclude: + labels: + - 'Bug' + - title: 'Major Transport' + labels: + - 'Major Transport' + exclude: + labels: + - 'Bug' + - title: 'Major Edge' + labels: + - 'Major Edge' + exclude: + labels: + - 'Bug' + - title: 'Core & Rule Engine' + labels: + - 'Core' + - 'Rule Engine' + exclude: + labels: + - 'Bug' + - title: 'UI' + labels: + - 'UI' + exclude: + labels: + - 'Bug' + - title: 'Transport' + labels: + - 'Transport' + exclude: + labels: + - 'Bug' + - title: 'Edge' + labels: + - 'Edge' + exclude: + labels: + - 'Bug' + - title: 'Bug: Core & Rule Engine' + labels: + - 'Core' + - 'Rule Engine' + - 'Bug' + - title: 'Bug: UI' + labels: + - 'UI' + - 'Bug' + - title: 'Bug: Transport' + labels: + - 'Transport' + - 'Bug' + - title: 'Bug: Edge' + labels: + - 'Edge' + - 'Bug' From df28b217330062e8cbb3560d05d1be7be736e8f6 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 15 Dec 2022 13:19:03 +0100 Subject: [PATCH 11/11] fixed bulk import test infinite await --- .../sync/ie/importing/csv/AbstractBulkImportService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java index 37d3cd2e71..d78403a884 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java @@ -87,7 +87,7 @@ public abstract class AbstractBulkImportService