From eafa9e6846f6d5548e8f6d22d323dc22e798d543 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 7 Jan 2026 15:58:41 +0200 Subject: [PATCH 001/224] fix bug validateKeyValueProtos String empty - ok --- .../org/thingsboard/server/common/adaptor/ProtoConverter.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/common/proto/src/main/java/org/thingsboard/server/common/adaptor/ProtoConverter.java b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/ProtoConverter.java index 6dc9387a37..a47caae954 100644 --- a/common/proto/src/main/java/org/thingsboard/server/common/adaptor/ProtoConverter.java +++ b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/ProtoConverter.java @@ -156,11 +156,7 @@ public class ProtoConverter { case BOOLEAN_V: case LONG_V: case DOUBLE_V: - break; case STRING_V: - if (StringUtils.isEmpty(keyValueProto.getStringV())) { - throw new IllegalArgumentException("Value is empty for key: " + key + "!"); - } break; case JSON_V: try { From 493aabebc1bf82ce91d33cb27ab013078ce90285 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 7 Jan 2026 16:28:10 +0200 Subject: [PATCH 002/224] fix bug validateKeyValueProtos String empty - add test --- .../AbstractMqttV5ClientSparkplugTest.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/AbstractMqttV5ClientSparkplugTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/AbstractMqttV5ClientSparkplugTest.java index 6fb08f9eea..4e4d0debf9 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/AbstractMqttV5ClientSparkplugTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/AbstractMqttV5ClientSparkplugTest.java @@ -159,14 +159,17 @@ public abstract class AbstractMqttV5ClientSparkplugTest extends AbstractMqttInte protected List connectClientWithCorrectAccessTokenWithNDEATHCreatedDevices(int cntDevices, long ts) throws Exception { List devices = new ArrayList<>(); clientWithCorrectNodeAccessTokenWithNDEATH(); - MetricDataType metricDataType = Int32; - String key = "Node Metric int32"; + String keyInt = "Node Metric int32"; int valueDeviceInt32 = 1024; - SparkplugBProto.Payload.Metric metric = createMetric(valueDeviceInt32, ts, key, metricDataType, -1L); + SparkplugBProto.Payload.Metric metricInt = createMetric(valueDeviceInt32, ts, keyInt, Int32, -1L); + String keyStringEmpty = "Node Metric String Empty"; + String valueDeviceStringEmpty = ""; + SparkplugBProto.Payload.Metric metricStringEmpty = createMetric(valueDeviceStringEmpty, ts, keyStringEmpty, MetricDataType.String, -1L); SparkplugBProto.Payload.Builder payloadBirthNode = SparkplugBProto.Payload.newBuilder() .setTimestamp(ts) .setSeq(getBdSeqNum()); - payloadBirthNode.addMetrics(metric); + payloadBirthNode.addMetrics(metricInt); + payloadBirthNode.addMetrics(metricStringEmpty); payloadBirthNode.setTimestamp(ts); if (client.isConnected()) { client.publish(TOPIC_ROOT_SPB_V_1_0 + "/" + groupId + "/" + SparkplugMessageType.NBIRTH.name() + "/" + edgeNode, @@ -174,14 +177,14 @@ public abstract class AbstractMqttV5ClientSparkplugTest extends AbstractMqttInte } valueDeviceInt32 = 4024; - metric = createMetric(valueDeviceInt32, ts, metricBirthName_Int32, metricBirthDataType_Int32, -1L); + metricInt = createMetric(valueDeviceInt32, ts, metricBirthName_Int32, metricBirthDataType_Int32, -1L); for (int i = 0; i < cntDevices; i++) { SparkplugBProto.Payload.Builder payloadBirthDevice = SparkplugBProto.Payload.newBuilder() .setTimestamp(ts) .setSeq(getSeqNum()); String deviceName = deviceId + "_" + i; - payloadBirthDevice.addMetrics(metric); + payloadBirthDevice.addMetrics(metricInt); if (client.isConnected()) { client.publish(TOPIC_ROOT_SPB_V_1_0 + "/" + groupId + "/" + SparkplugMessageType.DBIRTH.name() + "/" + edgeNode + "/" + deviceName, payloadBirthDevice.build().toByteArray(), 0, false); From 0468451f3ba02983e540e26f968305dd7a2ed04a Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Mon, 12 Jan 2026 10:52:29 +0200 Subject: [PATCH 003/224] added ws update on telemetry deletion --- .../DefaultTbLocalSubscriptionService.java | 4 +-- .../server/controller/WebsocketApiTest.java | 35 +++++++++++++++++++ .../server/common/data/kv/TsKvEntry.java | 5 +++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java index 51663eea9c..df28da2dad 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java @@ -348,7 +348,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer if (sub.isLatestValues()) { for (TsKvEntry kv : data) { Long stateTs = keyStates.get(kv.getKey()); - if (stateTs == null || kv.getTs() >= stateTs) { + if (stateTs == null || kv.getTs() >= stateTs || kv.isDeletedEntryMarker()) { if (updateData == null) { updateData = new ArrayList<>(); } @@ -362,7 +362,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer for (TsKvEntry kv : data) { Long stateTs = keyStates.get(kv.getKey()); if (stateTs != null) { - if (!sub.isLatestValues() || kv.getTs() >= stateTs) { + if (!sub.isLatestValues() || kv.getTs() >= stateTs || kv.isDeletedEntryMarker()) { if (updateData == null) { updateData = new ArrayList<>(); } diff --git a/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java b/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java index 87ba0ec3e8..e88808ad2a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java @@ -726,6 +726,41 @@ public class WebsocketApiTest extends AbstractControllerTest { Assert.assertNull(msg); } + @Test + public void testShouldSendWsUpdateMessageWhenTelemetryWasDeleted() throws Exception { + long now = System.currentTimeMillis() - 100; + TsKvEntry dataPoint = new BasicTsKvEntry(now, new LongDataEntry("temperature", 42L)); + List tsData = List.of(dataPoint); + sendTelemetry(device, tsData); + + List keys = List.of(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); + EntityDataUpdate update = getWsClient().subscribeLatestUpdate(keys, dtf); + + Assert.assertEquals(1, update.getCmdId()); + PageData pageData = update.getData(); + Assert.assertNotNull(pageData); + Assert.assertEquals(1, pageData.getData().size()); + Assert.assertEquals(device.getId(), pageData.getData().get(0).getEntityId()); + Assert.assertNotNull(pageData.getData().get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("temperature")); + Assert.assertEquals(now, pageData.getData().get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("temperature").getTs()); + Assert.assertEquals("42", pageData.getData().get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("temperature").getValue()); + + // delete telemetry + getWsClient().registerWaitForUpdate(); + doDeleteAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/delete?keys=temperature&deleteAllDataForKeys=true", String.class); + update = getWsClient().parseDataReply(getWsClient().waitForUpdate()); + + Assert.assertEquals(1, update.getCmdId()); + + List listData = update.getUpdate(); + Assert.assertNotNull(listData); + Assert.assertEquals(1, listData.size()); + Assert.assertEquals(device.getId(), listData.get(0).getEntityId()); + Assert.assertNotNull(listData.get(0).getLatest().get(EntityKeyType.TIME_SERIES)); + TsValue tsValue = listData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("temperature"); + Assert.assertEquals(new TsValue(0, ""), tsValue); + } + @Test public void testEntityDataLatestAttrWsCmd() throws Exception { long now = System.currentTimeMillis(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/TsKvEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/TsKvEntry.java index cb4092f433..eca0609704 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/TsKvEntry.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/TsKvEntry.java @@ -37,4 +37,9 @@ public interface TsKvEntry extends KvEntry, HasVersion { return new TsValue(getTs(), getValueAsString()); } + @JsonIgnore + default boolean isDeletedEntryMarker() { + return getTs() == 0 && (getValue() == null || getValueAsString().isEmpty()); + } + } From 97d68dda90433a441eac439bb1d377a9b58bd978 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Wed, 28 Jan 2026 12:00:25 +0200 Subject: [PATCH 004/224] Ota package unlink data object --- .../housekeeper/HousekeeperServiceTest.java | 10 ++-- .../AlarmsDeletionHousekeeperTask.java | 4 ++ .../AlarmsUnassignHousekeeperTask.java | 4 ++ .../EntitiesDeletionHousekeeperTask.java | 4 ++ .../data/housekeeper/HousekeeperTask.java | 4 ++ .../LatestTsDeletionHousekeeperTask.java | 5 ++ ...TenantEntitiesDeletionHousekeeperTask.java | 5 ++ .../TsHistoryDeletionHousekeeperTask.java | 5 ++ .../server/dao/ota/BaseOtaPackageService.java | 48 ++++++++++++------- .../server/dao/ota/OtaPackageDao.java | 7 ++- .../server/dao/sql/ota/JpaOtaPackageDao.java | 10 ++++ .../dao/sql/ota/OtaPackageRepository.java | 9 +++- .../dao/service/OtaPackageServiceTest.java | 37 ++++++++++++++ 13 files changed, 128 insertions(+), 24 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java b/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java index cf37afad5c..0c12767b60 100644 --- a/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java @@ -23,8 +23,8 @@ import org.junit.Test; import org.mockito.ArgumentMatcher; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; @@ -127,10 +127,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. }) public class HousekeeperServiceTest extends AbstractControllerTest { - @SpyBean + @MockitoSpyBean private HousekeeperService housekeeperService; - @SpyBean + @MockitoSpyBean private HousekeeperReprocessingService housekeeperReprocessingService; + @MockitoSpyBean + private TsHistoryDeletionTaskProcessor tsHistoryDeletionTaskProcessor; @Autowired private EventService eventService; @Autowired @@ -153,8 +155,6 @@ public class HousekeeperServiceTest extends AbstractControllerTest { private CustomerService customerService; @Autowired private DashboardService dashboardService; - @SpyBean - private TsHistoryDeletionTaskProcessor tsHistoryDeletionTaskProcessor; private TenantId tenantId; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/AlarmsDeletionHousekeeperTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/AlarmsDeletionHousekeeperTask.java index dea590295e..d66ec046de 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/AlarmsDeletionHousekeeperTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/AlarmsDeletionHousekeeperTask.java @@ -23,6 +23,7 @@ import lombok.ToString; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import java.io.Serial; import java.util.List; import java.util.UUID; @@ -32,6 +33,9 @@ import java.util.UUID; @NoArgsConstructor(access = AccessLevel.PROTECTED) public class AlarmsDeletionHousekeeperTask extends HousekeeperTask { + @Serial + private static final long serialVersionUID = 9214680001573764374L; + private List alarms; public AlarmsDeletionHousekeeperTask(TenantId tenantId, EntityId entityId) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/AlarmsUnassignHousekeeperTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/AlarmsUnassignHousekeeperTask.java index 0313190056..445850d387 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/AlarmsUnassignHousekeeperTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/AlarmsUnassignHousekeeperTask.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import java.io.Serial; import java.util.List; import java.util.UUID; @@ -33,6 +34,9 @@ import java.util.UUID; @NoArgsConstructor(access = AccessLevel.PROTECTED) public class AlarmsUnassignHousekeeperTask extends HousekeeperTask { + @Serial + private static final long serialVersionUID = 9156667024462937756L; + private String userTitle; private List alarms; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/EntitiesDeletionHousekeeperTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/EntitiesDeletionHousekeeperTask.java index fe25a98a1d..c1a233b1c4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/EntitiesDeletionHousekeeperTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/EntitiesDeletionHousekeeperTask.java @@ -23,6 +23,7 @@ import lombok.ToString; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; +import java.io.Serial; import java.util.List; import java.util.UUID; @@ -32,6 +33,9 @@ import java.util.UUID; @NoArgsConstructor public class EntitiesDeletionHousekeeperTask extends HousekeeperTask { + @Serial + private static final long serialVersionUID = 9009068831061529286L; + private EntityType entityType; private List entities; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTask.java index 875ef2765f..2df7cf4dd4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTask.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import java.io.Serial; import java.io.Serializable; @JsonIgnoreProperties(ignoreUnknown = true) @@ -45,6 +46,9 @@ import java.io.Serializable; @NoArgsConstructor(access = AccessLevel.PROTECTED) public class HousekeeperTask implements Serializable { + @Serial + private static final long serialVersionUID = -2585974110832225152L; + private TenantId tenantId; private EntityId entityId; private HousekeeperTaskType taskType; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/LatestTsDeletionHousekeeperTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/LatestTsDeletionHousekeeperTask.java index cd3e94e5c6..931c2931cb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/LatestTsDeletionHousekeeperTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/LatestTsDeletionHousekeeperTask.java @@ -23,12 +23,17 @@ import lombok.ToString; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import java.io.Serial; + @Data @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class LatestTsDeletionHousekeeperTask extends HousekeeperTask { + @Serial + private static final long serialVersionUID = 5193191938513490138L; + private String key; public LatestTsDeletionHousekeeperTask(TenantId tenantId, EntityId entityId, String key) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/TenantEntitiesDeletionHousekeeperTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/TenantEntitiesDeletionHousekeeperTask.java index 443d929d8b..be7ff6f7ec 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/TenantEntitiesDeletionHousekeeperTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/TenantEntitiesDeletionHousekeeperTask.java @@ -23,12 +23,17 @@ import lombok.ToString; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; +import java.io.Serial; + @Data @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) @NoArgsConstructor public class TenantEntitiesDeletionHousekeeperTask extends HousekeeperTask { + @Serial + private static final long serialVersionUID = -8033108795318393447L; + private EntityType entityType; public TenantEntitiesDeletionHousekeeperTask(TenantId tenantId, EntityType entityType) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/TsHistoryDeletionHousekeeperTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/TsHistoryDeletionHousekeeperTask.java index d9315f0ff4..b520899ca4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/TsHistoryDeletionHousekeeperTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/TsHistoryDeletionHousekeeperTask.java @@ -23,12 +23,17 @@ import lombok.ToString; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import java.io.Serial; + @Data @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class TsHistoryDeletionHousekeeperTask extends HousekeeperTask { + @Serial + private static final long serialVersionUID = 4573851542705079043L; + private String key; public TsHistoryDeletionHousekeeperTask(TenantId tenantId, EntityId entityId, String key) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java index 95515bd15e..aac4157706 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java @@ -196,7 +196,9 @@ public class BaseOtaPackageService extends AbstractCachedEntityService INCORRECT_OTA_PACKAGE_ID + id); try { + Long oid = otaPackageDao.getDataOidById(otaPackageId.getId()); otaPackageDao.removeById(tenantId, otaPackageId.getId()); + unlinkDataIfPresent(tenantId, otaPackageId, oid); publishEvictEvent(new OtaPackageCacheEvictEvent(otaPackageId)); eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(otaPackageId).build()); } catch (Exception t) { @@ -215,6 +217,20 @@ public class BaseOtaPackageService extends AbstractCachedEntityService tenantOtaPackageRemover = - new PaginatedRemover<>() { - - @Override - protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { - return otaPackageInfoDao.findOtaPackageInfoByTenantId(id, pageLink); - } + private final PaginatedRemover tenantOtaPackageRemover = new PaginatedRemover<>() { + @Override + protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { + return otaPackageInfoDao.findOtaPackageInfoByTenantId(id, pageLink); + } - @Override - protected void removeEntity(TenantId tenantId, OtaPackageInfo entity) { - deleteOtaPackage(tenantId, entity.getId()); - } - }; + @Override + protected void removeEntity(TenantId tenantId, OtaPackageInfo entity) { + deleteOtaPackage(tenantId, entity.getId()); + } + }; @Override public Optional> findEntity(TenantId tenantId, EntityId entityId) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java index c11a13cbe1..875aaea4ed 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java @@ -18,15 +18,20 @@ package org.thingsboard.server.dao.ota; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.dao.TenantEntityWithDataDao; +import java.util.UUID; + public interface OtaPackageDao extends Dao, TenantEntityWithDataDao, ExportableEntityDao { Long sumDataSizeByTenantId(TenantId tenantId); OtaPackage findOtaPackageByTenantIdAndTitleAndVersion(TenantId tenantId, String title, String version); + Long getDataOidById(UUID id); + + Integer unlinkLargeObject(Long dataOid); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java index d67110d53d..76bf5ca26e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java @@ -77,6 +77,16 @@ public class JpaOtaPackageDao extends JpaAbstractDao findIdsByTenantId(@Param("tenantId") UUID tenantId, Pageable pageable); + @Query(value = "SELECT data FROM ota_package WHERE id = :id AND data IS NOT NULL", nativeQuery = true) + Long getDataOidById(@Param("id") UUID id); + + @Transactional + @Query(value = "SELECT lo_unlink(:oid)", nativeQuery = true) + Integer unlinkLargeObject(@Param("oid") Long oid); + } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/OtaPackageServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/OtaPackageServiceTest.java index a38499c82c..e79a7bad07 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/OtaPackageServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/OtaPackageServiceTest.java @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileCon import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.ota.OtaPackageDao; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantProfileService; @@ -77,6 +78,8 @@ public class OtaPackageServiceTest extends AbstractServiceTest { @Autowired TenantProfileService tenantProfileService; @Autowired + OtaPackageDao otaPackageDao; + @Autowired TbTenantProfileCache tenantProfileCache; @Before @@ -533,6 +536,39 @@ public class OtaPackageServiceTest extends AbstractServiceTest { Assert.assertNull(foundFirmware); } + @Test + public void testDeleteOtaPackageWithoutData() { + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); + firmwareInfo.setTenantId(tenantId); + firmwareInfo.setDeviceProfileId(deviceProfileId); + firmwareInfo.setType(FIRMWARE); + firmwareInfo.setTitle(TITLE); + firmwareInfo.setVersion(VERSION); + OtaPackageInfo savedFirmwareInfo = otaPackageService.saveOtaPackageInfo(firmwareInfo, false); + + Assert.assertNotNull(savedFirmwareInfo); + Assert.assertNotNull(savedFirmwareInfo.getId()); + + // Should not throw NPE when deleting package without data (OID is null) + otaPackageService.deleteOtaPackage(tenantId, savedFirmwareInfo.getId()); + + OtaPackageInfo foundFirmware = otaPackageService.findOtaPackageInfoById(tenantId, savedFirmwareInfo.getId()); + Assert.assertNull(foundFirmware); + } + + @Test + public void testDeleteOtaPackageUnlinksLargeObject() { + OtaPackage savedFirmware = createAndSaveFirmware(tenantId, VERSION); + + Long oid = otaPackageDao.getDataOidById(savedFirmware.getId().getId()); + Assert.assertNotNull(oid); + + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); + + // Verify the large object was unlinked - PostgreSQL throws an exception when the object doesn't exist + assertThatThrownBy(() -> otaPackageDao.unlinkLargeObject(oid)).hasMessageContaining("large object " + oid + " does not exist"); + } + @Test public void testFindTenantFirmwaresByTenantId() { List firmwares = new ArrayList<>(); @@ -726,4 +762,5 @@ public class OtaPackageServiceTest extends AbstractServiceTest { firmware.setDataSize(DATA_SIZE); return firmware; } + } From cc6af84a928711c5d1d9f20e03e1661419969b7d Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Wed, 28 Jan 2026 12:04:42 +0200 Subject: [PATCH 005/224] Small revert --- .../server/dao/ota/BaseOtaPackageService.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java index aac4157706..54577ff9eb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java @@ -196,7 +196,7 @@ public class BaseOtaPackageService extends AbstractCachedEntityService INCORRECT_OTA_PACKAGE_ID + id); try { - Long oid = otaPackageDao.getDataOidById(otaPackageId.getId()); + Long oid = getDataOidById(tenantId, otaPackageId); otaPackageDao.removeById(tenantId, otaPackageId.getId()); unlinkDataIfPresent(tenantId, otaPackageId, oid); publishEvictEvent(new OtaPackageCacheEvictEvent(otaPackageId)); @@ -217,6 +217,16 @@ public class BaseOtaPackageService extends AbstractCachedEntityService Date: Thu, 29 Jan 2026 10:17:02 +0200 Subject: [PATCH 006/224] fixed SwaggerConfiguration to meet open-api standard, fixed overloading apis --- .../server/config/SwaggerConfiguration.java | 2 +- .../server/controller/AdminController.java | 2 +- .../server/controller/AssetController.java | 38 +++-- .../controller/AssetProfileController.java | 7 +- .../server/controller/AuditLogController.java | 15 +- .../server/controller/AuthController.java | 4 +- .../ComponentDescriptorController.java | 4 +- .../server/controller/CustomerController.java | 7 +- .../controller/DashboardController.java | 12 +- .../server/controller/DeviceController.java | 39 +++-- .../controller/DeviceProfileController.java | 7 +- .../server/controller/EdgeController.java | 40 +++-- .../EntitiesVersionControlController.java | 8 +- .../controller/EntityRelationController.java | 154 ++++++++++++------ .../controller/EntityViewController.java | 34 ++-- .../NotificationTargetController.java | 37 ++++- .../server/controller/OAuth2Controller.java | 15 +- .../server/controller/QueueController.java | 8 +- .../controller/QueueStatsController.java | 15 +- .../controller/RuleChainController.java | 6 +- .../controller/TbResourceController.java | 13 +- .../controller/TelemetryController.java | 43 +++-- .../server/controller/TenantController.java | 7 +- .../controller/TenantProfileController.java | 14 +- .../server/controller/UserController.java | 12 +- .../controller/WidgetTypeController.java | 40 ++--- .../controller/WidgetsBundleController.java | 16 +- 27 files changed, 364 insertions(+), 235 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java index 70259a15ef..3460eb19d9 100644 --- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java @@ -240,7 +240,7 @@ public class SwaggerConfiguration { return GroupedOpenApi.builder() .group(groupName) .pathsToMatch(apiPath) - .addRouterOperationCustomizer(routerOperationCustomizer(localSpringDocParameterNameDiscoverer)) + //.addRouterOperationCustomizer(routerOperationCustomizer(localSpringDocParameterNameDiscoverer)) // removed to meet open-api standard .addOperationCustomizer(operationCustomizer()) .addOpenApiCustomizer(customOpenApiCustomizer()) .build(); diff --git a/application/src/main/java/org/thingsboard/server/controller/AdminController.java b/application/src/main/java/org/thingsboard/server/controller/AdminController.java index 0b04a6939f..1c76f787ea 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -441,7 +441,7 @@ public class AdminController extends BaseController { .build() + "\""; } - @GetMapping(value = "/mail/oauth2/code", params = {"code", "state"}) + @GetMapping(value = "/mail/oauth2/code") public void codeProcessingUrl( @RequestParam(value = "code") String code, @RequestParam(value = "state") String state, HttpServletRequest request, HttpServletResponse response) throws ThingsboardException, IOException { diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index 0030896582..ce9b5fbc78 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/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; @@ -205,8 +207,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 getTenantAssets( @Parameter(description = PAGE_SIZE_DESCRIPTION) @RequestParam int pageSize, @@ -233,8 +234,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 getTenantAssetInfos( @Parameter(description = PAGE_SIZE_DESCRIPTION) @RequestParam int pageSize, @@ -262,25 +262,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 getCustomerAssets( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION) @PathVariable("customerId") String strCustomerId, @@ -312,8 +317,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 getCustomerAssetInfos( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION) @PathVariable("customerId") String strCustomerId, @@ -349,8 +353,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 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 { @@ -457,8 +460,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 getEdgeAssets( @Parameter(description = EDGE_ID_PARAM_DESCRIPTION) @PathVariable(EDGE_ID) String strEdgeId, diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java b/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java index 0233a488d6..0f0d534a9e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java @@ -21,6 +21,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.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -170,8 +171,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 getAssetProfiles( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -191,8 +191,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 getAssetProfileInfos( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, diff --git a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java index e22c0aa156..da7d2c4583 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java +++ b/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; @@ -73,8 +72,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 getAuditLogsByCustomerId( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION) @PathVariable("customerId") String strCustomerId, @@ -106,8 +104,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 getAuditLogsByUserId( @Parameter(description = USER_ID_PARAM_DESCRIPTION) @PathVariable("userId") String strUserId, @@ -140,8 +137,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 + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('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 getAuditLogsByEntityId( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String strEntityType, @@ -175,8 +171,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 + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/audit/logs", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/audit/logs") public PageData getAuditLogs( @Parameter(description = PAGE_SIZE_DESCRIPTION) @RequestParam int pageSize, diff --git a/application/src/main/java/org/thingsboard/server/controller/AuthController.java b/application/src/main/java/org/thingsboard/server/controller/AuthController.java index 166fe312f5..530041ce89 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java @@ -132,7 +132,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) { @@ -172,7 +172,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) { diff --git a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java index 55dbb8fe04..91569261b7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java +++ b/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 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, diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java index a29d8ab4c8..87948de81f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -22,6 +22,7 @@ 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.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -153,8 +154,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 getCustomers( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -174,8 +174,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 { diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index 16f9e8f299..0f8a1a465b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -331,8 +331,7 @@ public class DashboardController extends BaseController { notes = "Returns a page of dashboard info objects owned by tenant. " + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenant/{tenantId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenant/{tenantId}/dashboards") public PageData getTenantDashboards( @Parameter(description = TENANT_ID_PARAM_DESCRIPTION, required = true) @PathVariable(TENANT_ID) String strTenantId, @@ -356,8 +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')") - @RequestMapping(value = "/tenant/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenant/dashboards") public PageData getTenantDashboards( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -384,8 +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')") - @RequestMapping(value = "/customer/{customerId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/customer/{customerId}/dashboards") public PageData getCustomerDashboards( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true) @PathVariable(CUSTOMER_ID) String strCustomerId, @@ -583,8 +580,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')") - @RequestMapping(value = "/edge/{edgeId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/edge/{edgeId}/dashboards") public PageData getEdgeDashboards( @Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(EDGE_ID) String strEdgeId, diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 03fc5552c9..a89679b24f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/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; @@ -331,8 +333,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 getTenantDevices( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -359,8 +360,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 getTenantDeviceInfos( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -392,25 +392,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 getCustomerDevices( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true) @PathVariable(CUSTOMER_ID) String strCustomerId, @@ -442,8 +448,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 getCustomerDeviceInfos( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true) @PathVariable("customerId") String strCustomerId, @@ -483,8 +488,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 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 { @@ -711,8 +715,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 getEdgeDevices( @Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(EDGE_ID) String strEdgeId, diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java index 7cfb9dd274..b8215860a9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; 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.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -224,8 +225,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 getDeviceProfiles( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -245,8 +245,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 getDeviceProfileInfos( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java index 784153b650..0c406e0d3d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/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; @@ -53,7 +54,6 @@ import org.thingsboard.server.common.data.rule.RuleChain; 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.common.msg.edge.FromEdgeSyncResponse; -import org.thingsboard.server.common.msg.edge.ToEdgeSyncRequest; import org.thingsboard.server.config.annotations.ApiOperation; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; @@ -71,7 +71,6 @@ import org.thingsboard.server.service.security.permission.Resource; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; @@ -279,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 getTenantEdgeInfos( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -302,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) @@ -336,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 getCustomerEdges( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION) @PathVariable("customerId") String strCustomerId, @@ -371,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 getCustomerEdgeInfos( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION) @PathVariable("customerId") String strCustomerId, @@ -402,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 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(); @@ -427,6 +431,16 @@ public class EdgeController extends BaseController { return checkNotNull(edges); } + @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 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 (findByQuery)", notes = "Returns all edges that are related to the specific entity. " + "The entity id, relation type, edge types, depth of the search, and other query parameters defined using complex 'EdgeSearchQuery' object. " + diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java index 10f4cd2f51..b8ec907c5d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java @@ -234,7 +234,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> 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 +263,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> listEntityTypeVersions(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @PathVariable EntityType entityType, @Parameter(description = BRANCH_PARAM_DESCRIPTION, required = true) @@ -288,7 +288,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> listVersions(@Parameter(description = BRANCH_PARAM_DESCRIPTION, required = true) @RequestParam String branch, @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @@ -355,7 +355,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 compareEntityDataToVersion(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @PathVariable EntityType entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java index a87db28e92..3faeef3ea8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -15,16 +15,19 @@ */ 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.http.HttpStatus; 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; 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.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; @@ -76,8 +79,7 @@ public class EntityRelationController extends BaseController { "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')") - @RequestMapping(value = "/relation", method = RequestMethod.POST) - @ResponseStatus(value = HttpStatus.OK) + @PostMapping(value = "/relation") public void saveRelation(@Parameter(description = "A JSON value representing the relation.", required = true) @RequestBody EntityRelation relation) throws ThingsboardException { doSave(relation); @@ -88,8 +90,7 @@ public class EntityRelationController extends BaseController { "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')") - @RequestMapping(value = "/v2/relation", method = RequestMethod.POST) - @ResponseStatus(value = HttpStatus.OK) + @PostMapping(value = "/v2/relation") public EntityRelation saveRelationV2(@Parameter(description = "A JSON value representing the relation.", required = true) @RequestBody EntityRelation relation) throws ThingsboardException { return doSave(relation); @@ -109,8 +110,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')") - @RequestMapping(value = "/relation", method = RequestMethod.DELETE, params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE}) - @ResponseStatus(value = HttpStatus.OK) + @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, @@ -123,8 +123,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Delete Relation (deleteRelationV2)", notes = "Deletes a relation between two entities in the platform. " + SECURITY_CHECKS_ENTITIES_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/v2/relation", method = RequestMethod.DELETE, params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE}) - @ResponseStatus(value = HttpStatus.OK) + @DeleteMapping(value = "/v2/relation") 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, @@ -154,8 +153,7 @@ public class EntityRelationController extends BaseController { 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')") - @RequestMapping(value = "/relations", method = RequestMethod.DELETE, params = {"entityId", "entityType"}) - @ResponseStatus(value = HttpStatus.OK) + @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); @@ -168,8 +166,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')") - @RequestMapping(value = "/relation", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE}) - @ResponseBody + @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, @@ -189,15 +186,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')") - @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE}) - @ResponseBody - public List 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) + @GetMapping(value = "/relations", params = {FROM_ID, FROM_TYPE}) + public List 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); @@ -207,12 +200,21 @@ 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 (findByFromV2)", + 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')") - @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE}) - @ResponseBody + @GetMapping(value = "/relations/from/{fromType}/{fromId}") + public List findByFromV2(@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 findInfoByFrom(@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) @@ -225,16 +227,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 (findInfoByFromV2)", + 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')") - @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE, RELATION_TYPE}) - @ResponseBody - public List 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) + @GetMapping(value = "/relations/info/from/{fromType}/{fromId}") + public List findInfoByFromV2(@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 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); @@ -245,12 +255,22 @@ 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 (findByFromV2)", + 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')") - @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {TO_ID, TO_TYPE}) - @ResponseBody + @GetMapping(value = "/relations/from/{fromType}/{fromId}/{relationType}") + public List findByFromV2(@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 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, @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @@ -263,12 +283,21 @@ 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 (findByToV2)", + 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')") - @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {TO_ID, TO_TYPE}) - @ResponseBody + @GetMapping(value = "/relations/to/{toType}/{toId}") + public List findByToV2(@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 findInfoByTo(@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, @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @@ -281,12 +310,21 @@ 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 (findInfoByToV2)", + 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')") - @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {TO_ID, TO_TYPE, RELATION_TYPE}) - @ResponseBody + @GetMapping(value = "/relations/info/to/{toType}/{toId}") + public List findInfoByToV2(@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 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, @Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType, @@ -301,13 +339,25 @@ public class EntityRelationController extends BaseController { return checkNotNull(filterRelationsByReadPermission(relationService.findByToAndType(getTenantId(), entityId, strRelationType, typeGroup))); } + @ApiOperation(value = "Get List of Relations (findByToV2)", + 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 findByToV2(@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 (findByQuery)", notes = "Returns all entities that are related to the specific entity. " + "The entity id, relation type, entity types, depth of the search, and other query parameters defined using complex 'EntityRelationsQuery' object. " + "See 'Model' tab of the Parameters for more info.") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/relations", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/relations") public List findByQuery(@Parameter(description = "A JSON value representing the entity relations query object.", required = true) @RequestBody EntityRelationsQuery query) throws ThingsboardException, ExecutionException, InterruptedException { checkNotNull(query); diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java index 15bda4b87a..cdf030135d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -16,12 +16,14 @@ 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.Schema; 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.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -155,18 +157,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')") - @RequestMapping(value = "/tenant/entityViews", params = {"entityViewName"}, method = RequestMethod.GET) - @ResponseBody + @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 (getTenantEntityViewV2)", + 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 getTenantEntityViewV2( + @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')") @@ -213,8 +222,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')") - @RequestMapping(value = "/customer/{customerId}/entityViews", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/customer/{customerId}/entityViews") public PageData getCustomerEntityViews( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true) @PathVariable(CUSTOMER_ID) String strCustomerId, @@ -246,8 +254,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')") - @RequestMapping(value = "/customer/{customerId}/entityViewInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/customer/{customerId}/entityViewInfos") public PageData getCustomerEntityViewInfos( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true) @PathVariable(CUSTOMER_ID) String strCustomerId, @@ -279,8 +286,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')") - @RequestMapping(value = "/tenant/entityViews", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenant/entityViews") public PageData getTenantEntityViews( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -308,8 +314,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')") - @RequestMapping(value = "/tenant/entityViewInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenant/entityViewInfos") public PageData getTenantEntityViewInfos( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -436,8 +441,7 @@ public class EntityViewController extends BaseController { } @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/edge/{edgeId}/entityViews", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/edge/{edgeId}/entityViews") public PageData getEdgeEntityViews( @PathVariable(EDGE_ID) String strEdgeId, @RequestParam int pageSize, diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java index 72c7f15a3b..4775b78e8b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java +++ b/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,9 +154,7 @@ 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 getNotificationTargetsByIds(@Parameter(description = "Comma-separated list of uuids representing targets ids", array = @ArraySchema(schema = @Schema(type = "string")), required = true) @@ -166,6 +165,17 @@ public class NotificationTargetController extends BaseController { return notificationTargetService.findNotificationTargetsByTenantIdAndIds(user.getTenantId(), targetsIds); } + @ApiOperation(value = "Get notification targets by ids (getNotificationTargetsByIdsV2)", + 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 getNotificationTargetsByIdsV2(@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 getNotificationTargetsByIds(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,10 +198,7 @@ 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 getNotificationTargetsBySupportedNotificationType(@RequestParam int pageSize, @@ -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 (getNotificationTargetsBySupportedNotificationTypeV2)", + 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 getNotificationTargetsBySupportedNotificationTypeV2(@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 getNotificationTargetsBySupportedNotificationType(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." + diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index 71b8ac27a9..27aa6e557b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/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,17 +133,25 @@ public class OAuth2Controller extends BaseController { return oAuth2ClientService.findOAuth2ClientInfosByTenantId(getTenantId(), pageLink); } - @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) + @Hidden @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/oauth2/client/infos", params = {"clientIds"}) public List 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 oAuth2ClientIds = getOAuth2ClientIds(clientIds); return oAuth2ClientService.findOAuth2ClientInfosByIds(getTenantId(), oAuth2ClientIds); } + @ApiOperation(value = "Get OAuth2 Client infos By Ids (findTenantOAuth2ClientInfosByIdsV2)", + 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/list") + public List findTenantOAuth2ClientInfosByIdsV2( + @Parameter(description = "A list of oauth2 ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true) + @RequestParam("clientIds") UUID[] clientIds) throws ThingsboardException { + return findTenantOAuth2ClientInfosByIds(clientIds); + } + @ApiOperation(value = "Get OAuth2 Client by id (getOAuth2ClientById)", notes = SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/oauth2/client/{id}") diff --git a/application/src/main/java/org/thingsboard/server/controller/QueueController.java b/application/src/main/java/org/thingsboard/server/controller/QueueController.java index 2d584a6e27..dd2cc19fd6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/QueueController.java +++ b/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 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)) diff --git a/application/src/main/java/org/thingsboard/server/controller/QueueStatsController.java b/application/src/main/java/org/thingsboard/server/controller/QueueStatsController.java index 1ccc35b875..5412e5f70d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/QueueStatsController.java +++ b/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 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 { checkArrayParameter("queueStatsIds", strQueueStatsIds); List queueStatsIds = new ArrayList<>(); @@ -99,4 +98,14 @@ public class QueueStatsController extends BaseController { } return queueStatsService.findQueueStatsByIds(getTenantId(), queueStatsIds); } + + @ApiOperation(value = "Get QueueStats By Ids (getQueueStatsByIdsV2)", + notes = "Fetch the Queue stats objects based on the provided ids. ") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @GetMapping(value = "/queueStats/list") + public List getQueueStatsByIdsV2( + @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 getQueueStatsByIds(strQueueStatsIds); + } } diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index 12c26dedb3..7a27b7d889 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -278,7 +278,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 getRuleChains( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -406,7 +406,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 { @@ -502,7 +502,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 getEdgeRuleChains( @Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(EDGE_ID) String strEdgeId, diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index 14c0a3dcb2..73fe3be137 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/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; @@ -266,11 +267,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 getSystemOrTenantResourcesByIds( - @Parameter(description = "A list of resource ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string"))) @RequestParam("resourceIds") Set resourceUuids) throws ThingsboardException { SecurityUser user = getCurrentUser(); List resourceIds = new ArrayList<>(); @@ -280,6 +280,15 @@ public class TbResourceController extends BaseController { return resourceService.findSystemOrTenantResourcesByIds(user.getTenantId(), resourceIds); } + @ApiOperation(value = "Get Resource Infos by ids (getSystemOrTenantResourcesByIdsV2)") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @GetMapping(value = "/resource/list", params = {"resourceIds"}) + public List getSystemOrTenantResourcesByIdsV2( + @Parameter(description = "A list of resource ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string"))) + @RequestParam("resourceIds") Set resourceUuids) throws ThingsboardException { + return getSystemOrTenantResourcesByIds(resourceUuids); + } + @ApiOperation(value = "Get All Resource Infos (getAllResources)", notes = "Returns a page of Resource Info objects owned by tenant. " + PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH) diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java index 8ff71494df..03017aeb86 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -24,6 +24,7 @@ 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.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; @@ -36,6 +37,7 @@ import org.springframework.beans.factory.annotation.Autowired; 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; @@ -263,8 +265,7 @@ public class TelemetryController extends BaseController { + MARKDOWN_CODE_BLOCK_END + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/{entityType}/{entityId}/values/timeseries") public DeferredResult getLatestTimeseries( @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, @@ -276,7 +277,32 @@ public class TelemetryController extends BaseController { (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); } - @ApiOperation(value = "Get time series data (getTimeseries)", + @Hidden + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(value = "/{entityType}/{entityId}/values/timeseries", params = {"keys", "startTs", "endTs"}) + public DeferredResult getTimeseries( + @PathVariable("entityType") String entityType, + @PathVariable("entityId") String entityIdStr, + @RequestParam(name = "keys") String keys, + @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, + @Parameter(description = "An integer value that represents a max number of time series data points to fetch." + + " This parameter is used only in the case if 'agg' parameter is set to 'NONE'.", schema = @Schema(defaultValue = "100")) + @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) throws ThingsboardException { + DeferredResult response = new DeferredResult<>(); + Futures.addCallback(tbTelemetryService.getTimeseries(EntityIdFactory.getByTypeAndId(entityType, entityIdStr), toKeysList(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. " + @@ -286,9 +312,8 @@ public class TelemetryController extends BaseController { + MARKDOWN_CODE_BLOCK_END + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET, params = {"keys", "startTs", "endTs"}) - @ResponseBody - public DeferredResult getTimeseries( + @GetMapping(value = "/{entityType}/{entityId}/values/timeseries/history") + public DeferredResult 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, required = true) @RequestParam(name = "keys") String keys, @@ -313,11 +338,7 @@ 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) throws ThingsboardException { - DeferredResult response = new DeferredResult<>(); - Futures.addCallback(tbTelemetryService.getTimeseries(EntityIdFactory.getByTypeAndId(entityType, entityIdStr), toKeysList(keys), startTs, endTs, - intervalType, interval, timeZone, limit, Aggregation.valueOf(aggStr), orderBy, useStrictDataTypes, getCurrentUser()), - getTsKvListCallback(response, useStrictDataTypes), MoreExecutors.directExecutor()); - return response; + return getTimeseries(entityType, entityIdStr, keys, startTs, endTs, intervalType, interval, timeZone, limit, aggStr, orderBy, useStrictDataTypes); } @ApiOperation(value = "Save device attributes (saveDeviceAttributes)", diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantController.java b/application/src/main/java/org/thingsboard/server/controller/TenantController.java index 55bd76fdcb..64cf78e219 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantController.java @@ -21,6 +21,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.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -128,8 +129,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')") - @RequestMapping(value = "/tenants", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenants") public PageData getTenants( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -148,8 +148,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')") - @RequestMapping(value = "/tenantInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenantInfos") public PageData getTenantInfos( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java index 7d2f84e827..5644175e1f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java +++ b/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; @@ -240,8 +241,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')") - @RequestMapping(value = "/tenantProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenantProfileInfos") public PageData getTenantProfileInfos( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -257,12 +257,18 @@ public class TenantProfileController extends BaseController { return checkNotNull(tenantProfileService.findTenantProfileInfos(getTenantId(), pageLink)); } + @Hidden @GetMapping(value = "/tenantProfiles", params = {"ids"}) @PreAuthorize("hasAuthority('SYS_ADMIN')") - public List getTenantProfilesByIds(@Parameter(description = "Comma-separated list of tenant profile ids", array = @ArraySchema(schema = @Schema(type = "string"))) - @RequestParam("ids") UUID[] ids) { + public List getTenantProfilesByIds(@RequestParam("ids") UUID[] ids) { return tenantProfileService.findTenantProfilesByIds(TenantId.SYS_TENANT_ID, ids); } + @GetMapping(value = "/tenantProfiles/list") + @PreAuthorize("hasAuthority('SYS_ADMIN')") + public List getTenantProfileList(@Parameter(description = "Comma-separated list of tenant profile ids", array = @ArraySchema(schema = @Schema(type = "string"))) + @RequestParam("ids") UUID[] ids) { + return getTenantProfilesByIds(ids); + } } diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java index be0528734a..6de2035940 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -276,8 +276,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')") - @RequestMapping(value = "/users", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/users") public PageData getUsers( @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -339,8 +338,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')") - @RequestMapping(value = "/tenant/{tenantId}/users", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/tenant/{tenantId}/users") public PageData getTenantAdmins( @Parameter(description = TENANT_ID_PARAM_DESCRIPTION, required = true) @PathVariable(TENANT_ID) String strTenantId, @@ -363,8 +361,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')") - @RequestMapping(value = "/customer/{customerId}/users", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/customer/{customerId}/users") public PageData getCustomerUsers( @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true) @PathVariable(CUSTOMER_ID) String strCustomerId, @@ -412,8 +409,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')") - @RequestMapping(value = "/users/assign/{alarmId}", params = {"pageSize", "page"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/users/assign/{alarmId}") public PageData getUsersForAssign( @Parameter(description = ALARM_ID_PARAM_DESCRIPTION, required = true) @PathVariable("alarmId") String strAlarmId, diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java index bbcf370158..05adf71a3c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java +++ b/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; @@ -212,11 +213,9 @@ 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')") - @RequestMapping(value = "/widgetTypes", params = {"isSystem", "bundleAlias"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/widgetTypes", params = {"isSystem", "bundleAlias"}) @Deprecated public List getBundleWidgetTypesByBundleAlias( @Parameter(description = "System or Tenant", required = true) @@ -236,8 +235,7 @@ public class WidgetTypeController extends AutoCommitController { @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')") - @RequestMapping(value = "/widgetTypes", params = {"widgetsBundleId"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/widgetTypes", params = {"widgetsBundleId"}) public List getBundleWidgetTypes( @Parameter(description = "Widget Bundle Id", required = true) @RequestParam("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException { @@ -245,11 +243,9 @@ public class WidgetTypeController extends AutoCommitController { 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) + @Hidden @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/widgetTypesDetails", params = {"isSystem", "bundleAlias"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/widgetTypesDetails", params = {"isSystem", "bundleAlias"}) @Deprecated public List getBundleWidgetTypesDetailsByBundleAlias( @Parameter(description = "System or Tenant", required = true) @@ -266,11 +262,9 @@ public class WidgetTypeController extends AutoCommitController { return checkNotNull(widgetTypeService.findWidgetTypesDetailsByWidgetsBundleId(getTenantId(), widgetsBundle.getId())); } - @ApiOperation(value = "Get all Widget types details for specified Bundle (getBundleWidgetTypesDetails)", - notes = "Returns an array of Widget Type Details objects that belong to specified Widget Bundle." + WIDGET_TYPE_DETAILS_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) + @Hidden @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/widgetTypesDetails", params = {"widgetsBundleId"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/widgetTypesDetails", params = {"widgetsBundleId"}) public List getBundleWidgetTypesDetails( @Parameter(description = "Widget Bundle Id", required = true) @RequestParam("widgetsBundleId") String strWidgetsBundleId, @@ -291,8 +285,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')") - @RequestMapping(value = "/widgetTypeFqns", params = {"widgetsBundleId"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/widgetTypeFqns") public List getBundleWidgetTypeFqns( @Parameter(description = "Widget Bundle Id", required = true) @RequestParam("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException { @@ -300,11 +293,9 @@ 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')") - @RequestMapping(value = "/widgetTypesInfos", params = {"isSystem", "bundleAlias"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/widgetTypesInfos", params = {"isSystem", "bundleAlias"}) @Deprecated public List getBundleWidgetTypesInfosByBundleAlias( @Parameter(description = "System or Tenant", required = true) @@ -354,11 +345,9 @@ 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')") - @RequestMapping(value = "/widgetType", params = {"isSystem", "bundleAlias", "alias"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/widgetType", params = {"isSystem", "bundleAlias", "alias"}) @Deprecated public WidgetType getWidgetTypeByBundleAliasAndTypeAlias( @Parameter(description = "System or Tenant", required = true) @@ -382,8 +371,7 @@ public class WidgetTypeController extends AutoCommitController { @ApiOperation(value = "Get Widget Type (getWidgetType)", notes = "Get the Widget Type by FQN. " + WIDGET_TYPE_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER, hidden = true) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/widgetType", params = {"fqn"}, method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/widgetType", params = {"fqn"}) public WidgetType getWidgetType( @Parameter(description = "Widget Type fqn", required = true) @RequestParam String fqn) throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java index 357c23259a..5f9ca66724 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.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 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.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -220,11 +222,9 @@ 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')") - @RequestMapping(value = "/widgetsBundles", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/widgetsBundles") public List getWidgetsBundles() throws ThingsboardException { if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { return checkNotNull(widgetsBundleService.findSystemWidgetsBundles(getTenantId())); @@ -234,4 +234,12 @@ public class WidgetsBundleController extends BaseController { } } + @ApiOperation(value = "Get all Widget Bundles (getWidgetsBundlesV2)", + 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 getWidgetsBundlesV2() throws ThingsboardException { + return getWidgetsBundles(); + } + } From 23001780585881e3d502b864ca9f27596be102a9 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 29 Jan 2026 16:26:05 +0200 Subject: [PATCH 007/224] fixed SwaggerConfiguration, fixed WidgetTypeController --- .../server/config/SwaggerConfiguration.java | 2 +- .../server/controller/WidgetTypeController.java | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java index 3460eb19d9..d8eb98ab57 100644 --- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java @@ -147,7 +147,7 @@ public class SwaggerConfiguration { .bearerFormat("/api/auth/login|X-Authorization"); var openApi = new OpenAPI() - .components(new Components().addSecuritySchemes("HTTP login form", securityScheme)) + .components(new Components().addSecuritySchemes("http_login_form", securityScheme)) .info(info); addDefaultSchemas(openApi); addLoginOperation(openApi); diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java index 05adf71a3c..f6671ffa41 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java @@ -232,8 +232,7 @@ 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 getBundleWidgetTypes( @@ -243,6 +242,16 @@ public class WidgetTypeController extends AutoCommitController { return checkNotNull(widgetTypeService.findWidgetTypesByWidgetsBundleId(getTenantId(), widgetsBundleId)); } + @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 getBundleWidgetTypesV2( + @Parameter(description = "Widget Bundle Id", required = true) + @RequestParam("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException { + return getBundleWidgetTypes(strWidgetsBundleId); + } + @Hidden @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/widgetTypesDetails", params = {"isSystem", "bundleAlias"}) @@ -262,7 +271,8 @@ public class WidgetTypeController extends AutoCommitController { return checkNotNull(widgetTypeService.findWidgetTypesDetailsByWidgetsBundleId(getTenantId(), widgetsBundle.getId())); } - @Hidden + @ApiOperation(value = "Get all Widget types details for specified Bundle (getBundleWidgetTypesDetails)", + notes = "Returns an array of Widget Type Details objects that belong to specified Widget Bundle." + WIDGET_TYPE_DETAILS_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @GetMapping(value = "/widgetTypesDetails", params = {"widgetsBundleId"}) public List getBundleWidgetTypesDetails( From 168de4620a01181c6210f228e21d63ff376fe5ad Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 2 Feb 2026 10:50:16 +0200 Subject: [PATCH 008/224] lwm2m - fix bug tests testFirmwareUpdateByObject5_Ok --- .../transport/lwm2m/client/FwLwM2MDevice.java | 52 ++++++++++------ .../transport/lwm2m/client/SwLwM2MDevice.java | 60 ++++++++++++------- 2 files changed, 71 insertions(+), 41 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java index 979a2b8c96..9265893fa2 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java @@ -140,45 +140,61 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable { } private void startDownloading() { + long delay = 0; + // Step 1: state = 1 scheduler.schedule(() -> { - try { - state.set(1); - fireResourceChange(3); - Thread.sleep(100); - state.set(2); - fireResourceChange(3); - } catch (Exception e) { - } - }, 100, TimeUnit.MILLISECONDS); + state.set(1); + fireResourceChange(3); + log.info("Downloading started: state=[{}]", state.get()); + }, delay, TimeUnit.MILLISECONDS); + + delay += 100; // next step after 100 ms + + // Step 2: state = 2 + scheduler.schedule(() -> { + state.set(2); + fireResourceChange(3); + log.info("Downloading in progress: state=[{}]", state.get()); + }, delay, TimeUnit.MILLISECONDS); } + private void startUpdating(LwM2mServer identity) { scheduler.schedule(() -> { try { + // Update state + result state.set(3); fireResourceChange(3); - Thread.sleep(100); + updateResult.set(1); fireResourceChange(5); - this.pkgName = TITLE; - fireResourceChange(6); - this.pkgVersion = TARGET_FW_VERSION; - fireResourceChange(7); + if (this.leshanClient != null) { log.info("Stop/reboot LwM2M client {}", this.leshanClient.getEndpoint(identity)); this.leshanClient.stop(false); + log.info("Start after update fw LwM2M client {}", this.leshanClient.getEndpoint(identity)); this.leshanClient.start(); - this.pkgName = this.pkgNameDef; - this.pkgVersion = this.pkgVersionDef; + + // Delayed reset pkgName/pkgVersion, after reboot + registration + scheduler.schedule(() -> { + this.pkgName = this.pkgNameDef; + fireResourceChange(6); + + this.pkgVersion = this.pkgVersionDef; + fireResourceChange(7); + + log.info("FW resources updating to new values: pkgName=[{}], pkgVersion=[{}]", + this.pkgName, this.pkgVersion); + }, 15, TimeUnit.SECONDS); // 15 sec — safe timing } } catch (Exception e) { + log.error("Error during firmware update", e); } - }, 100, TimeUnit.MILLISECONDS); + }, 0, TimeUnit.SECONDS); // start immediately, without further delay } protected void setLeshanClient(LeshanClient leshanClient) { this.leshanClient = leshanClient; } - } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SwLwM2MDevice.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SwLwM2MDevice.java index 76b9827f5a..6db2cf2e9b 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SwLwM2MDevice.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SwLwM2MDevice.java @@ -33,6 +33,8 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import static org.thingsboard.server.controller.AbstractWebTest.TIMEOUT; + @Slf4j public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable { @@ -85,10 +87,7 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable { log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId); switch (resourceId) { - case 2: - startDownloading(); - return WriteResponse.success(); - case 3: + case 2, 3: startDownloading(); return WriteResponse.success(); default: @@ -123,25 +122,34 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable { } private void startDownloading() { + long delay = 0; + + // Step 1: start downloading scheduler.schedule(() -> { - try { - state.set(1); - updateResult.set(1); - fireResourceChange(7); - fireResourceChange(9); - Thread.sleep(100); - state.set(2); - fireResourceChange(7); - Thread.sleep(100); - state.set(3); - fireResourceChange(7); - Thread.sleep(100); - updateResult.set(3); - fireResourceChange(9); - } catch (Exception e) { - - } - }, 100, TimeUnit.MILLISECONDS); + state.set(1); + updateResult.set(1); + fireResourceChange(7); + fireResourceChange(9); + }, delay, TimeUnit.MILLISECONDS); + + delay += 100; + + // Step 2: downloading in progress + scheduler.schedule(() -> { + state.set(2); + fireResourceChange(7); + }, delay, TimeUnit.MILLISECONDS); + + delay += 100; + + // Step 3: downloading finished + scheduler.schedule(() -> { + state.set(3); + fireResourceChange(7); + + updateResult.set(3); + fireResourceChange(9); + }, delay, TimeUnit.MILLISECONDS); } private void startUpdating() { @@ -150,7 +158,13 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable { updateResult.set(2); fireResourceChange(7); fireResourceChange(9); + + // Optional: delayed log about FW update + scheduler.schedule(() -> { + log.info("FW resources updating to new values: state=[{}], updateResult=[{}]", + state.get(), updateResult.get()); + }, 500, TimeUnit.MILLISECONDS); + }, 100, TimeUnit.MILLISECONDS); } - } From 74ef364f33e775199465592f524267ade19833b3 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Mon, 2 Feb 2026 14:10:39 +0200 Subject: [PATCH 009/224] fix: respect forward_headers_strategy for client IP in audit logs Remove redundant manual X-Forwarded-For header parsing in RestAuthenticationDetails. The getClientIP() method duplicated functionality already provided by Spring's ForwardedHeaderFilter when server.forward_headers_strategy is configured. Now uses request.getRemoteAddr() directly, which respects the configured forward_headers_strategy setting (default: framework). Co-Authored-By: Claude Opus 4.5 --- .../security/auth/rest/RestAuthenticationDetails.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetails.java b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetails.java index aa084a7968..e75a49ccb2 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetails.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetails.java @@ -29,18 +29,10 @@ public class RestAuthenticationDetails implements Serializable { private final Client userAgent; public RestAuthenticationDetails(HttpServletRequest request) { - this.clientAddress = getClientIP(request); + this.clientAddress = request.getRemoteAddr(); this.userAgent = getUserAgent(request); } - private static String getClientIP(HttpServletRequest request) { - String xfHeader = request.getHeader("X-Forwarded-For"); - if (xfHeader == null) { - return request.getRemoteAddr(); - } - return xfHeader.split(",")[0]; - } - private static Client getUserAgent(HttpServletRequest request) { Parser uaParser = new Parser(); return uaParser.parse(request.getHeader("User-Agent")); From 609703e48d0fb9da306756fe26f8413dd9d8c486 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Tue, 3 Feb 2026 08:48:42 +0200 Subject: [PATCH 010/224] fixed yml parameter --- application/src/main/resources/thingsboard.yml | 2 +- edqs/src/main/resources/edqs.yml | 2 +- msa/vc-executor/src/main/resources/tb-vc-executor.yml | 2 +- transport/coap/src/main/resources/tb-coap-transport.yml | 2 +- transport/http/src/main/resources/tb-http-transport.yml | 2 +- transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml | 2 +- transport/mqtt/src/main/resources/tb-mqtt-transport.yml | 2 +- transport/snmp/src/main/resources/tb-snmp-transport.yml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index bdef34df3b..25463bdc6c 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -2011,7 +2011,7 @@ management: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). - include: '${METRICS_ENDPOINTS_EXPOSE:info}' + include: "${METRICS_ENDPOINTS_EXPOSE:info}" health: elasticsearch: # Enable the org.springframework.boot.actuate.elasticsearch.ElasticsearchRestClientHealthIndicator.doHealthCheck diff --git a/edqs/src/main/resources/edqs.yml b/edqs/src/main/resources/edqs.yml index a2a6ca6814..010994dc88 100644 --- a/edqs/src/main/resources/edqs.yml +++ b/edqs/src/main/resources/edqs.yml @@ -210,7 +210,7 @@ management: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). - include: '${METRICS_ENDPOINTS_EXPOSE:info}' + include: "${METRICS_ENDPOINTS_EXPOSE:info}" health: elasticsearch: # Enable the org.springframework.boot.actuate.elasticsearch.ElasticsearchRestClientHealthIndicator.doHealthCheck diff --git a/msa/vc-executor/src/main/resources/tb-vc-executor.yml b/msa/vc-executor/src/main/resources/tb-vc-executor.yml index 4f5dc7ef63..8314985622 100644 --- a/msa/vc-executor/src/main/resources/tb-vc-executor.yml +++ b/msa/vc-executor/src/main/resources/tb-vc-executor.yml @@ -232,7 +232,7 @@ management: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). - include: '${METRICS_ENDPOINTS_EXPOSE:info}' + include: "${METRICS_ENDPOINTS_EXPOSE:info}" # Service common properties service: diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index 1c45984257..9ab06ca996 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -435,7 +435,7 @@ management: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). - include: '${METRICS_ENDPOINTS_EXPOSE:info}' + include: "${METRICS_ENDPOINTS_EXPOSE:info}" # Notification system parameters notification_system: diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml index 39f7d15fb9..f869534088 100644 --- a/transport/http/src/main/resources/tb-http-transport.yml +++ b/transport/http/src/main/resources/tb-http-transport.yml @@ -384,7 +384,7 @@ management: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). - include: '${METRICS_ENDPOINTS_EXPOSE:info}' + include: "${METRICS_ENDPOINTS_EXPOSE:info}" # Notification system parameters notification_system: diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index 8a57c5829b..d22bd34505 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -485,7 +485,7 @@ management: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). - include: '${METRICS_ENDPOINTS_EXPOSE:info}' + include: "${METRICS_ENDPOINTS_EXPOSE:info}" # Notification system parameters notification_system: diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index 4a7571a734..ccbd3901ce 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -418,7 +418,7 @@ management: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). - include: '${METRICS_ENDPOINTS_EXPOSE:info}' + include: "${METRICS_ENDPOINTS_EXPOSE:info}" # Notification system parameters notification_system: diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml index b98ad2a9e9..656c01524d 100644 --- a/transport/snmp/src/main/resources/tb-snmp-transport.yml +++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml @@ -373,7 +373,7 @@ management: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). - include: '${METRICS_ENDPOINTS_EXPOSE:info}' + include: "${METRICS_ENDPOINTS_EXPOSE:info}" # Notification system parameters notification_system: From aaf4f35208b7a14ac9ce71fb109aa928c2546402 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 3 Feb 2026 14:53:20 +0100 Subject: [PATCH 011/224] mvn test with -Dtest ignores -Dsurefire.excludes so the exclusions should be inlined like -Dtest='!**/nosql/**,org.thingsboard.server.controller.**' --- TEST_FAST.md | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/TEST_FAST.md b/TEST_FAST.md index c0f1174584..4d1e9dc9a7 100644 --- a/TEST_FAST.md +++ b/TEST_FAST.md @@ -10,22 +10,43 @@ mvn clean install -T6 -DskipTests mvn test -pl='!application,!dao,!ui-ngx,!msa/js-executor,!msa/web-ui' -T4 mvn test -pl dao -Dparallel=packages -DforkCount=4 -mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.controller.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 -mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.edge.**' -DforkCount=4 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 -mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.service.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 -mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.mqtt.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 -mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.coap.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 -mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.lwm2m.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 -mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='**/*TestSuite.java' -DforkCount=4 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 +mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.controller.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 +mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.edge.**' -DforkCount=4 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 +mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.service.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 +mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.transport.mqtt.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 +mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.transport.coap.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 +mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.transport.lwm2m.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 +mvn test -pl application -Dtest='**/*TestSuite.java' -DforkCount=4 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 #the rest of application tests mvn test -pl application -Dtest=' -!**/nosql/*Test.java, +!**/nosql/**, !org.thingsboard.server.controller.**, !org.thingsboard.server.edge.**, !org.thingsboard.server.service.**, !org.thingsboard.server.transport.mqtt.**, !org.thingsboard.server.transport.coap.**, -!org.thingsboard.server.transport.lwm2m.** +!org.thingsboard.server.transport.lwm2m.**, +!**/*TestSuite.java ' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 ``` + +## Testcontainers compatibility with the Docker API workaround + +In case your tests failed to run testcontainers due to unsupported Docker API version + +:coffee: testcontainers (Docker API 1.32) + :whale: docker 29 (min API 1.44) workaround + +Add to /etc/docker/daemon.json and restart docker +```json +{ + "min-api-version": "1.32" +} +``` + +Same works on Mac, except `daemon.json` are located in another folder and required to be edited from Docker Desktop UI. + +Tip: If your testcontainer are struggling to find any Docker. You can try to remove the testcontainers property file. It will be recreated on the next testcontainers run. +```bash +rm ~/.testcontainers.properties +``` From 482cb6d43f17d0677e7018cd7cd5342a5ebcf6ba Mon Sep 17 00:00:00 2001 From: Maksym Tsymbarov Date: Wed, 4 Feb 2026 15:48:10 +0200 Subject: [PATCH 012/224] Fixed loading and placement of Material icons on Power Button widget --- .../lib/rpc/power-button-widget.models.ts | 51 +++++++++++++++---- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts index f538e8cfc8..4836cc3804 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts @@ -28,7 +28,7 @@ import { Circle, Effect, Element, G, Gradient, Path, Runner, Svg, Text, Timeline import '@svgdotjs/svg.filter.js'; import tinycolor from 'tinycolor2'; import { WidgetContext } from '@home/models/widget-component.models'; -import { Observable, of } from 'rxjs'; +import { Observable, of, shareReplay } from 'rxjs'; import { isSvgIcon, splitIconName } from '@shared/models/icon.models'; import { catchError, map, take } from 'rxjs/operators'; import { MatIconRegistry } from '@angular/material/icon'; @@ -336,6 +336,11 @@ export abstract class PowerButtonShape { protected onPowerSymbolCircle: Path; protected onPowerSymbolLine: Path; + private onIcon$: Observable; + private offIcon$: Observable; + protected onIconOffsetX: number = 0; + protected onIconOffsetY: number = 0; + protected constructor(protected widgetContext: WidgetContext, protected svgShape: Svg, protected iconRegistry: MatIconRegistry, @@ -360,10 +365,14 @@ export abstract class PowerButtonShape { take(1), map((svgElement) => { const element = new Element(svgElement.firstChild); + const iconGroup = this.svgShape.group(); + element.addTo(iconGroup); const box = element.bbox(); const scale = size / box.height; element.scale(scale); - return element; + const scaledBox = iconGroup.bbox(); + iconGroup.translate(-scaledBox.cx, -scaledBox.cy); + return iconGroup; }), catchError(() => of(null) )); @@ -414,8 +423,15 @@ export abstract class PowerButtonShape { public drawOffShape(centerGroup: G, label: boolean, labelWeight?: string, circleStroke?: boolean) { if (this.icons.offButtonIcon.showIcon) { - this.createIconElement(this.icons.offButtonIcon.icon, this.icons.offButtonIcon.iconSize).subscribe(icon => - this.offPowerSymbolIcon = icon.center(cx, cy).addTo(centerGroup)); + this.offIcon$ = this.createIconElement(this.icons.offButtonIcon.icon, this.icons.offButtonIcon.iconSize) + .pipe(shareReplay(1)); + + this.offIcon$.pipe(take(1)).subscribe(icon => { + this.offPowerSymbolIcon = icon; + icon.translate(cx, cy); + centerGroup.add(icon); + }); + } else { if (label) { this.offLabelShape = this.createOffLabel(labelWeight).addTo(centerGroup); @@ -428,10 +444,16 @@ export abstract class PowerButtonShape { public drawOnShape(onCenterGroup?: G, label?: boolean, labelWeight?: string, circleStroke?: boolean, mask?: Circle) { if (this.icons.onButtonIcon.showIcon) { - this.createIconElement(this.icons.onButtonIcon.icon, this.icons.onButtonIcon.iconSize).subscribe(icon => { - this.onPowerSymbolIcon = icon.center(cx, cy); + this.onIcon$ = this.createIconElement(this.icons.onButtonIcon.icon, this.icons.onButtonIcon.iconSize) + .pipe(shareReplay(1)); + + this.onIcon$.subscribe(icon => { + const iconBox = icon.bbox(); + this.onIconOffsetX = iconBox.cx; + this.onIconOffsetY = iconBox.cy; + this.onPowerSymbolIcon = icon.translate(cx, cy); if (isDefinedAndNotNull(onCenterGroup)) { - this.onPowerSymbolIcon.addTo(onCenterGroup); + onCenterGroup.add(this.onPowerSymbolIcon); } if (isDefinedAndNotNull(mask)) { this.createMask(mask, [this.onPowerSymbolIcon]); @@ -462,7 +484,9 @@ export abstract class PowerButtonShape { public onCenterTimeLine(timeline: Timeline, label: boolean) { if (this.icons.onButtonIcon.showIcon) { - this.onPowerSymbolIcon.timeline(timeline); + if (this.onIcon$) { + this.onIcon$.subscribe(icon => icon.timeline(timeline)); + } } else { if (label) { this.onLabelShape.timeline(timeline); @@ -475,7 +499,7 @@ export abstract class PowerButtonShape { public offCenterColor(mainColor: PowerButtonColor, label: boolean) { if (this.icons.offButtonIcon.showIcon) { - this.offPowerSymbolIcon.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.offIcon$.subscribe(icon => icon.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity})) } else { if (label) { this.offLabelShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); @@ -488,7 +512,7 @@ export abstract class PowerButtonShape { public onCenterColor(mainColor: PowerButtonColor, label: boolean) { if (this.icons.onButtonIcon.showIcon) { - this.onPowerSymbolIcon.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.onIcon$.subscribe((icon)=> icon.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity})) } else { if (label) { this.onLabelShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); @@ -501,7 +525,12 @@ export abstract class PowerButtonShape { public buttonAnimation(scale: number, label: boolean) { if (this.icons.onButtonIcon.showIcon) { - powerButtonAnimation(this.onPowerSymbolIcon).transform({scale}); + const translateX = cx - this.onIconOffsetX; + const translateY = cy - this.onIconOffsetY; + powerButtonAnimation(this.onPowerSymbolIcon).transform({ + scale: scale, + translate: [translateX, translateY] + }); } else { if (label) { powerButtonAnimation(this.onLabelShape).transform({scale, origin: {x: cx, y: cy}}); From 8f9c4b70d7f1fbb6753bcb2ebc1bf98e9666f858 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 4 Feb 2026 17:28:21 +0200 Subject: [PATCH 013/224] Migrate to Java 25 Co-Authored-By: Claude --- .github/workflows/license-header-format.yml | 2 +- .../install/centos/instructions.md | 10 +- .../install/ubuntu/instructions.md | 10 +- .../script/api/tbel/TbDateTest.java | 134 ++++++++++++++---- .../server/dao/sql/alarm/AlarmRepository.java | 36 ++--- .../dao/sql/audit/AuditLogRepository.java | 12 +- msa/tb/docker-cassandra/Dockerfile | 2 +- msa/tb/docker-postgres/Dockerfile | 2 +- packaging/java/build.gradle | 4 +- packaging/java/scripts/windows/install.bat | 8 +- packaging/js/build.gradle | 2 +- pom.xml | 20 ++- tbel-date-note.md | 81 +++++++++++ 13 files changed, 241 insertions(+), 82 deletions(-) create mode 100644 tbel-date-note.md diff --git a/.github/workflows/license-header-format.yml b/.github/workflows/license-header-format.yml index 0ac41c7bc8..6be1171b32 100644 --- a/.github/workflows/license-header-format.yml +++ b/.github/workflows/license-header-format.yml @@ -35,7 +35,7 @@ jobs: uses: actions/setup-java@v4 with: distribution: 'corretto' # https://github.com/actions/setup-java?tab=readme-ov-file#supported-distributions - java-version: '21' + java-version: '25' cache: 'maven' # https://github.com/actions/setup-java?tab=readme-ov-file#caching-sbt-dependencies - name: License header format diff --git a/application/src/main/data/json/edge/instructions/install/centos/instructions.md b/application/src/main/data/json/edge/instructions/install/centos/instructions.md index 1822add6d8..be0a4903a4 100644 --- a/application/src/main/data/json/edge/instructions/install/centos/instructions.md +++ b/application/src/main/data/json/edge/instructions/install/centos/instructions.md @@ -8,15 +8,15 @@ sudo yum install -y nano wget && sudo yum install -y https://dl.fedoraproject.or {:copy-code} ``` -#### Step 1. Install Java 17 (OpenJDK) -ThingsBoard service is running on Java 17. To install OpenJDK 17, follow these instructions: +#### Step 1. Install Java 25 (OpenJDK) +ThingsBoard service is running on Java 25. To install OpenJDK 25, follow these instructions: ```bash -sudo dnf install java-17-openjdk +sudo dnf install java-25-openjdk {:copy-code} ``` -Configure your operating system to use OpenJDK 17 by default. You can configure the default version by running the following command: +Configure your operating system to use OpenJDK 25 by default. You can configure the default version by running the following command: ```bash sudo update-alternatives --config java @@ -33,7 +33,7 @@ java -version The expected result is: ```text -openjdk version "17.x.xx" +openjdk version "25.x.xx" OpenJDK Runtime Environment (...) OpenJDK 64-Bit Server VM (build ...) ``` diff --git a/application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md b/application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md index b4782dd46a..f0e7ac6a92 100644 --- a/application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md +++ b/application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md @@ -1,14 +1,14 @@ Here is the list of commands that can be used to quickly install ThingsBoard Edge on Ubuntu Server and connect to the server. -#### Step 1. Install Java 17 (OpenJDK) -ThingsBoard service is running on Java 17. To install OpenJDK 17, follow these instructions: +#### Step 1. Install Java 25 (OpenJDK) +ThingsBoard service is running on Java 25. To install OpenJDK 25, follow these instructions: ```bash -sudo apt update && sudo apt install openjdk-17-jdk +sudo apt update && sudo apt install openjdk-25-jdk {:copy-code} ``` -Configure your operating system to use OpenJDK 17 by default. You can configure the default version by running the following command: +Configure your operating system to use OpenJDK 25 by default. You can configure the default version by running the following command: ```bash sudo update-alternatives --config java @@ -25,7 +25,7 @@ java -version The expected result is: ```text -openjdk version "17.x.xx" +openjdk version "25.x.xx" OpenJDK Runtime Environment (...) OpenJDK 64-Bit Server VM (build ...) ``` diff --git a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTest.java b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTest.java index 3529c94ee4..494eb64cde 100644 --- a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTest.java +++ b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTest.java @@ -209,7 +209,9 @@ class TbDateTest { .put("timeZone", "America/New_York") .put("dateStyle", "full") .toString())); - Assertions.assertEquals("середа, 6 вересня 2023 р.", d.toLocaleDateString("uk-UA", JacksonUtil.newObjectNode() + // Java 21+ uses narrow no-break space (U+202F) before "р." per CLDR 42+ + String expectedDate_ukUA = Runtime.version().feature() >= 21 ? "середа, 6 вересня 2023\u202Fр." : "середа, 6 вересня 2023 р."; + Assertions.assertEquals(expectedDate_ukUA, d.toLocaleDateString("uk-UA", JacksonUtil.newObjectNode() .put("timeZone", "Europe/Kiev") .put("dateStyle", "full") .toString())); @@ -244,12 +246,18 @@ class TbDateTest { Assertions.assertNotNull(d.toLocaleTimeString()); Assertions.assertNotNull(d.toLocaleTimeString("en-US")); - Assertions.assertEquals("9:04:05 PM", d.toLocaleTimeString("en-US", "America/New_York")); + // Java 21+ uses narrow no-break space (U+202F) before AM/PM per CLDR 42+ + String expectedTime_enUS = Runtime.version().feature() >= 21 ? "9:04:05\u202FPM" : "9:04:05 PM"; + Assertions.assertEquals(expectedTime_enUS, d.toLocaleTimeString("en-US", "America/New_York")); Assertions.assertEquals("오후 9:04:05", d.toLocaleTimeString("ko-KR", "America/New_York")); Assertions.assertEquals("04:04:05", d.toLocaleTimeString( "uk-UA", "Europe/Kiev")); Assertions.assertEquals("9:04:05 م", d.toLocaleTimeString( "ar-EG", "America/New_York")); - Assertions.assertEquals("9:04:05 PM Eastern Daylight Time", d.toLocaleTimeString("en-US", JacksonUtil.newObjectNode() + // Java 21+ uses narrow no-break space (U+202F) before AM/PM per CLDR 42+ + String expectedFullTime_enUS = Runtime.version().feature() >= 21 + ? "9:04:05\u202FPM Eastern Daylight Time" + : "9:04:05 PM Eastern Daylight Time"; + Assertions.assertEquals(expectedFullTime_enUS, d.toLocaleTimeString("en-US", JacksonUtil.newObjectNode() .put("timeZone", "America/New_York") .put("timeStyle", "full") .toString())); @@ -292,14 +300,27 @@ class TbDateTest { Assertions.assertNotNull(d.toLocaleString()); Assertions.assertNotNull(d.toLocaleString("en-US")); - Assertions.assertEquals("9/5/23, 9:04:05 PM", d.toLocaleString("en-US", "America/New_York")); + // Java 21+ uses narrow no-break space (U+202F) before AM/PM per CLDR 42+ + String expectedLocale_enUS = Runtime.version().feature() >= 21 ? "9/5/23, 9:04:05\u202FPM" : "9/5/23, 9:04:05 PM"; + Assertions.assertEquals(expectedLocale_enUS, d.toLocaleString("en-US", "America/New_York")); Assertions.assertEquals("23. 9. 5. 오후 9:04:05", d.toLocaleString("ko-KR", "America/New_York")); Assertions.assertEquals("06.09.23, 04:04:05", d.toLocaleString( "uk-UA", "Europe/Kiev")); - String expected_ver = Runtime.version().feature() == 11 ? "5\u200F/9\u200F/2023 9:04:05 م" : - "5\u200F/9\u200F/2023, 9:04:05 م"; - Assertions.assertEquals(expected_ver, d.toLocaleString( "ar-EG", "America/New_York")); + // Java 11 has no comma, Java 17 uses ASCII comma, Java 21+ uses Arabic comma (U+060C) + String expected_ar; + if (Runtime.version().feature() == 11) { + expected_ar = "5\u200F/9\u200F/2023 9:04:05 م"; + } else if (Runtime.version().feature() >= 21) { + expected_ar = "5\u200F/9\u200F/2023\u060C 9:04:05 م"; + } else { + expected_ar = "5\u200F/9\u200F/2023, 9:04:05 م"; + } + Assertions.assertEquals(expected_ar, d.toLocaleString( "ar-EG", "America/New_York")); - Assertions.assertEquals("Tuesday, September 5, 2023 at 9:04:05 PM Eastern Daylight Time", d.toLocaleString("en-US", JacksonUtil.newObjectNode() + // Java 21+ changed "at" to "," and uses NNBSP before AM/PM per CLDR 42+ + String expected_enUS_full = Runtime.version().feature() >= 21 + ? "Tuesday, September 5, 2023, 9:04:05\u202FPM Eastern Daylight Time" + : "Tuesday, September 5, 2023 at 9:04:05 PM Eastern Daylight Time"; + Assertions.assertEquals(expected_enUS_full, d.toLocaleString("en-US", JacksonUtil.newObjectNode() .put("timeZone", "America/New_York") .put("dateStyle", "full") .put("timeStyle", "full") @@ -309,15 +330,26 @@ class TbDateTest { .put("dateStyle", "full") .put("timeStyle", "full") .toString())); - Assertions.assertEquals("середа, 6 вересня 2023 р. о 04:04:05 за східноєвропейським літнім часом", d.toLocaleString("uk-UA", JacksonUtil.newObjectNode() + // Java 21+ changed Ukrainian locale: "о" replaced with "," and NNBSP before "р." per CLDR 42+ + String expected_ukUA_full_summer = Runtime.version().feature() >= 21 + ? "середа, 6 вересня 2023\u202Fр., 04:04:05 за східноєвропейським літнім часом" + : "середа, 6 вересня 2023 р. о 04:04:05 за східноєвропейським літнім часом"; + Assertions.assertEquals(expected_ukUA_full_summer, d.toLocaleString("uk-UA", JacksonUtil.newObjectNode() .put("timeZone", "Europe/Kiev") .put("dateStyle", "full") .put("timeStyle", "full") .toString())); - expected_ver = Runtime.version().feature() == 11 ? "الثلاثاء، 5 سبتمبر 2023 9:04:05 م التوقيت الصيفي الشرقي لأمريكا الشمالية" : - "الثلاثاء، 5 سبتمبر 2023 في 9:04:05 م التوقيت الصيفي الشرقي لأمريكا الشمالية"; - Assertions.assertEquals(expected_ver, d.toLocaleString("ar-EG", JacksonUtil.newObjectNode() + // Java 11 has no connector, Java 17 uses "في", Java 21+ uses Arabic comma "،" + String expected_ar_full; + if (Runtime.version().feature() == 11) { + expected_ar_full = "الثلاثاء، 5 سبتمبر 2023 9:04:05 م التوقيت الصيفي الشرقي لأمريكا الشمالية"; + } else if (Runtime.version().feature() >= 21) { + expected_ar_full = "الثلاثاء، 5 سبتمبر 2023\u060C 9:04:05 م التوقيت الصيفي الشرقي لأمريكا الشمالية"; + } else { + expected_ar_full = "الثلاثاء، 5 سبتمبر 2023 في 9:04:05 م التوقيت الصيفي الشرقي لأمريكا الشمالية"; + } + Assertions.assertEquals(expected_ar_full, d.toLocaleString("ar-EG", JacksonUtil.newObjectNode() .put("timeZone", "America/New_York") .put("dateStyle", "full") .put("timeStyle", "full") @@ -825,16 +857,44 @@ class TbDateTest { @Test public void toStringAsJs() { TbDate d1 = new TbDate(1975, 12, 31, 23,15,30, 567,"-04:00"); - Assertions.assertEquals("четвер, 1 січня 1976 р. о 06:15:30 за східноєвропейським стандартним часом", d1.toString("uk-UA", "Europe/Kyiv")); - Assertions.assertEquals("Thursday, January 1, 1976 at 6:15:30 AM Eastern European Standard Time", d1.toString("en-US", "Europe/Kyiv")); - Assertions.assertEquals("1976 Jan 1, Thu 06:15:30 Eastern European Time", d1.toString("UTC", "Europe/Kyiv")); - Assertions.assertEquals("Wednesday, December 31, 1975 at 10:15:30 PM Eastern Standard Time", d1.toString("en-US", "America/New_York")); - Assertions.assertEquals("1975 Dec 31, Wed 22:15:30 Eastern Standard Time", d1.toString("GMT", "America/New_York")); - Assertions.assertEquals("1975 Dec 31, Wed 22:15:30 Eastern Standard Time", d1.toString("UTC", "America/New_York")); + // Java 21+ changed Ukrainian locale: "о" (at) replaced with "," and NNBSP before "р." per CLDR 42+ + String expected_ukUA_full = Runtime.version().feature() >= 21 + ? "четвер, 1 січня 1976\u202Fр., 06:15:30 за східноєвропейським стандартним часом" + : "четвер, 1 січня 1976 р. о 06:15:30 за східноєвропейським стандартним часом"; + Assertions.assertEquals(expected_ukUA_full, d1.toString("uk-UA", "Europe/Kyiv")); + // Java 21+ changed "at" to "," and uses NNBSP before AM/PM per CLDR 42+ + String expected_enUS_Kyiv = Runtime.version().feature() >= 21 + ? "Thursday, January 1, 1976, 6:15:30\u202FAM Eastern European Standard Time" + : "Thursday, January 1, 1976 at 6:15:30 AM Eastern European Standard Time"; + Assertions.assertEquals(expected_enUS_Kyiv, d1.toString("en-US", "Europe/Kyiv")); + // Java 21+ changed timezone display for UTC locale + String expected_UTC_Kyiv = Runtime.version().feature() >= 21 + ? "1976 Jan 1, Thu 06:15:30 Kyiv (+0)" + : "1976 Jan 1, Thu 06:15:30 Eastern European Time"; + Assertions.assertEquals(expected_UTC_Kyiv, d1.toString("UTC", "Europe/Kyiv")); + // Java 21+ changed "at" to "," and uses NNBSP before AM/PM per CLDR 42+ + String expected_enUS_NY = Runtime.version().feature() >= 21 + ? "Wednesday, December 31, 1975, 10:15:30\u202FPM Eastern Standard Time" + : "Wednesday, December 31, 1975 at 10:15:30 PM Eastern Standard Time"; + Assertions.assertEquals(expected_enUS_NY, d1.toString("en-US", "America/New_York")); + // Java 21+ changed timezone display for GMT/UTC locales + String expected_GMT_NY = Runtime.version().feature() >= 21 + ? "1975 Dec 31, Wed 22:15:30 New York (+0)" + : "1975 Dec 31, Wed 22:15:30 Eastern Standard Time"; + Assertions.assertEquals(expected_GMT_NY, d1.toString("GMT", "America/New_York")); + Assertions.assertEquals(expected_GMT_NY, d1.toString("UTC", "America/New_York")); Assertions.assertEquals(d1.toUTCString("UTC"), d1.toUTCString()); - Assertions.assertEquals("четвер, 1 січня 1976 р., 03:15:30", d1.toUTCString("uk-UA")); - Assertions.assertEquals("Thursday, January 1, 1976, 3:15:30 AM", d1.toUTCString("en-US")); + // Java 21+ uses NNBSP before "р." per CLDR 42+ + String expected_ukUA_utc = Runtime.version().feature() >= 21 + ? "четвер, 1 січня 1976\u202Fр., 03:15:30" + : "четвер, 1 січня 1976 р., 03:15:30"; + Assertions.assertEquals(expected_ukUA_utc, d1.toUTCString("uk-UA")); + // Java 21+ uses NNBSP before AM/PM per CLDR 42+ + String expected_enUS_utc = Runtime.version().feature() >= 21 + ? "Thursday, January 1, 1976, 3:15:30\u202FAM" + : "Thursday, January 1, 1976, 3:15:30 AM"; + Assertions.assertEquals(expected_enUS_utc, d1.toUTCString("en-US")); Assertions.assertEquals("1976-01-01T03:15:30.567Z", d1.toJSON()); Assertions.assertEquals("1976-01-01T03:15:30.567Z", d1.toISOString()); @@ -842,22 +902,44 @@ class TbDateTest { Assertions.assertEquals("1976-01-01 06:15:30", d1.toLocaleString("UTC", "Europe/Kyiv")); Assertions.assertEquals("01.01.76, 06:15:30", d1.toLocaleString("uk-UA", "Europe/Kyiv")); Assertions.assertEquals("1975-12-31 22:15:30", d1.toLocaleString("UTC", "America/New_York")); - Assertions.assertEquals("12/31/75, 10:15:30 PM", d1.toLocaleString("en-US", "America/New_York")); + // Java 21+ uses NNBSP before AM/PM per CLDR 42+ + String expected_enUS_locale_NY = Runtime.version().feature() >= 21 + ? "12/31/75, 10:15:30\u202FPM" + : "12/31/75, 10:15:30 PM"; + Assertions.assertEquals(expected_enUS_locale_NY, d1.toLocaleString("en-US", "America/New_York")); Assertions.assertEquals("1976 Jan 1, Thu", d1.toDateString("UTC", "Europe/Kyiv")); - Assertions.assertEquals("четвер, 1 січня 1976 р.", d1.toDateString("uk-UA", "Europe/Kyiv")); + // Java 21+ uses NNBSP before "р." per CLDR 42+ + String expected_ukUA_date = Runtime.version().feature() >= 21 + ? "четвер, 1 січня 1976\u202Fр." + : "четвер, 1 січня 1976 р."; + Assertions.assertEquals(expected_ukUA_date, d1.toDateString("uk-UA", "Europe/Kyiv")); Assertions.assertEquals("1975 Dec 31, Wed", d1.toDateString("UTC", "America/New_York")); Assertions.assertEquals("Wednesday, December 31, 1975", d1.toDateString("en-US", "America/New_York")); Assertions.assertEquals("06:15:30", d1.toLocaleTimeString("uk-UA", "Europe/Kyiv")); Assertions.assertEquals("06:15:30", d1.toLocaleTimeString("UTC", "Europe/Kyiv")); - Assertions.assertEquals("10:15:30 PM", d1.toLocaleTimeString("en-US", "America/New_York")); + // Java 21+ uses NNBSP before AM/PM per CLDR 42+ + String expected_enUS_time = Runtime.version().feature() >= 21 ? "10:15:30\u202FPM" : "10:15:30 PM"; + Assertions.assertEquals(expected_enUS_time, d1.toLocaleTimeString("en-US", "America/New_York")); Assertions.assertEquals("22:15:30", d1.toLocaleTimeString("UTC", "America/New_York")); Assertions.assertEquals("06:15:30 за східноєвропейським стандартним часом", d1.toTimeString("uk-UA", "Europe/Kyiv")); - Assertions.assertEquals("06:15:30 Eastern European Time", d1.toTimeString("UTC", "Europe/Kyiv")); - Assertions.assertEquals("10:15:30 PM Eastern Standard Time", d1.toTimeString("en-US", "America/New_York")); - Assertions.assertEquals("22:15:30 Eastern Standard Time", d1.toTimeString("UTC", "America/New_York")); + // Java 21+ changed timezone display for UTC locale + String expected_UTC_time_Kyiv = Runtime.version().feature() >= 21 + ? "06:15:30 Kyiv (+0)" + : "06:15:30 Eastern European Time"; + Assertions.assertEquals(expected_UTC_time_Kyiv, d1.toTimeString("UTC", "Europe/Kyiv")); + // Java 21+ uses NNBSP before AM/PM per CLDR 42+ + String expected_enUS_timeStr = Runtime.version().feature() >= 21 + ? "10:15:30\u202FPM Eastern Standard Time" + : "10:15:30 PM Eastern Standard Time"; + Assertions.assertEquals(expected_enUS_timeStr, d1.toTimeString("en-US", "America/New_York")); + // Java 21+ changed timezone display for UTC locale + String expected_UTC_time_NY = Runtime.version().feature() >= 21 + ? "22:15:30 New York (+0)" + : "22:15:30 Eastern Standard Time"; + Assertions.assertEquals(expected_UTC_time_NY, d1.toTimeString("UTC", "America/New_York")); } @Test diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java index f812d13f65..67e71ebcdf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java @@ -98,10 +98,8 @@ public interface AlarmRepository extends JpaRepository { "AND ea.entityType = :affectedEntityType " + "AND (:startTime IS NULL OR (a.createdTime >= :startTime AND ea.createdTime >= :startTime)) " + "AND (:endTime IS NULL OR (a.createdTime <= :endTime AND ea.createdTime <= :endTime)) " + - "AND ((:#{#alarmTypes == null} = true) OR a.type IN (:alarmTypes)) " + //HHH-15968 - "AND ((:#{#alarmSeverities == null} = true) OR a.severity IN (:alarmSeverities)) " + //HHH-15968 -// "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " + -// "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " + + "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " + + "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " + "AND ((:clearFilterEnabled) = FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) = FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = :assigneeId) " + @@ -119,10 +117,8 @@ public interface AlarmRepository extends JpaRepository { "AND ea.entityType = :affectedEntityType " + "AND (:startTime IS NULL OR (a.createdTime >= :startTime AND ea.createdTime >= :startTime)) " + "AND (:endTime IS NULL OR (a.createdTime <= :endTime AND ea.createdTime <= :endTime)) " + - "AND ((:#{#alarmTypes == null} = true) OR a.type IN (:alarmTypes)) " + //HHH-15968 - "AND ((:#{#alarmSeverities == null} = true) OR a.severity IN (:alarmSeverities)) " + //HHH-15968 -// "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " + -// "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " + + "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " + + "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " + "AND ((:clearFilterEnabled) = FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) = FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = :assigneeId) " + @@ -183,10 +179,8 @@ public interface AlarmRepository extends JpaRepository { "WHERE a.tenantId = :tenantId " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + - "AND ((:#{#alarmTypes == null} = true) OR a.type IN (:alarmTypes)) " + //HHH-15968 - "AND ((:#{#alarmSeverities == null} = true) OR a.severity IN (:alarmSeverities)) " + //HHH-15968 -// "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " + -// "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " + + "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " + + "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " + "AND ((:clearFilterEnabled) = FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) = FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = :assigneeId) " + @@ -199,10 +193,8 @@ public interface AlarmRepository extends JpaRepository { "WHERE a.tenantId = :tenantId " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + - "AND ((:#{#alarmTypes == null} = true) OR a.type IN (:alarmTypes)) " + //HHH-15968 - "AND ((:#{#alarmSeverities == null} = true) OR a.severity IN (:alarmSeverities)) " + //HHH-15968 -// "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " + -// "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " + + "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " + + "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " + "AND ((:clearFilterEnabled) = FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) = FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = :assigneeId) " + @@ -263,10 +255,8 @@ public interface AlarmRepository extends JpaRepository { "WHERE a.tenantId = :tenantId AND a.customerId = :customerId " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + - "AND ((:#{#alarmTypes == null} = true) OR a.type IN (:alarmTypes)) " + //HHH-15968 - "AND ((:#{#alarmSeverities == null} = true) OR a.severity IN (:alarmSeverities)) " + //HHH-15968 -// "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " + -// "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " + + "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " + + "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " + "AND ((:clearFilterEnabled) = FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) = FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = :assigneeId) " + @@ -280,10 +270,8 @@ public interface AlarmRepository extends JpaRepository { "WHERE a.tenantId = :tenantId AND a.customerId = :customerId " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + - "AND ((:#{#alarmTypes == null} = true) OR a.type IN (:alarmTypes)) " + //HHH-15968 - "AND ((:#{#alarmSeverities == null} = true) OR a.severity IN (:alarmSeverities)) " + //HHH-15968 -// "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " + -// "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " + + "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " + + "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " + "AND ((:clearFilterEnabled) = FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) = FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = :assigneeId) " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/audit/AuditLogRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/audit/AuditLogRepository.java index d685d9ee78..4266003706 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/audit/AuditLogRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/audit/AuditLogRepository.java @@ -33,8 +33,7 @@ public interface AuditLogRepository extends JpaRepository "a.tenantId = :tenantId " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + - "AND ((:#{#actionTypes == null} = true) OR a.actionType IN (:actionTypes)) " + //HHH-15968 -// "AND ((:actionTypes) IS NULL OR a.actionType in (:actionTypes)) " + + "AND ((:actionTypes) IS NULL OR a.actionType IN (:actionTypes)) " + "AND (:textSearch IS NULL OR ilike(a.entityType, CONCAT('%', :textSearch, '%')) = true " + "OR ilike(a.entityName, CONCAT('%', :textSearch, '%')) = true " + "OR ilike(a.userName, CONCAT('%', :textSearch, '%')) = true " + @@ -54,8 +53,7 @@ public interface AuditLogRepository extends JpaRepository "AND a.entityType = :entityType AND a.entityId = :entityId " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + - "AND ((:#{#actionTypes == null} = true) OR a.actionType IN (:actionTypes)) " + //HHH-15968 -// "AND ((:actionTypes) IS NULL OR a.actionType in (:actionTypes)) " + + "AND ((:actionTypes) IS NULL OR a.actionType IN (:actionTypes)) " + "AND (:textSearch IS NULL OR ilike(a.entityName, CONCAT('%', :textSearch, '%')) = true " + "OR ilike(a.userName, CONCAT('%', :textSearch, '%')) = true " + "OR ilike(a.actionType, CONCAT('%', :textSearch, '%')) = true " + @@ -75,8 +73,7 @@ public interface AuditLogRepository extends JpaRepository "AND a.customerId = :customerId " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + - "AND ((:#{#actionTypes == null} = true) OR a.actionType IN (:actionTypes)) " + //HHH-15968 -// "AND ((:actionTypes) IS NULL OR a.actionType in (:actionTypes)) " + + "AND ((:actionTypes) IS NULL OR a.actionType IN (:actionTypes)) " + "AND (:textSearch IS NULL OR ilike(a.entityType, CONCAT('%', :textSearch, '%')) = true " + "OR ilike(a.entityName, CONCAT('%', :textSearch, '%')) = true " + "OR ilike(a.userName, CONCAT('%', :textSearch, '%')) = true " + @@ -96,8 +93,7 @@ public interface AuditLogRepository extends JpaRepository "AND a.userId = :userId " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + - "AND ((:#{#actionTypes == null} = true) OR a.actionType IN (:actionTypes)) " + //HHH-15968 -// "AND ((:actionTypes) IS NULL OR a.actionType in (:actionTypes)) " + + "AND ((:actionTypes) IS NULL OR a.actionType IN (:actionTypes)) " + "AND (:textSearch IS NULL OR ilike(a.entityType, CONCAT('%', :textSearch, '%')) = true " + "OR ilike(a.entityName, CONCAT('%', :textSearch, '%')) = true " + "OR ilike(a.actionType, CONCAT('%', :textSearch, '%')) = true " + diff --git a/msa/tb/docker-cassandra/Dockerfile b/msa/tb/docker-cassandra/Dockerfile index 34a90e6fb7..74c33b2bfa 100644 --- a/msa/tb/docker-cassandra/Dockerfile +++ b/msa/tb/docker-cassandra/Dockerfile @@ -14,7 +14,7 @@ # limitations under the License. # -FROM thingsboard/openjdk17:bookworm-slim +FROM thingsboard/openjdk25:trixie-slim ENV PG_MAJOR=16 diff --git a/msa/tb/docker-postgres/Dockerfile b/msa/tb/docker-postgres/Dockerfile index 0a0f61f904..b74727c166 100644 --- a/msa/tb/docker-postgres/Dockerfile +++ b/msa/tb/docker-postgres/Dockerfile @@ -14,7 +14,7 @@ # limitations under the License. # -FROM thingsboard/openjdk17:bookworm-slim +FROM thingsboard/openjdk25:trixie-slim ENV PG_MAJOR 12 diff --git a/packaging/java/build.gradle b/packaging/java/build.gradle index 5ef4576a91..cdf96fcd26 100644 --- a/packaging/java/build.gradle +++ b/packaging/java/build.gradle @@ -16,7 +16,7 @@ import org.apache.tools.ant.filters.ReplaceTokens plugins { - id "nebula.ospackage" version "8.6.3" + id "com.netflix.nebula.ospackage" version "12.1.1" } buildDir = projectBuildDir @@ -92,7 +92,7 @@ buildRpm { archiveVersion = projectVersion.replace('-', '') archiveFileName = "${pkgName}.rpm" - // Support Java 17 (existing), plus Java 21 and Java 25 for RPM-based distros + // Support Java 25 (current), plus Java 21 and Java 17 (backward compatibility) for RPM-based distros // Keep using RPM boolean expression syntax since .or() chaining is for DEB only requires("(java-17 or java-17-headless or jre-17 or jre-17-headless or " + "java-21 or java-21-headless or jre-21 or jre-21-headless or " + diff --git a/packaging/java/scripts/windows/install.bat b/packaging/java/scripts/windows/install.bat index ca17408dfe..566286d4b5 100644 --- a/packaging/java/scripts/windows/install.bat +++ b/packaging/java/scripts/windows/install.bat @@ -7,11 +7,11 @@ setlocal ENABLEEXTENSIONS for /f tokens^=2-5^ delims^=.-_^" %%j in ('java -fullversion 2^>^&1') do set "jver=%%j%%k" @ECHO CurrentVersion %jver% -if %jver% NEQ 170 GOTO JAVA_NOT_INSTALLED +if %jver% NEQ 250 GOTO JAVA_NOT_INSTALLED :JAVA_INSTALLED -@ECHO Java 17 found! +@ECHO Java 25 found! @ECHO Installing thingsboard ... SET loadDemo=false @@ -50,8 +50,8 @@ POPD GOTO END :JAVA_NOT_INSTALLED -@ECHO Java 17 is not installed. Only Java 17 is supported -@ECHO Please go to https://adoptopenjdk.net/index.html and install Java 17. Then retry installation. +@ECHO Java 25 is not installed. Only Java 25 is supported +@ECHO Please go to https://adoptopenjdk.net/index.html and install Java 25. Then retry installation. PAUSE GOTO END diff --git a/packaging/js/build.gradle b/packaging/js/build.gradle index 15b8e546a1..001dafabb0 100644 --- a/packaging/js/build.gradle +++ b/packaging/js/build.gradle @@ -16,7 +16,7 @@ import org.apache.tools.ant.filters.ReplaceTokens plugins { - id "nebula.ospackage" version "8.6.3" + id "com.netflix.nebula.ospackage" version "12.1.1" } buildDir = projectBuildDir diff --git a/pom.xml b/pom.xml index c4cfec0ad3..a3498dd788 100755 --- a/pom.xml +++ b/pom.xml @@ -28,8 +28,8 @@ 2016 - 17 - 17 + 25 + 25 ${basedir} true none @@ -65,7 +65,7 @@ 3.25.5 1.76.0 1.2.8 - 1.18.38 + 1.18.40 1.2.5 1.2.5 1.7.1 @@ -111,6 +111,7 @@ 4.0.2 1.7.5 3.8.0 + 1.18.4 1.8.0-TB 2.38.0 1.24 @@ -605,7 +606,7 @@ maven-compiler-plugin 3.11.0 - 17 + 25 -Xlint:deprecation -Xlint:removal @@ -663,6 +664,7 @@ -XX:+UseStringDeduplication -XX:MaxGCPauseMillis=200 + -XX:+EnableDynamicAgentLoading --add-opens=java.base/java.lang.reflect=ALL-UNNAMED -Dqueue.edqs.local.rocksdb_path="target/rocksdb/fork_${surefire.forkNumber}/edqs" -Dqueue.calculated_fields.rocks_db_path="target/rocksdb/fork_${surefire.forkNumber}/cf" @@ -913,6 +915,16 @@ pom import + + net.bytebuddy + byte-buddy + ${bytebuddy.version} + + + net.bytebuddy + byte-buddy-agent + ${bytebuddy.version} + org.thingsboard.langchain4j langchain4j-bom diff --git a/tbel-date-note.md b/tbel-date-note.md new file mode 100644 index 0000000000..5c0f3ea741 --- /dev/null +++ b/tbel-date-note.md @@ -0,0 +1,81 @@ +## Release Note: TBEL Date Formatting Changes (Java 21+) + +### Summary + +When upgrading ThingsBoard to Java 21 or later, users may notice changes in locale-formatted date/time strings produced by the `TbDate` class in TBEL scripts. These changes are caused by updates to the Unicode CLDR (Common Locale Data Repository) in Java 21+. + +### What Changed + +| Format | Java 17 | Java 21+ | +|--------|---------|----------| +| Time with AM/PM | `9:04:05 PM` | `9:04:05 PM` (narrow no-break space before AM/PM) | +| Full datetime (English) | `Tuesday, September 5, 2023 at 9:04:05 PM` | `Tuesday, September 5, 2023, 9:04:05 PM` | +| Full datetime (Ukrainian) | `середа, 6 вересня 2023 р. о 04:04:05` | `середа, 6 вересня 2023 р., 04:04:05` | +| Short datetime (Arabic) | `5/9/2023, 9:04:05 م` | `5/9/2023، 9:04:05 م` (Arabic comma) | +| UTC timezone display | `Eastern European Time` | `Kyiv (+0)` | + +### Impact + +TBEL scripts that rely on exact string matching or parsing of locale-formatted dates may behave differently after upgrading. For example: + +```javascript +// ⚠️ May break after upgrade - string comparison +var dateStr = new Date(ts).toLocaleString("en-US", "America/New_York"); +if (dateStr == "9/5/23, 9:04:05 PM") { + // Will fail - invisible character difference +} + +// ⚠️ May break after upgrade - string splitting +var parts = new Date(ts).toLocaleTimeString("en-US", tz).split(" "); +// "PM" now preceded by narrow no-break space (U+202F), not regular space +``` + +### Recommendations + +1. **Use ISO formats for comparisons and storage** + ```javascript + // ✅ Stable across Java versions + var dateStr = new Date(ts).toISOString(); // "2023-09-05T21:04:05Z" + var dateJson = new Date(ts).toJSON(); // "2023-09-05T21:04:05.000Z" + ``` + +2. **Use explicit patterns for consistent formatting** + ```javascript + // ✅ Explicit pattern - stable output + var dateStr = new Date(ts).toLocaleString("en-US", '{"pattern": "M/d/yyyy, h:mm:ss a"}'); + ``` + +3. **Use numeric getters for comparisons** + ```javascript + // ✅ Compare numeric values instead of strings + var d = new Date(ts); + if (d.getHours() == 21 && d.getMinutes() == 4) { ... } + ``` + +4. **Avoid string equality checks on formatted dates** + ```javascript + // ❌ Avoid + if (date.toLocaleString() == storedDateString) { ... } + + // ✅ Prefer + if (date.getTime() == storedTimestamp) { ... } + ``` + +### Affected Methods + +The following `TbDate` methods may produce different output: +- `toLocaleString()` +- `toLocaleDateString()` +- `toLocaleTimeString()` +- `toString()` +- `toDateString()` +- `toTimeString()` +- `toUTCString()` + +### Unaffected Methods + +These methods produce consistent output across Java versions: +- `toISOString()` +- `toJSON()` +- `getTime()` / `valueOf()` +- All numeric getters (`getFullYear()`, `getMonth()`, `getDate()`, `getHours()`, etc.) From b1e75cdfa0972ca27c557896ac039a60950dcc81 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 5 Feb 2026 12:00:30 +0200 Subject: [PATCH 014/224] Fix Gradle builds --- packaging/java/build.gradle | 52 +++++++++++++++++++++++++------------ packaging/js/build.gradle | 37 ++++++++++++++++---------- pom.xml | 2 +- 3 files changed, 61 insertions(+), 30 deletions(-) diff --git a/packaging/java/build.gradle b/packaging/java/build.gradle index cdf96fcd26..6bd28968e9 100644 --- a/packaging/java/build.gradle +++ b/packaging/java/build.gradle @@ -21,7 +21,9 @@ plugins { buildDir = projectBuildDir version = projectVersion -distsDirName = "./" +base { + distsDirectory = layout.buildDirectory.dir("./") +} // OS Package plugin configuration ospackage { @@ -33,8 +35,8 @@ ospackage { into pkgInstallFolder - user pkgUser - permissionGroup pkgUser + user = pkgUser + permissionGroup = pkgUser // Copy the actual .jar file from(mainJar) { @@ -42,19 +44,25 @@ ospackage { rename { String fileName -> "${pkgName}.jar" } - fileMode 0500 + filePermissions { + unix('r-x------') // 0500 + } into "bin" } if("${pkgCopyInstallScripts}".equalsIgnoreCase("true")) { // Copy the install files from("${buildDir}/bin/install/install.sh") { - fileMode 0775 + filePermissions { + unix('rwxrwxr-x') // 0775 + } into "bin/install" } from("${buildDir}/bin/install/upgrade.sh") { - fileMode 0775 + filePermissions { + unix('rwxrwxr-x') // 0775 + } into "bin/install" } @@ -67,14 +75,18 @@ ospackage { from("${buildDir}/conf") { exclude "${pkgName}.conf" fileType CONFIG | NOREPLACE - fileMode 0754 + filePermissions { + unix('rwxr-xr--') // 0754 + } into "conf" } // Copy the data files from("${buildDir}/data") { fileType CONFIG | NOREPLACE - fileMode 0754 + filePermissions { + unix('rwxr-xr--') // 0754 + } into "data" } @@ -102,7 +114,9 @@ buildRpm { include "${pkgName}.conf" filter(ReplaceTokens, tokens: ['pkg.platform': 'rpm']) fileType CONFIG | NOREPLACE - fileMode 0754 + filePermissions { + unix('rwxr-xr--') // 0754 + } into "${pkgInstallFolder}/conf" } @@ -111,13 +125,15 @@ buildRpm { preUninstall file("${buildDir}/control/rpm/prerm") postUninstall file("${buildDir}/control/rpm/postrm") - user pkgUser - permissionGroup pkgUser + user = pkgUser + permissionGroup = pkgUser // Copy the system unit files from("${buildDir}/control/template.service") { addParentDirs = false - fileMode 0644 + filePermissions { + unix('rw-r--r--') // 0644 + } into "/usr/lib/systemd/system" rename { String filename -> "${pkgName}.service" @@ -143,7 +159,9 @@ buildDeb { include "${pkgName}.conf" filter(ReplaceTokens, tokens: ['pkg.platform': 'deb']) fileType CONFIG | NOREPLACE - fileMode 0754 + filePermissions { + unix('rwxr-xr--') // 0754 + } into "${pkgInstallFolder}/conf" } @@ -157,13 +175,15 @@ buildDeb { preUninstall file("${buildDir}/control/deb/prerm") postUninstall file("${buildDir}/control/deb/postrm") - user pkgUser - permissionGroup pkgUser + user = pkgUser + permissionGroup = pkgUser // Copy the system unit files from("${buildDir}/control/template.service") { addParentDirs = false - fileMode 0644 + filePermissions { + unix('rw-r--r--') // 0644 + } into "/lib/systemd/system" rename { String filename -> "${pkgName}.service" diff --git a/packaging/js/build.gradle b/packaging/js/build.gradle index 001dafabb0..13a2779f34 100644 --- a/packaging/js/build.gradle +++ b/packaging/js/build.gradle @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import org.apache.tools.ant.filters.ReplaceTokens plugins { id "com.netflix.nebula.ospackage" version "12.1.1" @@ -21,7 +20,9 @@ plugins { buildDir = projectBuildDir version = projectVersion -distsDirName = "./" +base { + distsDirectory = layout.buildDirectory.dir("./") +} // OS Package plugin configuration ospackage { @@ -33,18 +34,22 @@ ospackage { into pkgInstallFolder - user pkgUser - permissionGroup pkgUser + user = pkgUser + permissionGroup = pkgUser // Copy the executable file from("${buildDir}/package/linux/bin/${pkgName}") { - fileMode 0500 + filePermissions { + unix('r-x------') // 0500 + } into "bin" } // Copy the init file from("${buildDir}/package/linux/init/template") { - fileMode 0500 + filePermissions { + unix('r-x------') // 0500 + } into "init" rename { String filename -> "${pkgName}" @@ -54,7 +59,9 @@ ospackage { // Copy the config files from("${buildDir}/package/linux/conf") { fileType CONFIG | NOREPLACE - fileMode 0754 + filePermissions { + unix('rwxr-xr--') // 0754 + } into "conf" } @@ -78,13 +85,15 @@ buildRpm { preUninstall file("${buildDir}/control/rpm/prerm") postUninstall file("${buildDir}/control/rpm/postrm") - user pkgUser - permissionGroup pkgUser + user = pkgUser + permissionGroup = pkgUser // Copy the system unit files from("${buildDir}/control/template.service") { addParentDirs = false - fileMode 0644 + filePermissions { + unix('rw-r--r--') // 0644 + } into "/usr/lib/systemd/system" rename { String filename -> "${pkgName}.service" @@ -111,13 +120,15 @@ buildDeb { preUninstall file("${buildDir}/control/deb/prerm") postUninstall file("${buildDir}/control/deb/postrm") - user pkgUser - permissionGroup pkgUser + user = pkgUser + permissionGroup = pkgUser // Copy the system unit files from("${buildDir}/control/template.service") { addParentDirs = false - fileMode 0644 + filePermissions { + unix('rw-r--r--') // 0644 + } into "/lib/systemd/system" rename { String filename -> "${pkgName}.service" diff --git a/pom.xml b/pom.xml index a3498dd788..806659f567 100755 --- a/pom.xml +++ b/pom.xml @@ -650,7 +650,7 @@ org.thingsboard gradle-maven-plugin - 1.0.12 + 1.0.15 com.github.eirslett From 2264a40285b9d05d79f79b34308a1c7072e219b6 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Fri, 6 Feb 2026 09:02:44 +0200 Subject: [PATCH 015/224] fix: resolve race condition in TbRestApiCallNodeTest The deleteRequestWithBody and deleteRequestWithoutBody tests used time-based synchronization (Thread.sleep) to wait for the async WebClient response callback. Under CI load, the callback could fire after the verify() check, causing flaky failures. Replace the sleep-based approach with Mockito's timeout() on verify, which properly polls for the async interaction. Co-Authored-By: Claude Opus 4.6 --- .../engine/rest/TbRestApiCallNodeTest.java | 31 +++---------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java index 5ad0bccf03..a93417275f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java @@ -56,6 +56,7 @@ import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) @@ -115,18 +116,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest { assertTrue(request.containsHeader("Foo"), "Custom header included"); assertEquals("Bar", request.getFirstHeader("Foo").getValue(), "Custom header value"); response.setStatusCode(200); - new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(1000L); - } catch (InterruptedException e) { - // ignore - } finally { - latch.countDown(); - } - } - }).start(); + latch.countDown(); } catch (Exception e) { System.out.println("Exception handling request: " + e.toString()); e.printStackTrace(); @@ -158,7 +148,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest { ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); - verify(ctx).transformMsg(msgCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); + verify(ctx, timeout(10_000)).transformMsg(msgCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); assertNotSame(metaData, metadataCaptor.getValue()); assertEquals(TbMsg.EMPTY_JSON_OBJECT, dataCaptor.getValue()); @@ -184,18 +174,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest { assertTrue(request.containsHeader("Foo"), "Custom header included"); assertEquals("Bar", request.getFirstHeader("Foo").getValue(), "Custom header value"); response.setStatusCode(200); - new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(1000L); - } catch (InterruptedException e) { - // ignore - } finally { - latch.countDown(); - } - } - }).start(); + latch.countDown(); } catch (Exception e) { System.out.println("Exception handling request: " + e.toString()); e.printStackTrace(); @@ -227,7 +206,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest { ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); - verify(ctx).transformMsg(msgCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); + verify(ctx, timeout(10_000)).transformMsg(msgCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); assertNotSame(metaData, metadataCaptor.getValue()); assertEquals(TbMsg.EMPTY_JSON_OBJECT, dataCaptor.getValue()); From c4ff463296f729d6b9a06a9e47cdaf504cec6919 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 6 Feb 2026 15:55:39 +0200 Subject: [PATCH 016/224] Add own pluginRepository for gradle-maven-plugin --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index 806659f567..fb56c476ef 100755 --- a/pom.xml +++ b/pom.xml @@ -1967,4 +1967,10 @@ + + + thingsboard-repo + https://repo.thingsboard.io/artifactory/libs-release-public + + From 19b3c113f72c9cfbf46fe2bbeaa9ca08e2597a87 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 6 Feb 2026 16:28:14 +0200 Subject: [PATCH 017/224] Try fix Dockerfiles for tb-postgres and tb-cassandra --- msa/tb/docker-cassandra/Dockerfile | 11 +++++------ msa/tb/docker-postgres/Dockerfile | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/msa/tb/docker-cassandra/Dockerfile b/msa/tb/docker-cassandra/Dockerfile index 74c33b2bfa..358f2907c8 100644 --- a/msa/tb/docker-cassandra/Dockerfile +++ b/msa/tb/docker-cassandra/Dockerfile @@ -44,15 +44,14 @@ COPY logback.xml ${pkg.name}.conf start-db.sh stop-db.sh start-tb.sh upgrade-tb. RUN apt-get update \ && apt-get install -y --no-install-recommends wget nmap procps gnupg2 \ - && echo "deb http://apt.postgresql.org/pub/repos/apt/ $(. /etc/os-release && echo -n $VERSION_CODENAME)-pgdg main" | tee --append /etc/apt/sources.list.d/pgdg.list > /dev/null \ - && wget -q https://www.postgresql.org/media/keys/ACCC4CF8.asc -O- | apt-key add - \ - && echo "deb https://debian.cassandra.apache.org 40x main" | tee -a /etc/apt/sources.list.d/cassandra.sources.list > /dev/null \ - && wget -q https://downloads.apache.org/cassandra/KEYS -O- | apt-key add - \ + && mkdir -p /etc/apt/keyrings \ + && wget -q https://www.postgresql.org/media/keys/ACCC4CF8.asc -O- | gpg --dearmor -o /etc/apt/keyrings/postgresql.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/postgresql.gpg] http://apt.postgresql.org/pub/repos/apt/ $(. /etc/os-release && echo -n $VERSION_CODENAME)-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list > /dev/null \ + && wget -q https://downloads.apache.org/cassandra/KEYS -O- | gpg --dearmor -o /etc/apt/keyrings/cassandra.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/cassandra.gpg] https://debian.cassandra.apache.org 40x main" | tee /etc/apt/sources.list.d/cassandra.sources.list > /dev/null \ && apt-get update \ && apt-get install -y --no-install-recommends cassandra cassandra-tools postgresql-${PG_MAJOR} \ && rm -rf /var/lib/apt/lists/* \ - && update-rc.d cassandra disable \ - && update-rc.d postgresql disable \ && apt-get purge -y --auto-remove \ && sed -i.old '/ulimit/d' /etc/init.d/cassandra \ && chmod a+x /tmp/*.sh \ diff --git a/msa/tb/docker-postgres/Dockerfile b/msa/tb/docker-postgres/Dockerfile index b74727c166..6ce066d1ed 100644 --- a/msa/tb/docker-postgres/Dockerfile +++ b/msa/tb/docker-postgres/Dockerfile @@ -36,12 +36,12 @@ COPY logback.xml ${pkg.name}.conf start-db.sh stop-db.sh start-tb.sh upgrade-tb. RUN apt-get update \ && apt-get install -y --no-install-recommends wget gnupg2 \ - && echo "deb http://apt.postgresql.org/pub/repos/apt/ $(. /etc/os-release && echo -n $VERSION_CODENAME)-pgdg main" | tee --append /etc/apt/sources.list.d/pgdg.list > /dev/null \ - && wget -q https://www.postgresql.org/media/keys/ACCC4CF8.asc -O- | apt-key add - \ + && mkdir -p /etc/apt/keyrings \ + && wget -q https://www.postgresql.org/media/keys/ACCC4CF8.asc -O- | gpg --dearmor -o /etc/apt/keyrings/postgresql.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/postgresql.gpg] http://apt.postgresql.org/pub/repos/apt/ $(. /etc/os-release && echo -n $VERSION_CODENAME)-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list > /dev/null \ && apt-get update \ && apt-get install -y --no-install-recommends postgresql-${PG_MAJOR} \ && rm -rf /var/lib/apt/lists/* \ - && update-rc.d postgresql disable \ && apt-get purge -y --auto-remove \ && chmod a+x /tmp/*.sh \ && mv /tmp/start-tb.sh /usr/bin \ From 8414db39409f18859b8676080dda95aebbda304f Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 6 Feb 2026 16:36:28 +0200 Subject: [PATCH 018/224] Fix TbelInvokeDocsIoTest --- .../server/service/script/TbelInvokeDocsIoTest.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java b/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java index 67e4ba8f32..76caa4d53c 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java @@ -2355,7 +2355,8 @@ class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { expected.put("date_1", d1.toString()); TbDate d2 = new TbDate(2023, 8, 6, 4, 4, 5, "Europe/Berlin"); expected.put("dLocal_2", d2.toLocaleString()); - expected.put("dLocal_2_us", "8/5/23, 10:04:05 PM"); + // Java 21+ uses narrow no-break space (U+202F) before AM/PM per CLDR 42+ + expected.put("dLocal_2_us", Runtime.version().feature() >= 21 ? "8/5/23, 10:04:05\u202FPM" : "8/5/23, 10:04:05 PM"); expected.put("dIso_2", d2.toISOString()); expected.put("date_2", d2.toString()); Object actual = invokeScript(evalScript(decoderStr), msgStr); @@ -2387,7 +2388,8 @@ class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { """, s2); LinkedHashMap expected = new LinkedHashMap<>(); TbDate d1 = new TbDate(2023, 8, 6, 4, 4, 5); - expected.put("dLocal_1_us", "8/6/23, 4:04:05 AM"); + // Java 21+ uses narrow no-break space (U+202F) before AM/PM per CLDR 42+ + expected.put("dLocal_1_us", Runtime.version().feature() >= 21 ? "8/6/23, 4:04:05\u202FAM" : "8/6/23, 4:04:05 AM"); expected.put("dIso_1", d1.toISOString()); expected.put("d1", d1.toString()); TbDate d2 = new TbDate(s2); @@ -2419,7 +2421,8 @@ class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { TbDate d = new TbDate(2023, 8, 6, 4, 4, 5, "Europe/Berlin"); expected.put("dIso", d.toISOString()); expected.put("dLocal_utc", d.toLocaleString("UTC")); - expected.put("dLocal_us", "8/5/23, 10:04:05 PM"); + // Java 21+ uses narrow no-break space (U+202F) before AM/PM per CLDR 42+ + expected.put("dLocal_us", Runtime.version().feature() >= 21 ? "8/5/23, 10:04:05\u202FPM" : "8/5/23, 10:04:05 PM"); expected.put("dLocal_de", "06.08.23, 04:04:05"); Object actual = invokeScript(evalScript(decoderStr), msgStr); assertEquals(expected, actual); From 20b1b8f41f049a0ea71c4eb79c9277db1f9aabb9 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 6 Feb 2026 16:46:31 +0200 Subject: [PATCH 019/224] Update Edge installation instructions --- .../install/centos/instructions.md | 28 ++++++++++--------- .../install/ubuntu/instructions.md | 2 ++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/application/src/main/data/json/edge/instructions/install/centos/instructions.md b/application/src/main/data/json/edge/instructions/install/centos/instructions.md index be0a4903a4..964cdcb887 100644 --- a/application/src/main/data/json/edge/instructions/install/centos/instructions.md +++ b/application/src/main/data/json/edge/instructions/install/centos/instructions.md @@ -1,10 +1,12 @@ -Here is the list of commands that can be used to quickly install ThingsBoard Edge on RHEL/CentOS 7/8 and connect to the server. +Here is the list of commands that can be used to quickly install ThingsBoard Edge on RHEL/CentOS 9/10 and connect to the server. + +**Note:** OpenJDK 25 requires RHEL/CentOS 9+ or RHEL/CentOS 10+. Earlier versions (RHEL/CentOS 7, 8) are not supported. #### Prerequisites Before continuing to installation, execute the following commands to install the necessary tools: ```bash -sudo yum install -y nano wget && sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm +sudo dnf install -y nano wget {:copy-code} ``` @@ -12,7 +14,7 @@ sudo yum install -y nano wget && sudo yum install -y https://dl.fedoraproject.or ThingsBoard service is running on Java 25. To install OpenJDK 25, follow these instructions: ```bash -sudo dnf install java-25-openjdk +sudo dnf install -y java-25-openjdk {:copy-code} ``` @@ -41,7 +43,7 @@ OpenJDK 64-Bit Server VM (build ...) #### Step 2. Configure ThingsBoard Edge Database ThingsBoard Edge supports **SQL** and **hybrid** database configurations. -In this guide, we’ll use an **SQL** database. +In this guide, we'll use an **SQL** database. For more details about the hybrid setup, please refer to the official installation instructions on the ThingsBoard documentation site. To install the PostgreSQL database, run these commands: @@ -54,19 +56,19 @@ sudo dnf update Install the repository RPM: -* **For CentOS/RHEL 8:** +* **For RHEL/CentOS 9:** ```bash -# Install the repository RPM (For CentOS/RHEL 8): -sudo sudo dnf -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm +# Install the repository RPM (for RHEL/CentOS 9): +sudo dnf -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm {:copy-code} ``` -* **For CentOS/RHEL 9:** +* **For RHEL/CentOS 10:** ```bash -# Install the repository RPM (for CentOS 9): -sudo dnf -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm +# Install the repository RPM (for RHEL/CentOS 10): +sudo dnf -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-10-x86_64/pgdg-redhat-repo-latest.noarch.rpm {:copy-code} ``` @@ -91,8 +93,8 @@ sudo -u postgres psql -c "\password" Then, enter and confirm the password. -Since ThingsBoard Edge uses the PostgreSQL database for local storage, configuring MD5 authentication ensures that only authenticated users or -applications can access the database, thus protecting your data. After configuring the password, +Since ThingsBoard Edge uses the PostgreSQL database for local storage, configuring MD5 authentication ensures that only authenticated users or +applications can access the database, thus protecting your data. After configuring the password, edit the pg_hba.conf file to use MD5 hashing for authentication instead of the default method (ident) for local IPv4 connections. To replace ident with md5, run the following command: @@ -102,7 +104,7 @@ sudo sed -i 's/^host\s\+all\s\+all\s\+127\.0\.0\.1\/32\s\+ident/host all {:copy-code} ``` -Then run the command that will restart the PostgreSQL service to apply configuration changes, connect to the database as a postgres user, +Then run the command that will restart the PostgreSQL service to apply configuration changes, connect to the database as a postgres user, and create the ThingsBoard Edge database (tb_edge). To connect to the PostgreSQL database, enter the PostgreSQL password. ```bash diff --git a/application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md b/application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md index f0e7ac6a92..6f74c09c08 100644 --- a/application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md +++ b/application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md @@ -1,5 +1,7 @@ Here is the list of commands that can be used to quickly install ThingsBoard Edge on Ubuntu Server and connect to the server. +**Note:** OpenJDK 25 requires Ubuntu 22.04 LTS or newer. Earlier versions (20.04 and below) are not supported. + #### Step 1. Install Java 25 (OpenJDK) ThingsBoard service is running on Java 25. To install OpenJDK 25, follow these instructions: From 50ee9c4888bba9491387cf75acc7d4c50a7f3bbc Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Sun, 8 Feb 2026 17:19:12 +0200 Subject: [PATCH 020/224] feat: add RestClient support for getCalculatedFields API Add getCalculatedFields method to RestClient that calls the /api/calculatedFields endpoint with filtering by types, entityType, entities, and names. Uses Apache URIBuilder to construct the URL. Co-Authored-By: Claude Opus 4.6 --- rest-client/pom.xml | 4 ++ .../thingsboard/rest/client/RestClient.java | 45 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/rest-client/pom.xml b/rest-client/pom.xml index c12ba7be2c..159ef826a0 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -51,6 +51,10 @@ com.auth0 java-jwt + + org.apache.httpcomponents.core5 + httpcore5 + diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index bd1d7b05c3..102fb90d1f 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -22,6 +22,7 @@ import com.google.common.base.Strings; import lombok.Getter; import lombok.SneakyThrows; import org.apache.commons.io.IOUtils; +import org.apache.hc.core5.net.URIBuilder; import org.apache.commons.lang3.concurrent.LazyInitializer; import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.io.ByteArrayResource; @@ -93,6 +94,8 @@ import org.thingsboard.server.common.data.asset.AssetSearchQuery; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldInfo; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.domain.Domain; import org.thingsboard.server.common.data.domain.DomainInfo; @@ -210,11 +213,13 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle; import java.io.Closeable; import java.io.IOException; import java.net.URI; +import java.net.URISyntaxException; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; @@ -4309,6 +4314,46 @@ public class RestClient implements Closeable { } + @SneakyThrows(URISyntaxException.class) + public PageData getCalculatedFields(PageLink pageLink, + Set types, + EntityType entityType, + Set entities, + Set names) { + var urlBuilder = new URIBuilder(baseURL).appendPath("/api/calculatedFields"); + urlBuilder.addParameter("pageSize", String.valueOf(pageLink.getPageSize())); + urlBuilder.addParameter("page", String.valueOf(pageLink.getPage())); + if (!isEmpty(pageLink.getTextSearch())) { + urlBuilder.addParameter("textSearch", pageLink.getTextSearch()); + } + if (pageLink.getSortOrder() != null) { + urlBuilder.addParameter("sortProperty", pageLink.getSortOrder().getProperty()); + urlBuilder.addParameter("sortOrder", pageLink.getSortOrder().getDirection().name()); + } + if (!CollectionUtils.isEmpty(types)) { + for (CalculatedFieldType type : types) { + urlBuilder.addParameter("types", type.name()); + } + } + if (entityType != null) { + urlBuilder.addParameter("entityType", entityType.name()); + } + if (!CollectionUtils.isEmpty(entities)) { + for (UUID entity : entities) { + urlBuilder.addParameter("entities", entity.toString()); + } + } + if (!CollectionUtils.isEmpty(names)) { + for (String name : names) { + urlBuilder.addParameter("name", name); + } + } + return restTemplate.exchange( + urlBuilder.build(), + HttpMethod.GET, HttpEntity.EMPTY, + new ParameterizedTypeReference>() {}).getBody(); + } + public void deleteCalculatedField(CalculatedFieldId calculatedFieldId) { restTemplate.delete(baseURL + "/api/calculatedField/{calculatedFieldId}", calculatedFieldId.getId()); } From ceb407f6c171b716ac5ac2ce6e16d97412472086 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Mon, 9 Feb 2026 10:03:32 +0200 Subject: [PATCH 021/224] chore: add .claude/ to .gitignore Co-Authored-By: Claude Opus 4.6 --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e9a4e11b85..777a52efc7 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ rebuild-docker.sh */.run/** .run/** .run +.claude/ From 20afefc33cd1179ea6e9ede140f637ca460626f4 Mon Sep 17 00:00:00 2001 From: Nikita Mazurenko Date: Mon, 9 Feb 2026 10:15:51 +0200 Subject: [PATCH 022/224] Set next 4.2 EdgeVersion proto value to canonical one --- common/edge-api/src/main/proto/edge.proto | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 1998789d91..8e14926fbb 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -45,9 +45,9 @@ enum EdgeVersion { V_4_1_0 = 11; V_4_2_0 = 12; V_4_2_1_2 = 14; - V_4_2_1_3 = 420; + V_4_2_1_3 = 4213; - V_LATEST = 999; + V_LATEST = 99999; } /** From db14860e72e7962d1a19eaf982fcf2aa5fc6700b Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Mon, 9 Feb 2026 11:34:29 +0200 Subject: [PATCH 023/224] added appropriate swagger annotations for correct open-api json --- .../server/config/SwaggerConfiguration.java | 126 ++++++++++++++---- .../TwoFactorAuthConfigController.java | 1 + .../server/controller/UserController.java | 9 +- .../controller/WidgetTypeController.java | 2 +- .../common/data/ApiUsageStateValue.java | 3 + .../server/common/data/AttributeScope.java | 2 + .../server/common/data/CoapDeviceType.java | 3 + .../data/DeviceProfileProvisionType.java | 3 + .../server/common/data/DeviceProfileType.java | 3 + .../common/data/DeviceTransportType.java | 3 + .../server/common/data/EntityType.java | 2 + .../server/common/data/ResourceSubType.java | 3 + .../server/common/data/ResourceType.java | 2 + .../server/common/data/TenantProfileType.java | 3 + .../common/data/TransportPayloadType.java | 3 + .../thingsboard/server/common/data/User.java | 4 +- .../common/data/ai/dto/TbChatRequest.java | 24 +++- .../common/data/ai/dto/TbChatResponse.java | 12 +- .../common/data/ai/model/AiModelConfig.java | 37 +++++ .../ai/provider/OllamaProviderConfig.java | 11 ++ .../common/data/alarm/AlarmComment.java | 4 +- .../common/data/alarm/AlarmCommentType.java | 3 + .../common/data/alarm/AlarmSearchStatus.java | 3 + .../common/data/alarm/AlarmSeverity.java | 3 + .../server/common/data/alarm/AlarmStatus.java | 3 + .../common/data/cf/CalculatedFieldType.java | 3 + .../data/DefaultDeviceConfiguration.java | 3 +- .../data/device/data/DeviceConfiguration.java | 11 +- .../data/DeviceTransportConfiguration.java | 13 +- .../common/data/device/data/PowerMode.java | 3 + .../device/data/PowerSavingConfiguration.java | 2 + .../device/profile/AlarmConditionSpec.java | 12 ++ .../profile/AlarmConditionSpecType.java | 3 + .../data/device/profile/AlarmSchedule.java | 12 ++ .../device/profile/AlarmScheduleType.java | 3 + ...apDeviceProfileTransportConfiguration.java | 3 + .../DefaultDeviceProfileConfiguration.java | 3 + ...ltDeviceProfileTransportConfiguration.java | 3 + .../profile/DeviceProfileConfiguration.java | 10 ++ .../DeviceProfileTransportConfiguration.java | 14 ++ .../profile/DurationAlarmConditionSpec.java | 5 + ...2mDeviceProfileTransportConfiguration.java | 13 +- ...ttDeviceProfileTransportConfiguration.java | 10 ++ .../profile/RepeatingAlarmConditionSpec.java | 4 + .../profile/SimpleAlarmConditionSpec.java | 3 + ...mpDeviceProfileTransportConfiguration.java | 5 + .../TransportPayloadTypeConfiguration.java | 13 ++ .../profile/lwm2m/ObjectAttributes.java | 2 + .../profile/lwm2m/OtherConfiguration.java | 2 + .../lwm2m/TelemetryMappingConfiguration.java | 8 ++ .../lwm2m/TelemetryObserveStrategy.java | 2 + .../server/common/data/event/EventFilter.java | 14 +- .../server/common/data/event/EventType.java | 2 + .../data/housekeeper/HousekeeperTaskType.java | 2 + .../server/common/data/job/JobResult.java | 21 ++- .../server/common/data/job/JobStatus.java | 3 + .../server/common/data/job/JobType.java | 2 + .../server/common/data/kv/DataType.java | 2 + .../server/common/data/kv/IntervalType.java | 3 + .../server/common/data/limit/LimitedApi.java | 2 + .../common/data/mail/MailOauth2Provider.java | 3 + .../data/mobile/app/MobileAppStatus.java | 3 + .../data/mobile/layout/DefaultPageId.java | 3 + .../common/data/mobile/layout/MobilePage.java | 13 ++ .../data/mobile/layout/MobilePageType.java | 3 + .../mobile/qrCodeSettings/BadgePosition.java | 4 + .../NotificationDeliveryMethod.java | 2 + .../data/notification/NotificationStatus.java | 3 + .../data/notification/NotificationType.java | 2 + .../trigger/ResourcesShortageTrigger.java | 2 + ...signmentNotificationRuleTriggerConfig.java | 9 ++ ...mCommentNotificationRuleTriggerConfig.java | 9 ++ .../AlarmNotificationRuleTriggerConfig.java | 10 +- ...ageLimitNotificationRuleTriggerConfig.java | 6 + ...ActivityNotificationRuleTriggerConfig.java | 8 ++ ...nFailureNotificationRuleTriggerConfig.java | 5 + ...nnectionNotificationRuleTriggerConfig.java | 7 + ...iesLimitNotificationRuleTriggerConfig.java | 5 + ...tyActionNotificationRuleTriggerConfig.java | 5 + ...mVersionNotificationRuleTriggerConfig.java | 3 + .../config/NotificationRuleTriggerConfig.java | 24 ++++ .../config/NotificationRuleTriggerType.java | 2 + ...teLimitsNotificationRuleTriggerConfig.java | 5 + ...ShortageNotificationRuleTriggerConfig.java | 3 + ...cleEventNotificationRuleTriggerConfig.java | 8 +- ...gFailureNotificationRuleTriggerConfig.java | 3 + .../targets/NotificationTargetType.java | 2 + .../targets/platform/UsersFilterType.java | 2 + .../targets/slack/SlackConversationType.java | 3 + .../DeliveryMethodNotificationTemplate.java | 15 +++ ...ailDeliveryMethodNotificationTemplate.java | 3 + ...amsDeliveryMethodNotificationTemplate.java | 12 ++ ...AppDeliveryMethodNotificationTemplate.java | 4 + ...ackDeliveryMethodNotificationTemplate.java | 2 + ...SmsDeliveryMethodNotificationTemplate.java | 2 + ...WebDeliveryMethodNotificationTemplate.java | 4 + .../server/common/data/oauth2/MapperType.java | 3 + .../common/data/oauth2/PlatformType.java | 3 + .../server/common/data/oauth2/SchemeType.java | 3 + .../data/oauth2/TenantNameStrategyType.java | 3 + .../common/data/ota/ChecksumAlgorithm.java | 3 + .../common/data/ota/OtaPackageType.java | 2 + .../data/ota/OtaPackageUpdateStatus.java | 3 + .../data/plugin/ComponentLifecycleEvent.java | 2 + .../data/plugin/ComponentLifecycleState.java | 3 + .../common/data/plugin/ComponentScope.java | 3 + .../common/data/plugin/ComponentType.java | 3 + .../common/data/query/AlarmCountQuery.java | 2 + .../common/data/query/AliasEntityType.java | 3 + .../data/query/BooleanFilterPredicate.java | 2 + .../data/query/ComplexFilterPredicate.java | 2 + .../data/query/DynamicValueSourceType.java | 3 + .../data/query/EntityDataSortOrder.java | 2 + .../common/data/query/EntityFilter.java | 23 ++++ .../common/data/query/EntityFilterType.java | 3 + .../common/data/query/EntityKeyType.java | 3 + .../common/data/query/EntityKeyValueType.java | 3 + .../data/query/FilterPredicateType.java | 3 + .../common/data/query/KeyFilterPredicate.java | 13 ++ .../data/query/NumericFilterPredicate.java | 2 + .../data/query/SimpleKeyFilterPredicate.java | 7 + .../data/query/StringFilterPredicate.java | 2 + .../data/queue/ProcessingStrategyType.java | 3 + .../common/data/queue/SubmitStrategyType.java | 3 + .../data/relation/EntitySearchDirection.java | 3 + .../data/relation/RelationTypeGroup.java | 3 + .../server/common/data/rpc/RpcStatus.java | 2 + .../common/data/rule/RuleChainType.java | 3 + .../server/common/data/rule/RuleType.java | 3 + .../server/common/data/rule/Scope.java | 3 + .../common/data/script/ScriptLanguage.java | 3 + .../common/data/security/Authority.java | 3 + .../data/security/DeviceCredentialsType.java | 3 + .../mfa/account/AccountTwoFaSettings.java | 6 + .../model/mfa/account/TwoFaAccountConfig.java | 13 ++ .../model/mfa/provider/TwoFaProviderType.java | 3 + .../data/settings/UserDashboardAction.java | 3 + .../data/settings/UserSettingsType.java | 2 + .../config/SmppSmsProviderConfiguration.java | 4 +- .../sms/config/SmsProviderConfiguration.java | 12 ++ .../data/sms/config/SmsProviderType.java | 3 + .../common/data/sync/ie/EntityExportData.java | 19 +++ .../sync/vc/request/create/SyncStrategy.java | 3 + .../request/create/VersionCreateRequest.java | 11 ++ .../create/VersionCreateRequestType.java | 3 + .../vc/request/load/VersionLoadRequest.java | 11 ++ .../request/load/VersionLoadRequestType.java | 3 + .../data/transport/resource/ResourceType.java | 3 + .../snmp/AuthenticationProtocol.java | 3 + .../data/transport/snmp/PrivacyProtocol.java | 3 + .../transport/snmp/SnmpCommunicationSpec.java | 2 + .../snmp/config/SnmpCommunicationConfig.java | 14 ++ .../common/data/widget/DeprecatedFilter.java | 3 + .../transport/http/DeviceApiController.java | 2 + 154 files changed, 901 insertions(+), 48 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java index d8eb98ab57..967b33299e 100644 --- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java @@ -322,7 +322,7 @@ public class SwaggerConfiguration { } private OpenApiCustomizer customOpenApiCustomizer() { - var loginForm = new SecurityRequirement().addList("HTTP login form", Arrays.asList( + var loginForm = new SecurityRequirement().addList("http_login_form", Arrays.asList( Authority.SYS_ADMIN.name(), Authority.TENANT_ADMIN.name(), Authority.CUSTOMER_USER.name() @@ -350,11 +350,83 @@ public class SwaggerConfiguration { }); sortedPaths.setExtensions(paths.getExtensions()); openAPI.setPaths(sortedPaths); + + // Fix schemas with additionalProperties to ensure they have type: object + if (openAPI.getComponents() != null && openAPI.getComponents().getSchemas() != null) { + Map 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 maps: replace oneOf in additionalProperties with base type reference + schemas.values().forEach(schema -> { + if (schema.getProperties() != null) { + 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()) { + // Find the base type from discriminator mappings + String baseType = findBaseTypeForOneOf(schemas, additionalProps.getOneOf()); + + if (baseType != null) { + // Replace oneOf with direct reference to base type + Schema refSchema = new Schema<>(); + refSchema.set$ref("#/components/schemas/" + baseType); + prop.setAdditionalProperties(refSchema); + log.debug("Replaced oneOf with $ref to {} in property {}", baseType, propName); + } + } + } + } + }); + } + }); + } + var sortedSchemas = new TreeMap<>(openAPI.getComponents().getSchemas()); openAPI.getComponents().setSchemas(new LinkedHashMap<>(sortedSchemas)); }; } + // Add this helper method + private String findBaseTypeForOneOf(Map schemas, List oneOfSchemas) { + if (oneOfSchemas.isEmpty()) { + return null; + } + + // Extract the first subtype name from oneOf + String firstRef = oneOfSchemas.get(0).get$ref(); + if (firstRef == null) { + return null; + } + + String subtypeName = firstRef.substring(firstRef.lastIndexOf('/') + 1); + + // Find which schema has this subtype in its discriminator mapping + return schemas.entrySet().stream() + .filter(entry -> { + Schema schema = entry.getValue(); + if (schema.getDiscriminator() != null && schema.getDiscriminator().getMapping() != null) { + return schema.getDiscriminator().getMapping().values().stream() + .anyMatch(ref -> ref.endsWith("/" + subtypeName)); + } + return false; + }) + .map(Map.Entry::getKey) + .findFirst() + .orElse(null); + } + private Tag tagsCustomization(Map.Entry entry) { var tagItem = tagItemFromPathItem(entry.getValue()); @@ -449,29 +521,37 @@ 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(); - credentialsExpiredSchema.$ref("#/components/schemas/ThingsboardCredentialsExpiredResponse"); - apiResponses.addApiResponse("401 ", errorResponse("Unauthorized (**Expired credentials**)", - Map.of( - "credentials-expired", errorExample("Expired credentials", - ThingsboardCredentialsExpiredResponse.of("User password expired!", StringUtils.randomAlphanumeric(30))) - ), - credentialsExpiredSchema + // Combine all 401 examples into a single map + Map 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)))); + + // Create a schema that can represent both response types using oneOf + Schema unauthorizedSchema = new Schema<>(); + unauthorizedSchema.oneOf(List.of( + new Schema().$ref("#/components/schemas/ThingsboardErrorResponse"), + new Schema().$ref("#/components/schemas/ThingsboardCredentialsExpiredResponse") )); + + // Add single 401 response with all examples + apiResponses.addApiResponse("401", errorResponse("Unauthorized", unauthorizedExamples, unauthorizedSchema)); + return apiResponses; } diff --git a/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java b/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java index 5ad64a9f7b..e797a205e2 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java @@ -266,6 +266,7 @@ public class TwoFactorAuthConfigController extends BaseController { @Data + @Schema public static class TwoFaAccountConfigUpdateRequest { private boolean useByDefault; } diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java index 6de2035940..03228e7c7d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/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.Schema; import jakarta.servlet.http.HttpServletRequest; @@ -461,10 +462,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 { @@ -472,8 +470,7 @@ 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. ") + @Hidden @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @GetMapping(value = "/user/settings") public JsonNode getUserSettings() throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java index f6671ffa41..5cb6156408 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java @@ -248,7 +248,7 @@ public class WidgetTypeController extends AutoCommitController { @GetMapping(value = "/widgetsBundles/{widgetsBundleId}/widgetTypes") public List getBundleWidgetTypesV2( @Parameter(description = "Widget Bundle Id", required = true) - @RequestParam("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException { + @PathVariable("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException { return getBundleWidgetTypes(strWidgetsBundleId); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageStateValue.java b/common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageStateValue.java index aa639f9c14..f05458358a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageStateValue.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageStateValue.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum ApiUsageStateValue { ENABLED, WARNING, DISABLED; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/AttributeScope.java b/common/data/src/main/java/org/thingsboard/server/common/data/AttributeScope.java index ea645f9757..1c5dd03327 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/AttributeScope.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/AttributeScope.java @@ -15,12 +15,14 @@ */ package org.thingsboard.server.common.data; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; +@Schema(enumAsRef = true) public enum AttributeScope { CLIENT_SCOPE(1), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/CoapDeviceType.java b/common/data/src/main/java/org/thingsboard/server/common/data/CoapDeviceType.java index c8862784b0..b506867f93 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/CoapDeviceType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/CoapDeviceType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum CoapDeviceType { DEFAULT, EFENTO diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileProvisionType.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileProvisionType.java index 10317a1eec..898cf8e752 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileProvisionType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileProvisionType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum DeviceProfileProvisionType { DISABLED, ALLOW_CREATE_NEW_DEVICES, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileType.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileType.java index cd7a18c9e7..e6e22e946b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum DeviceProfileType { DEFAULT } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java index c06ee27c18..0fac7e7bf7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum DeviceTransportType { DEFAULT, MQTT, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index a95208e457..b2d259f906 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import org.apache.commons.lang3.StringUtils; @@ -22,6 +23,7 @@ import java.util.Arrays; import java.util.EnumSet; import java.util.List; +@Schema(enumAsRef = true) public enum EntityType { TENANT(1), CUSTOMER(2), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ResourceSubType.java b/common/data/src/main/java/org/thingsboard/server/common/data/ResourceSubType.java index ac7b7466f5..e19c9308ed 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ResourceSubType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ResourceSubType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum ResourceSubType { IMAGE, SCADA_SYMBOL, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java b/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java index df73ea73c8..ae6262878e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java @@ -15,9 +15,11 @@ */ package org.thingsboard.server.common.data; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.RequiredArgsConstructor; +@Schema(enumAsRef = true) @RequiredArgsConstructor public enum ResourceType { LWM2M_MODEL("application/xml", false, false), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfileType.java b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfileType.java index 7965ce719c..8d79a7f149 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfileType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfileType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum TenantProfileType { DEFAULT } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TransportPayloadType.java b/common/data/src/main/java/org/thingsboard/server/common/data/TransportPayloadType.java index fbae54d0d3..9354d57fc7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TransportPayloadType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TransportPayloadType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum TransportPayloadType { JSON, PROTOBUF diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/User.java b/common/data/src/main/java/org/thingsboard/server/common/data/User.java index 69ba115af1..160054763f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/User.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/User.java @@ -91,7 +91,7 @@ public class User extends BaseDataWithAdditionalInfo 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 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; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ai/dto/TbChatRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/ai/dto/TbChatRequest.java index 66706fdb1c..c39a18ae6d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ai/dto/TbChatRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ai/dto/TbChatRequest.java @@ -20,10 +20,20 @@ import dev.langchain4j.data.message.Content; import dev.langchain4j.data.message.SystemMessage; import dev.langchain4j.data.message.UserMessage; import dev.langchain4j.model.chat.request.ChatRequest; +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; import org.thingsboard.server.common.data.ai.model.chat.AiChatModelConfig; +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; +import org.thingsboard.server.common.data.ai.model.chat.GitHubModelsChatModelConfig; +import org.thingsboard.server.common.data.ai.model.chat.GoogleAiGeminiChatModelConfig; +import org.thingsboard.server.common.data.ai.model.chat.GoogleVertexAiGeminiChatModelConfig; +import org.thingsboard.server.common.data.ai.model.chat.MistralAiChatModelConfig; +import org.thingsboard.server.common.data.ai.model.chat.OllamaChatModelConfig; +import org.thingsboard.server.common.data.ai.model.chat.OpenAiChatModelConfig; import java.util.ArrayList; import java.util.List; @@ -48,7 +58,19 @@ public record TbChatRequest( @Schema( requiredMode = Schema.RequiredMode.REQUIRED, accessMode = Schema.AccessMode.READ_WRITE, - description = "Configuration of the AI chat model that should execute the request" + description = "Configuration of the AI chat model that should execute the request", + discriminatorProperty = "modelType", + discriminatorMapping = { + @DiscriminatorMapping(value = "OPENAI", schema = OpenAiChatModelConfig.class), + @DiscriminatorMapping(value = "AZURE_OPENAI", schema = AzureOpenAiChatModelConfig.class), + @DiscriminatorMapping(value = "GOOGLE_AI", schema = GoogleAiGeminiChatModelConfig.class), + @DiscriminatorMapping(value = "VERTEX_AI", schema = GoogleVertexAiGeminiChatModelConfig.class), + @DiscriminatorMapping(value = "MISTRAL", schema = MistralAiChatModelConfig.class), + @DiscriminatorMapping(value = "ANTHROPIC", schema = AnthropicChatModelConfig.class), + @DiscriminatorMapping(value = "BEDROCK", schema = AmazonBedrockChatModelConfig.class), + @DiscriminatorMapping(value = "GITHUB", schema = GitHubModelsChatModelConfig.class), + @DiscriminatorMapping(value = "OLLAMA", schema = OllamaChatModelConfig.class) + } ) @NotNull @Valid AiChatModelConfig chatModelConfig diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ai/dto/TbChatResponse.java b/common/data/src/main/java/org/thingsboard/server/common/data/ai/dto/TbChatResponse.java index a553d66bf0..f1004790d5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ai/dto/TbChatResponse.java +++ b/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(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/AiModelConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/AiModelConfig.java index adcc176edc..18564e5665 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/AiModelConfig.java +++ b/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,45 @@ 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( + description = "Provider-specific configuration details", + discriminatorProperty = "provider", + discriminatorMapping = { + @DiscriminatorMapping(value = "OPENAI", schema = OpenAiProviderConfig.class), + @DiscriminatorMapping(value = "AZURE_OPENAI", schema = AzureOpenAiProviderConfig.class), + @DiscriminatorMapping(value = "GOOGLE_AI_GEMINI", schema = GoogleAiGeminiProviderConfig.class), + @DiscriminatorMapping(value = "GOOGLE_VERTEX_AI_GEMINI", schema = GoogleVertexAiGeminiProviderConfig.class), + @DiscriminatorMapping(value = "MISTRAL_AI", schema = MistralAiProviderConfig.class), + @DiscriminatorMapping(value = "ANTHROPIC", schema = AnthropicProviderConfig.class), + @DiscriminatorMapping(value = "AMAZON_BEDROCK", schema = AmazonBedrockProviderConfig.class), + @DiscriminatorMapping(value = "GITHUB_MODELS", schema = GitHubModelsProviderConfig.class), + @DiscriminatorMapping(value = "OLLAMA", schema = OllamaProviderConfig.class) + } + ) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/OllamaProviderConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/OllamaProviderConfig.java index 0a4da6c66c..6873281fca 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/OllamaProviderConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/OllamaProviderConfig.java @@ -17,6 +17,8 @@ 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; @@ -25,6 +27,15 @@ public record OllamaProviderConfig( @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, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java index 0a011c287f..92158057e7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java @@ -47,7 +47,7 @@ public class AlarmComment extends BaseData implements HasName { private UserId userId; @Schema(description = "Defines origination of comment. System type means comment was created by TB. OTHER type means comment was created by user.", example = "SYSTEM/OTHER", accessMode = Schema.AccessMode.READ_ONLY) private AlarmCommentType type; - @Schema(description = "JSON object with text of comment.") + @Schema(description = "JSON object with text of comment.", type = "object") @NoXss @Length(fieldName = "comment", max = 10000) @EqualsAndHashCode.Include @@ -56,7 +56,7 @@ public class AlarmComment extends BaseData 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(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentType.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentType.java index 3265d69d7e..9a944a0c42 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.alarm; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum AlarmCommentType { SYSTEM, OTHER; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSearchStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSearchStatus.java index b49fb4212a..962e5a581f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSearchStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSearchStatus.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.alarm; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum AlarmSearchStatus { ANY, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSeverity.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSeverity.java index e7776ce286..f1f345724d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSeverity.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSeverity.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.alarm; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum AlarmSeverity { CRITICAL, MAJOR, MINOR, WARNING, INDETERMINATE; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java index 1fd7f45a8b..cd7c59d423 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.alarm; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum AlarmStatus { ACTIVE_UNACK, ACTIVE_ACK, CLEARED_UNACK, CLEARED_ACK; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldType.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldType.java index c533aad712..8a7e8256ac 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.cf; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum CalculatedFieldType { SIMPLE, SCRIPT diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DefaultDeviceConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DefaultDeviceConfiguration.java index 85c1d83744..c605ffff9e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DefaultDeviceConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DefaultDeviceConfiguration.java @@ -19,12 +19,13 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.thingsboard.server.common.data.DeviceProfileType; -@Schema +@Schema(description = "Default device configuration", allOf = DeviceConfiguration.class) @Data public class DefaultDeviceConfiguration implements DeviceConfiguration { private static final long serialVersionUID = -2225378639573611325L; + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Device profile type") @Override public DeviceProfileType getType() { return DeviceProfileType.DEFAULT; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceConfiguration.java index 9fa449fdfa..6fe689d17c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceConfiguration.java @@ -19,12 +19,19 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 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.DeviceProfileType; import java.io.Serializable; -@Schema +@Schema( + description = "Device configuration", + discriminatorProperty = "type", + discriminatorMapping = { + @DiscriminatorMapping(value = "DEFAULT", schema = DefaultDeviceConfiguration.class) + } +) @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -34,7 +41,7 @@ import java.io.Serializable; @JsonSubTypes.Type(value = DefaultDeviceConfiguration.class, name = "DEFAULT")}) public interface DeviceConfiguration extends Serializable { - @JsonIgnore + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Device profile type") DeviceProfileType getType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java index 84c8e3b463..39032acd2c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java @@ -19,12 +19,23 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 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.DeviceTransportType; import java.io.Serializable; -@Schema +@Schema( + description = "Configuration for device transport", + discriminatorProperty = "type", + discriminatorMapping = { + @DiscriminatorMapping(value = "DEFAULT", schema = DefaultDeviceTransportConfiguration.class), + @DiscriminatorMapping(value = "MQTT", schema = MqttDeviceTransportConfiguration.class), + @DiscriminatorMapping(value = "COAP", schema = CoapDeviceTransportConfiguration.class), + @DiscriminatorMapping(value = "LWM2M", schema = Lwm2mDeviceTransportConfiguration.class), + @DiscriminatorMapping(value = "SNMP", schema = SnmpDeviceTransportConfiguration.class) + } +) @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/PowerMode.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/PowerMode.java index 28a673365d..e2f2c082ad 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/PowerMode.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/PowerMode.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.device.data; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum PowerMode { PSM, DRX, E_DRX } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/PowerSavingConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/PowerSavingConfiguration.java index 5509845855..21f2b619d5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/PowerSavingConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/PowerSavingConfiguration.java @@ -15,11 +15,13 @@ */ package org.thingsboard.server.common.data.device.data; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.io.Serializable; @Data +@Schema public class PowerSavingConfiguration implements Serializable { private static final long serialVersionUID = 2905389805488525362L; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpec.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpec.java index 5bb1cd752f..6c7a9f1307 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpec.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpec.java @@ -19,9 +19,20 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 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 java.io.Serializable; +@Schema( + description = "Specification for alarm conditions", + discriminatorProperty = "type", + discriminatorMapping = { + @DiscriminatorMapping(value = "SIMPLE", schema = SimpleAlarmConditionSpec.class), + @DiscriminatorMapping(value = "DURATION", schema = DurationAlarmConditionSpec.class), + @DiscriminatorMapping(value = "REPEATING", schema = RepeatingAlarmConditionSpec.class) + } +) @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -33,6 +44,7 @@ import java.io.Serializable; @JsonSubTypes.Type(value = RepeatingAlarmConditionSpec.class, name = "REPEATING")}) public interface AlarmConditionSpec extends Serializable { + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) @JsonIgnore AlarmConditionSpecType getType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpecType.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpecType.java index f8ce5947ed..1cea3646eb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpecType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpecType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.device.profile; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum AlarmConditionSpecType { SIMPLE, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmSchedule.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmSchedule.java index 00faaef712..f45302e5d1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmSchedule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmSchedule.java @@ -18,10 +18,21 @@ package org.thingsboard.server.common.data.device.profile; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 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.query.DynamicValue; import java.io.Serializable; +@Schema( + description = "Configuration for alarm schedule", + 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, @@ -33,6 +44,7 @@ import java.io.Serializable; @JsonSubTypes.Type(value = CustomTimeSchedule.class, name = "CUSTOM")}) public interface AlarmSchedule extends Serializable { + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Type of the alarm schedule") AlarmScheduleType getType(); DynamicValue getDynamicValue(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmScheduleType.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmScheduleType.java index 56148844fa..a90beabf45 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmScheduleType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmScheduleType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.device.profile; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum AlarmScheduleType { ANY_TIME, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceProfileTransportConfiguration.java index b12d0bdf40..0b607892a8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceProfileTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceProfileTransportConfiguration.java @@ -15,16 +15,19 @@ */ package org.thingsboard.server.common.data.device.profile; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.device.data.PowerSavingConfiguration; +@Schema @Data public class CoapDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { private CoapDeviceTypeConfiguration coapDeviceTypeConfiguration; private PowerSavingConfiguration clientSettings; + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Type of the device transport") @Override public DeviceTransportType getType() { return DeviceTransportType.COAP; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DefaultDeviceProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DefaultDeviceProfileConfiguration.java index 5058024680..895bb4ac93 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DefaultDeviceProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DefaultDeviceProfileConfiguration.java @@ -15,12 +15,15 @@ */ package org.thingsboard.server.common.data.device.profile; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.thingsboard.server.common.data.DeviceProfileType; +@Schema(description = "Default device profile configuration", allOf = DeviceProfileConfiguration.class) @Data public class DefaultDeviceProfileConfiguration implements DeviceProfileConfiguration { + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Device profile type") @Override public DeviceProfileType getType() { return DeviceProfileType.DEFAULT; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DefaultDeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DefaultDeviceProfileTransportConfiguration.java index 012b09c8e1..d204ab2ffd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DefaultDeviceProfileTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DefaultDeviceProfileTransportConfiguration.java @@ -15,12 +15,15 @@ */ package org.thingsboard.server.common.data.device.profile; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.thingsboard.server.common.data.DeviceTransportType; +@Schema @Data public class DefaultDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Type of the device transport") @Override public DeviceTransportType getType() { return DeviceTransportType.DEFAULT; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileConfiguration.java index 98546180e2..6b3108be26 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileConfiguration.java @@ -19,10 +19,19 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 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.DeviceProfileType; import java.io.Serializable; +@Schema( + description = "Device profile configuration", + discriminatorProperty = "type", + discriminatorMapping = { + @DiscriminatorMapping(value = "DEFAULT", schema = DefaultDeviceProfileConfiguration.class) + } +) @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -32,6 +41,7 @@ import java.io.Serializable; @JsonSubTypes.Type(value = DefaultDeviceProfileConfiguration.class, name = "DEFAULT")}) public interface DeviceProfileConfiguration extends Serializable { + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Device profile type") @JsonIgnore DeviceProfileType getType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java index 17627ecc6b..21d42909b9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java @@ -19,10 +19,23 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 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.DeviceTransportType; import java.io.Serializable; +@Schema( + description = "Configuration for device profile transport", + discriminatorProperty = "type", + discriminatorMapping = { + @DiscriminatorMapping(value = "DEFAULT", schema = DefaultDeviceProfileTransportConfiguration.class), + @DiscriminatorMapping(value = "MQTT", schema = MqttDeviceProfileTransportConfiguration.class), + @DiscriminatorMapping(value = "LWM2M", schema = Lwm2mDeviceProfileTransportConfiguration.class), + @DiscriminatorMapping(value = "COAP", schema = CoapDeviceProfileTransportConfiguration.class), + @DiscriminatorMapping(value = "SNMP", schema = SnmpDeviceProfileTransportConfiguration.class) + } +) @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -37,6 +50,7 @@ import java.io.Serializable; }) public interface DeviceProfileTransportConfiguration extends Serializable { + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) @JsonIgnore DeviceTransportType getType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DurationAlarmConditionSpec.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DurationAlarmConditionSpec.java index b23d5f9a6e..c6336b823d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DurationAlarmConditionSpec.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DurationAlarmConditionSpec.java @@ -16,19 +16,24 @@ package org.thingsboard.server.common.data.device.profile; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.thingsboard.server.common.data.query.FilterPredicateValue; import java.util.concurrent.TimeUnit; +@Schema(description = "Duration Alarm Condition Specification") @Data @JsonIgnoreProperties(ignoreUnknown = true) public class DurationAlarmConditionSpec implements AlarmConditionSpec { + @Schema(description = "Duration time unit") private TimeUnit unit; + @Schema(description = "Duration predicate") private FilterPredicateValue predicate; @Override + @Schema(description = "Type of the Alarm Condition Specification", requiredMode = Schema.RequiredMode.REQUIRED) public AlarmConditionSpecType getType() { return AlarmConditionSpecType.DURATION; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java index ccaa730f45..f2a6a46408 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.device.profile; +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.DeviceTransportType; import org.thingsboard.server.common.data.device.data.PowerMode; @@ -22,20 +24,25 @@ import org.thingsboard.server.common.data.device.profile.lwm2m.OtherConfiguratio import org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryMappingConfiguration; import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential; -import static org.eclipse.leshan.core.LwM2m.Version.V1_0; -import static org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryObserveStrategy.SINGLE; - import java.util.Collections; import java.util.List; +import static org.eclipse.leshan.core.LwM2m.Version.V1_0; +import static org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryObserveStrategy.SINGLE; + @Data +@Schema public class Lwm2mDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { private static final long serialVersionUID = 6257277825459600068L; + @Schema(description = "Configuration for mapping LwM2M resources to telemetry and attributes") private TelemetryMappingConfiguration observeAttr; + @Schema(description = "Flag indicating whether LwM2M bootstrap server update is enabled") private boolean bootstrapServerUpdateEnable; + @ArraySchema(schema = @Schema(implementation = LwM2MBootstrapServerCredential.class)) private List bootstrap; + @Schema(description = "Other LwM2M client settings") private OtherConfiguration clientLwM2mSettings; public Lwm2mDeviceProfileTransportConfiguration() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/MqttDeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/MqttDeviceProfileTransportConfiguration.java index 4e165e7745..f1c8ea582b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/MqttDeviceProfileTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/MqttDeviceProfileTransportConfiguration.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.device.profile; +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.DeviceTransportType; import org.thingsboard.server.common.data.StringUtils; @@ -26,18 +28,26 @@ import java.util.Set; @Data public class MqttDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { + @Schema @NoXss private String deviceTelemetryTopic = MqttTopics.DEVICE_TELEMETRY_TOPIC; + @Schema @NoXss private String deviceAttributesTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC; + @Schema @NoXss private String deviceAttributesSubscribeTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC; + @Schema private TransportPayloadTypeConfiguration transportPayloadTypeConfiguration; + @Schema private boolean sparkplug; + @ArraySchema(schema = @Schema(implementation = String.class)) private Set sparkplugAttributesMetricNames; + @Schema private boolean sendAckOnValidationException; + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Type of the device transport") @Override public DeviceTransportType getType() { return DeviceTransportType.MQTT; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/RepeatingAlarmConditionSpec.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/RepeatingAlarmConditionSpec.java index cdbb7a80df..1c6ae74e2e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/RepeatingAlarmConditionSpec.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/RepeatingAlarmConditionSpec.java @@ -16,16 +16,20 @@ package org.thingsboard.server.common.data.device.profile; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.thingsboard.server.common.data.query.FilterPredicateValue; +@Schema @Data @JsonIgnoreProperties(ignoreUnknown = true) public class RepeatingAlarmConditionSpec implements AlarmConditionSpec { + @Schema(description = "Repeating predicate") private FilterPredicateValue predicate; @Override + @Schema(description = "Type of the Alarm Condition Specification", requiredMode = Schema.RequiredMode.REQUIRED) public AlarmConditionSpecType getType() { return AlarmConditionSpecType.REPEATING; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SimpleAlarmConditionSpec.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SimpleAlarmConditionSpec.java index 5f492060d0..abc14c90e0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SimpleAlarmConditionSpec.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SimpleAlarmConditionSpec.java @@ -16,12 +16,15 @@ package org.thingsboard.server.common.data.device.profile; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +@Schema @Data @JsonIgnoreProperties(ignoreUnknown = true) public class SimpleAlarmConditionSpec implements AlarmConditionSpec { @Override + @Schema(description = "Type of the Alarm Condition Specification", requiredMode = Schema.RequiredMode.REQUIRED) public AlarmConditionSpecType getType() { return AlarmConditionSpecType.SIMPLE; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java index ee1656c04a..04802abbf4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java @@ -16,18 +16,23 @@ package org.thingsboard.server.common.data.device.profile; import com.fasterxml.jackson.annotation.JsonIgnore; +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.DeviceTransportType; import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig; import java.util.List; +@Schema @Data public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { private Integer timeoutMs; private Integer retries; + @ArraySchema(schema = @Schema(implementation = SnmpCommunicationConfig.class)) private List communicationConfigs; + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Type of the device transport") @Override public DeviceTransportType getType() { return DeviceTransportType.SNMP; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/TransportPayloadTypeConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/TransportPayloadTypeConfiguration.java index c7111f8ffd..8175e09934 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/TransportPayloadTypeConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/TransportPayloadTypeConfiguration.java @@ -19,10 +19,20 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 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.TransportPayloadType; import java.io.Serializable; +@Schema( + description = "Configuration for transport payload type", + discriminatorProperty = "transportPayloadType", + discriminatorMapping = { + @DiscriminatorMapping(value = "JSON", schema = JsonTransportPayloadConfiguration.class), + @DiscriminatorMapping(value = "PROTOBUF", schema = ProtoTransportPayloadConfiguration.class) + } +) @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -34,6 +44,9 @@ import java.io.Serializable; public interface TransportPayloadTypeConfiguration extends Serializable { @JsonIgnore + @Schema( + description = "Transport payload type", requiredMode = Schema.RequiredMode.REQUIRED + ) TransportPayloadType getTransportPayloadType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/ObjectAttributes.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/ObjectAttributes.java index 393e08efee..6e83e82692 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/ObjectAttributes.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/ObjectAttributes.java @@ -16,11 +16,13 @@ package org.thingsboard.server.common.data.device.profile.lwm2m; import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.eclipse.leshan.core.LwM2m; import java.io.Serializable; +@Schema @Data @JsonInclude(JsonInclude.Include.NON_NULL) public class ObjectAttributes implements Serializable { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/OtherConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/OtherConfiguration.java index 0b0d4095b1..cdf9ed89df 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/OtherConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/OtherConfiguration.java @@ -16,6 +16,7 @@ package org.thingsboard.server.common.data.device.profile.lwm2m; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; @@ -23,6 +24,7 @@ import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.device.data.PowerMode; import org.thingsboard.server.common.data.device.data.PowerSavingConfiguration; +@Schema @EqualsAndHashCode(callSuper = true) @Data @NoArgsConstructor diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryMappingConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryMappingConfiguration.java index a166d992a4..24cfcc0b69 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryMappingConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryMappingConfiguration.java @@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.device.profile.lwm2m; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.NoArgsConstructor; @@ -27,15 +28,22 @@ import java.util.Set; @Data @NoArgsConstructor +@Schema public class TelemetryMappingConfiguration implements Serializable { private static final long serialVersionUID = -7594999741305410419L; + @Schema(description = "Map of LwM2M resource paths to telemetry key names") private Map keyName; + @Schema(description = "Set of resources to observe") private Set observe; + @Schema(description = "Set of attribute keys") private Set attribute; + @Schema(description = "Set of telemetry keys") private Set telemetry; + @Schema(description = "Map of resource paths to specific LwM2M object attributes") private Map attributeLwm2m; + @Schema(description = "Observation strategy for telemetry") private TelemetryObserveStrategy observeStrategy; @JsonCreator diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryObserveStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryObserveStrategy.java index 53dafe19b7..b9409dbd37 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryObserveStrategy.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryObserveStrategy.java @@ -15,8 +15,10 @@ */ package org.thingsboard.server.common.data.device.profile.lwm2m; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; +@Schema(enumAsRef = true) public enum TelemetryObserveStrategy { SINGLE("One resource equals one single observe request", 0), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/event/EventFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/event/EventFilter.java index 07789b9208..4d955f1817 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/event/EventFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/event/EventFilter.java @@ -17,9 +17,21 @@ package org.thingsboard.server.common.data.event; 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 +@Schema( + description = "Filter for various event types", + discriminatorProperty = "eventType", + discriminatorMapping = { + @DiscriminatorMapping(value = "DEBUG_RULE_NODE", schema = RuleNodeDebugEventFilter.class), + @DiscriminatorMapping(value = "DEBUG_RULE_CHAIN", schema = RuleChainDebugEventFilter.class), + @DiscriminatorMapping(value = "ERROR", schema = ErrorEventFilter.class), + @DiscriminatorMapping(value = "LC_EVENT", schema = LifeCycleEventFilter.class), + @DiscriminatorMapping(value = "STATS", schema = StatisticsEventFilter.class), + @DiscriminatorMapping(value = "DEBUG_CALCULATED_FIELD", schema = CalculatedFieldDebugEventFilter.class) + } +) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/event/EventType.java b/common/data/src/main/java/org/thingsboard/server/common/data/event/EventType.java index 416e711ed2..90ecf264f4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/event/EventType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/event/EventType.java @@ -15,8 +15,10 @@ */ package org.thingsboard.server.common.data.event; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; +@Schema(enumAsRef = true) public enum EventType { ERROR("error_event", "ERROR"), LC_EVENT("lc_event", "LC_EVENT"), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTaskType.java b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTaskType.java index 7988526bdf..3e7fdef060 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTaskType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTaskType.java @@ -15,9 +15,11 @@ */ package org.thingsboard.server.common.data.housekeeper; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.RequiredArgsConstructor; +@Schema(enumAsRef = true) @RequiredArgsConstructor @Getter public enum HousekeeperTaskType { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java index 2009f10f9f..714ee236a8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.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 lombok.Data; import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.job.task.TaskResult; @@ -28,6 +30,13 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; +@Schema( + description = "Job execution result", + discriminatorProperty = "jobType", + discriminatorMapping = { + @DiscriminatorMapping(value = "DUMMY", schema = DummyJobResult.class) + } +) @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "jobType") @JsonSubTypes({ @@ -37,15 +46,24 @@ import java.util.List; @NoArgsConstructor public abstract class JobResult implements Serializable { + @Schema(description = "Count of successfully completed tasks") private int successfulCount; + @Schema(description = "Count of failed tasks") private int failedCount; + @Schema(description = "Count of discarded tasks") private int discardedCount; - private Integer totalCount = null; // set when all tasks are submitted + @Schema(description = "Total number of tasks, set when all tasks are submitted", nullable = true) + private Integer totalCount = null; + @Schema(description = "List of task results, preserving only first 100 errors") private List results = new ArrayList<>(); + @Schema(description = "General error message if the job failed") private String generalError; + @Schema(description = "Timestamp of the job start, in milliseconds") private long startTs; + @Schema(description = "Timestamp of the job finish, in milliseconds") private long finishTs; + @Schema(description = "Timestamp of the job cancellation, in milliseconds") private long cancellationTs; @JsonIgnore @@ -72,6 +90,7 @@ public abstract class JobResult implements Serializable { } } + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) @JsonIgnore public abstract JobType getJobType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java index f15cc34b9d..fbcfc1361e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.job; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum JobStatus { QUEUED, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java index 623dacfabb..089b30ebf9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java @@ -15,9 +15,11 @@ */ package org.thingsboard.server.common.data.job; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.RequiredArgsConstructor; +@Schema(enumAsRef = true) @RequiredArgsConstructor @Getter public enum JobType { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java index 97f28ba853..3e92fa8db3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java @@ -15,8 +15,10 @@ */ package org.thingsboard.server.common.data.kv; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; +@Schema(enumAsRef = true) public enum DataType { BOOLEAN(0), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/IntervalType.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/IntervalType.java index f88c02b592..6503d5e029 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/IntervalType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/IntervalType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.kv; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum IntervalType { MILLISECONDS, WEEK/*Sunday-Saturday*/, WEEK_ISO/*Monday-Sunday*/, MONTH, QUARTER diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java index e6c59ce7a7..ab9f124918 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java @@ -15,12 +15,14 @@ */ package org.thingsboard.server.common.data.limit; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import java.util.Optional; import java.util.function.Function; +@Schema(enumAsRef = true) @Getter public enum LimitedApi { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mail/MailOauth2Provider.java b/common/data/src/main/java/org/thingsboard/server/common/data/mail/MailOauth2Provider.java index c58906b7bc..09e093cc8a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mail/MailOauth2Provider.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mail/MailOauth2Provider.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.mail; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum MailOauth2Provider { GOOGLE("Google"), OFFICE_365("Office 365"), SENDGRID("SendGrid"), CUSTOM("Custom"); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/MobileAppStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/MobileAppStatus.java index 9372ef0c31..ee1413e7ea 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/MobileAppStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/MobileAppStatus.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.mobile.app; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum MobileAppStatus { DRAFT, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/DefaultPageId.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/DefaultPageId.java index 2591914167..65208a345c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/DefaultPageId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/DefaultPageId.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.mobile.layout; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum DefaultPageId { HOME, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/MobilePage.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/MobilePage.java index 61fa5a0c46..4ab8388f8b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/MobilePage.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/MobilePage.java @@ -19,10 +19,22 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonView; +import io.swagger.v3.oas.annotations.media.DiscriminatorMapping; +import io.swagger.v3.oas.annotations.media.Schema; import org.thingsboard.server.common.data.Views; import java.io.Serializable; +@Schema( + description = "Configuration for a mobile page", + discriminatorProperty = "type", + discriminatorMapping = { + @DiscriminatorMapping(value = "DEFAULT", schema = DefaultMobilePage.class), + @DiscriminatorMapping(value = "DASHBOARD", schema = DashboardPage.class), + @DiscriminatorMapping(value = "WEB_VIEW", schema = WebViewPage.class), + @DiscriminatorMapping(value = "CUSTOM", schema = CustomMobilePage.class) + } +) @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -36,6 +48,7 @@ import java.io.Serializable; }) public interface MobilePage extends Serializable { + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) @JsonView(Views.Private.class) MobilePageType getType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/MobilePageType.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/MobilePageType.java index c77165921d..3e4395e53d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/MobilePageType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/MobilePageType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.mobile.layout; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum MobilePageType { DEFAULT, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/qrCodeSettings/BadgePosition.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/qrCodeSettings/BadgePosition.java index 72e185327b..464293c362 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/qrCodeSettings/BadgePosition.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/qrCodeSettings/BadgePosition.java @@ -15,6 +15,10 @@ */ package org.thingsboard.server.common.data.mobile.qrCodeSettings; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum BadgePosition { RIGHT, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java index 750606c06d..398453b572 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java @@ -15,9 +15,11 @@ */ package org.thingsboard.server.common.data.notification; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.RequiredArgsConstructor; +@Schema(enumAsRef = true) @RequiredArgsConstructor public enum NotificationDeliveryMethod { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationStatus.java index b2cb54582e..c0ce206389 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationStatus.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.notification; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum NotificationStatus { SENT, READ diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java index 921a972225..4610d7b29d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java @@ -15,10 +15,12 @@ */ package org.thingsboard.server.common.data.notification; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; +@Schema(enumAsRef = true) @AllArgsConstructor @NoArgsConstructor public enum NotificationType { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java index fd6b089a42..b3472fff4b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; import lombok.Data; import org.thingsboard.server.common.data.id.EntityId; @@ -66,6 +67,7 @@ public class ResourcesShortageTrigger implements NotificationRuleTrigger { return NotificationRuleTriggerType.RESOURCES_SHORTAGE; } + @Schema(enumAsRef = true) public enum Resource { CPU, RAM, STORAGE } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmAssignmentNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmAssignmentNotificationRuleTriggerConfig.java index d9acca4930..a4714024ab 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmAssignmentNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmAssignmentNotificationRuleTriggerConfig.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import lombok.AllArgsConstructor; import lombok.Builder; @@ -29,19 +31,26 @@ import java.util.Set; @AllArgsConstructor @NoArgsConstructor @Builder +@Schema public class AlarmAssignmentNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + @ArraySchema(schema = @Schema(implementation = String.class)) private Set alarmTypes; + @ArraySchema(schema = @Schema(implementation = AlarmSeverity.class)) private Set alarmSeverities; + @ArraySchema(schema = @Schema(implementation = AlarmSearchStatus.class)) private Set alarmStatuses; @NotEmpty + @ArraySchema(schema = @Schema(implementation = Action.class)) private Set notifyOn; @Override + @Schema(description = "Type of the notification rule trigger") public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.ALARM_ASSIGNMENT; } + @Schema(enumAsRef = true) public enum Action { ASSIGNED, UNASSIGNED } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmCommentNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmCommentNotificationRuleTriggerConfig.java index f220002fc0..5c752243d8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmCommentNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmCommentNotificationRuleTriggerConfig.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -28,15 +30,22 @@ import java.util.Set; @AllArgsConstructor @NoArgsConstructor @Builder +@Schema public class AlarmCommentNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + @ArraySchema(schema = @Schema(implementation = String.class)) private Set alarmTypes; + @ArraySchema(schema = @Schema(implementation = AlarmSeverity.class)) private Set alarmSeverities; + @ArraySchema(schema = @Schema(implementation = AlarmSearchStatus.class)) private Set alarmStatuses; + @Schema private boolean onlyUserComments; + @Schema private boolean notifyOnCommentUpdate; @Override + @Schema(description = "Type of the notification rule trigger") public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.ALARM_COMMENT; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmNotificationRuleTriggerConfig.java index 041d10ba90..bb16215b8f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmNotificationRuleTriggerConfig.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import lombok.AllArgsConstructor; import lombok.Builder; @@ -30,16 +32,21 @@ import java.util.Set; @AllArgsConstructor @NoArgsConstructor @Builder +@Schema public class AlarmNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + @ArraySchema(schema = @Schema(implementation = String.class)) private Set alarmTypes; + @ArraySchema(schema = @Schema(implementation = AlarmSeverity.class)) private Set alarmSeverities; @NotEmpty + @ArraySchema(schema = @Schema(implementation = AlarmAction.class)) private Set notifyOn; - + @Schema private ClearRule clearRule; @Override + @Schema(description = "Type of the notification rule trigger") public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.ALARM; } @@ -49,6 +56,7 @@ public class AlarmNotificationRuleTriggerConfig implements NotificationRuleTrigg private Set alarmStatuses; } + @Schema(enumAsRef = true) public enum AlarmAction { CREATED, SEVERITY_CHANGED, ACKNOWLEDGED, CLEARED } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ApiUsageLimitNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ApiUsageLimitNotificationRuleTriggerConfig.java index e7051778b2..973e8d5f6a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ApiUsageLimitNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ApiUsageLimitNotificationRuleTriggerConfig.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -28,12 +30,16 @@ import java.util.Set; @AllArgsConstructor @NoArgsConstructor @Builder +@Schema public class ApiUsageLimitNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + @ArraySchema(schema = @Schema(implementation = ApiFeature.class)) private Set apiFeatures; + @ArraySchema(schema = @Schema(implementation = ApiUsageStateValue.class)) private Set notifyOn; @Override + @Schema(description = "Type of the notification rule trigger") public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.API_USAGE_LIMIT; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/DeviceActivityNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/DeviceActivityNotificationRuleTriggerConfig.java index 2ad8586f75..0eaf7a9b51 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/DeviceActivityNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/DeviceActivityNotificationRuleTriggerConfig.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import lombok.AllArgsConstructor; import lombok.Builder; @@ -28,18 +30,24 @@ import java.util.UUID; @AllArgsConstructor @NoArgsConstructor @Builder +@Schema public class DeviceActivityNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + @ArraySchema(schema = @Schema(implementation = UUID.class)) private Set devices; + @ArraySchema(schema = @Schema(implementation = UUID.class)) private Set deviceProfiles; // set either devices or profiles @NotEmpty + @ArraySchema(schema = @Schema(implementation = DeviceEvent.class)) private Set notifyOn; @Override + @Schema(description = "Type of the notification rule trigger") public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.DEVICE_ACTIVITY; } + @Schema(enumAsRef = true) public enum DeviceEvent { ACTIVE, INACTIVE } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeCommunicationFailureNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeCommunicationFailureNotificationRuleTriggerConfig.java index 6def4c1799..2250369164 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeCommunicationFailureNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeCommunicationFailureNotificationRuleTriggerConfig.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -27,11 +29,14 @@ import java.util.UUID; @NoArgsConstructor @AllArgsConstructor @Builder +@Schema public class EdgeCommunicationFailureNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + @ArraySchema(schema = @Schema(implementation = UUID.class)) private Set edges; // if empty - all edges @Override + @Schema(description = "Type of the notification rule trigger") public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.EDGE_COMMUNICATION_FAILURE; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectionNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectionNotificationRuleTriggerConfig.java index 264fa9aa55..30356d7dfc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectionNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectionNotificationRuleTriggerConfig.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -27,16 +29,21 @@ import java.util.UUID; @NoArgsConstructor @AllArgsConstructor @Builder +@Schema public class EdgeConnectionNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + @ArraySchema(schema = @Schema(implementation = UUID.class)) private Set edges; // if empty - all edges + @ArraySchema(schema = @Schema(implementation = EdgeConnectivityEvent.class)) private Set notifyOn; @Override + @Schema(description = "Type of the notification rule trigger") public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.EDGE_CONNECTION; } + @Schema(enumAsRef = true) public enum EdgeConnectivityEvent { CONNECTED, DISCONNECTED } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntitiesLimitNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntitiesLimitNotificationRuleTriggerConfig.java index 7c8997dae5..6fe1b1327a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntitiesLimitNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntitiesLimitNotificationRuleTriggerConfig.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Max; import lombok.AllArgsConstructor; import lombok.Builder; @@ -28,13 +30,16 @@ import java.util.Set; @AllArgsConstructor @NoArgsConstructor @Builder +@Schema public class EntitiesLimitNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + @ArraySchema(schema = @Schema(implementation = EntityType.class)) private Set entityTypes; @Max(1) private float threshold; // in percents, @Override + @Schema(description = "Type of the notification rule trigger") public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.ENTITIES_LIMIT; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntityActionNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntityActionNotificationRuleTriggerConfig.java index bdc871ad96..6865976b0b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntityActionNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntityActionNotificationRuleTriggerConfig.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -27,14 +29,17 @@ import java.util.Set; @AllArgsConstructor @NoArgsConstructor @Builder +@Schema public class EntityActionNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + @ArraySchema(schema = @Schema(implementation = EntityType.class)) private Set entityTypes; // maybe add name filter ? private boolean created; private boolean updated; private boolean deleted; @Override + @Schema(description = "Type of the notification rule trigger") public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.ENTITY_ACTION; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NewPlatformVersionNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NewPlatformVersionNotificationRuleTriggerConfig.java index b625efb506..fe50d09365 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NewPlatformVersionNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NewPlatformVersionNotificationRuleTriggerConfig.java @@ -15,12 +15,15 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data +@Schema public class NewPlatformVersionNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { @Override + @Schema(description = "Type of the notification rule trigger") public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.NEW_PLATFORM_VERSION; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java index 6fb4bbd48b..a58b3d5eef 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java @@ -20,9 +20,32 @@ 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 java.io.Serializable; +@Schema( + name = "NotificationRuleTriggerConfig", + description = "Configuration for notification rule trigger", + discriminatorProperty = "triggerType", + discriminatorMapping = { + @DiscriminatorMapping(value = "ALARM", schema = AlarmNotificationRuleTriggerConfig.class), + @DiscriminatorMapping(value = "DEVICE_ACTIVITY", schema = DeviceActivityNotificationRuleTriggerConfig.class), + @DiscriminatorMapping(value = "ENTITY_ACTION", schema = EntityActionNotificationRuleTriggerConfig.class), + @DiscriminatorMapping(value = "ALARM_COMMENT", schema = AlarmCommentNotificationRuleTriggerConfig.class), + @DiscriminatorMapping(value = "RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT", schema = RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.class), + @DiscriminatorMapping(value = "ALARM_ASSIGNMENT", schema = AlarmAssignmentNotificationRuleTriggerConfig.class), + @DiscriminatorMapping(value = "NEW_PLATFORM_VERSION", schema = NewPlatformVersionNotificationRuleTriggerConfig.class), + @DiscriminatorMapping(value = "ENTITIES_LIMIT", schema = EntitiesLimitNotificationRuleTriggerConfig.class), + @DiscriminatorMapping(value = "API_USAGE_LIMIT", schema = ApiUsageLimitNotificationRuleTriggerConfig.class), + @DiscriminatorMapping(value = "RATE_LIMITS", schema = RateLimitsNotificationRuleTriggerConfig.class), + @DiscriminatorMapping(value = "EDGE_CONNECTION", schema = EdgeConnectionNotificationRuleTriggerConfig.class), + @DiscriminatorMapping(value = "EDGE_COMMUNICATION_FAILURE", schema = EdgeCommunicationFailureNotificationRuleTriggerConfig.class), + @DiscriminatorMapping(value = "TASK_PROCESSING_FAILURE", schema = TaskProcessingFailureNotificationRuleTriggerConfig.class), + @DiscriminatorMapping(value = "RESOURCES_SHORTAGE", schema = ResourcesShortageNotificationRuleTriggerConfig.class) + } +) @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "triggerType") @JsonSubTypes({ @@ -43,6 +66,7 @@ import java.io.Serializable; }) public interface NotificationRuleTriggerConfig extends Serializable { + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) NotificationRuleTriggerType getTriggerType(); @JsonIgnore diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java index ca34472b45..9dacc7be86 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java @@ -15,9 +15,11 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @Getter +@Schema(enumAsRef = true) public enum NotificationRuleTriggerType { ENTITY_ACTION, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/RateLimitsNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/RateLimitsNotificationRuleTriggerConfig.java index 982e20284c..33baf11617 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/RateLimitsNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/RateLimitsNotificationRuleTriggerConfig.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -28,11 +30,14 @@ import java.util.stream.Collectors; @AllArgsConstructor @NoArgsConstructor @Builder +@Schema public class RateLimitsNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + @ArraySchema(schema = @Schema(implementation = LimitedApi.class)) private Set apis; @Override + @Schema(description = "Type of the notification rule trigger") public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.RATE_LIMITS; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java index 77782a73cf..ded3cdabd5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Max; import lombok.AllArgsConstructor; import lombok.Builder; @@ -27,6 +28,7 @@ import java.io.Serial; @AllArgsConstructor @NoArgsConstructor @Builder +@Schema public class ResourcesShortageNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { @Serial @@ -40,6 +42,7 @@ public class ResourcesShortageNotificationRuleTriggerConfig implements Notificat private float storageThreshold; // in percents @Override + @Schema(description = "Type of the notification rule trigger") public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.RESOURCES_SHORTAGE; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java index 4b46c26d65..0d280264fb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -28,18 +30,22 @@ import java.util.UUID; @AllArgsConstructor @NoArgsConstructor @Builder +@Schema public class RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + @ArraySchema(schema = @Schema(implementation = UUID.class)) private Set ruleChains; // if empty - all rule chains - + @ArraySchema(schema = @Schema(implementation = ComponentLifecycleEvent.class)) private Set ruleChainEvents; // available options: STARTED, UPDATED, STOPPED. if empty - all events private boolean onlyRuleChainLifecycleFailures; private boolean trackRuleNodeEvents; + @ArraySchema(schema = @Schema(implementation = ComponentLifecycleEvent.class)) private Set ruleNodeEvents; // available options: STARTED, UPDATED, STOPPED. if empty - all events private boolean onlyRuleNodeLifecycleFailures; @Override + @Schema(description = "Type of the notification rule trigger") public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/TaskProcessingFailureNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/TaskProcessingFailureNotificationRuleTriggerConfig.java index 6a88000124..1a70298385 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/TaskProcessingFailureNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/TaskProcessingFailureNotificationRuleTriggerConfig.java @@ -15,14 +15,17 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; import lombok.Data; @Data @Builder +@Schema public class TaskProcessingFailureNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { @Override + @Schema(description = "Type of the notification rule trigger", requiredMode = Schema.RequiredMode.REQUIRED) public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.TASK_PROCESSING_FAILURE; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java index 9a71a95c7e..7544a2c7e3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.notification.targets; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; @@ -22,6 +23,7 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho import java.util.Arrays; import java.util.Set; +@Schema(enumAsRef = true) @RequiredArgsConstructor public enum NotificationTargetType { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java index d9dae29b76..0f497cd463 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java @@ -15,10 +15,12 @@ */ package org.thingsboard.server.common.data.notification.targets.platform; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; +@Schema(enumAsRef = true) @AllArgsConstructor @NoArgsConstructor @Getter diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversationType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversationType.java index ae8fd410a6..e9baa94206 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversationType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversationType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.notification.targets.slack; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum SlackConversationType { DIRECT, PUBLIC_CHANNEL, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java index a679d0e6e7..62600542a1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.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.constraints.NotEmpty; import lombok.Data; import lombok.NoArgsConstructor; @@ -27,6 +29,18 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho import java.util.List; +@Schema( + description = "Base template for different delivery methods", + discriminatorProperty = "method", + discriminatorMapping = { + @DiscriminatorMapping(value = "WEB", schema = WebDeliveryMethodNotificationTemplate.class), + @DiscriminatorMapping(value = "EMAIL", schema = EmailDeliveryMethodNotificationTemplate.class), + @DiscriminatorMapping(value = "SMS", schema = SmsDeliveryMethodNotificationTemplate.class), + @DiscriminatorMapping(value = "SLACK", schema = SlackDeliveryMethodNotificationTemplate.class), + @DiscriminatorMapping(value = "MICROSOFT_TEAMS", schema = MicrosoftTeamsDeliveryMethodNotificationTemplate.class), + @DiscriminatorMapping(value = "MOBILE_APP", schema = MobileAppDeliveryMethodNotificationTemplate.class) + } +) @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "method") @JsonSubTypes({ @@ -50,6 +64,7 @@ public abstract class DeliveryMethodNotificationTemplate { this.body = other.body; } + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) @JsonIgnore public abstract NotificationDeliveryMethod getMethod(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java index 59293e9429..dc8d1dc52c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.notification.template; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import lombok.Data; import lombok.EqualsAndHashCode; @@ -30,6 +31,7 @@ import java.util.List; @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) +@Schema public class EmailDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject { @NoXss(fieldName = "email subject") @@ -47,6 +49,7 @@ public class EmailDeliveryMethodNotificationTemplate extends DeliveryMethodNotif this.subject = other.subject; } + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) @Override public NotificationDeliveryMethod getMethod() { return NotificationDeliveryMethod.EMAIL; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MicrosoftTeamsDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MicrosoftTeamsDeliveryMethodNotificationTemplate.java index 97def8310e..78bbd13229 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MicrosoftTeamsDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MicrosoftTeamsDeliveryMethodNotificationTemplate.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.notification.template; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -30,8 +31,17 @@ import java.util.UUID; @ToString(callSuper = true) public class MicrosoftTeamsDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject { + @Schema( + description = "The header or title shown at the top of the Teams message card.", + example = "System Alert: High CPU Usage" + ) private String subject; + @Schema( + description = "The hex color code for the accent bar on the left side of the Teams card.", + example = "0078D7" + ) private String themeColor; + @Schema(description = "Configuration for an optional call-to-action button within the Teams message.") private Button button; private final List templatableValues = List.of( @@ -50,6 +60,7 @@ public class MicrosoftTeamsDeliveryMethodNotificationTemplate extends DeliveryMe this.button = other.button != null ? new Button(other.button) : null; } + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) @Override public NotificationDeliveryMethod getMethod() { return NotificationDeliveryMethod.MICROSOFT_TEAMS; @@ -82,6 +93,7 @@ public class MicrosoftTeamsDeliveryMethodNotificationTemplate extends DeliveryMe this.setEntityIdInState = other.setEntityIdInState; } + @Schema(enumAsRef = true) public enum LinkType { LINK, DASHBOARD } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MobileAppDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MobileAppDeliveryMethodNotificationTemplate.java index 248d2dedf5..31eb96219b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MobileAppDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MobileAppDeliveryMethodNotificationTemplate.java @@ -16,6 +16,7 @@ package org.thingsboard.server.common.data.notification.template; import com.fasterxml.jackson.databind.JsonNode; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import lombok.Data; import lombok.EqualsAndHashCode; @@ -31,8 +32,10 @@ import java.util.List; @ToString(callSuper = true) public class MobileAppDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject { + @Schema(description = "Subject line for the mobile notification", example = "New Message Received") @NotEmpty private String subject; + @Schema(description = "Additional JSON configuration for web buttons/actions", type = "object") private JsonNode additionalConfig; private final List templatableValues = List.of( @@ -46,6 +49,7 @@ public class MobileAppDeliveryMethodNotificationTemplate extends DeliveryMethodN this.additionalConfig = other.additionalConfig; } + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) @Override public NotificationDeliveryMethod getMethod() { return NotificationDeliveryMethod.MOBILE_APP; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java index ea93ba2aab..7fa5fc292c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.notification.template; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -44,6 +45,7 @@ public class SlackDeliveryMethodNotificationTemplate extends DeliveryMethodNotif return super.getBody(); } + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) @Override public NotificationDeliveryMethod getMethod() { return NotificationDeliveryMethod.SLACK; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SmsDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SmsDeliveryMethodNotificationTemplate.java index cbfe0bdcf4..e8d4e05363 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SmsDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SmsDeliveryMethodNotificationTemplate.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.notification.template; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -46,6 +47,7 @@ public class SmsDeliveryMethodNotificationTemplate extends DeliveryMethodNotific return super.getBody(); } + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) @Override public NotificationDeliveryMethod getMethod() { return NotificationDeliveryMethod.SMS; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java index 9385c00abf..fd197fea97 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import lombok.Data; import lombok.EqualsAndHashCode; @@ -37,10 +38,12 @@ import java.util.Optional; @ToString(callSuper = true) public class WebDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject { + @Schema(description = "Subject line for the web notification", example = "New Message Received") @NoXss(fieldName = "web notification subject") @Length(fieldName = "web notification subject", max = 150, message = "cannot be longer than 150 chars") @NotEmpty private String subject; + @Schema(description = "Additional JSON configuration for web buttons/actions") private JsonNode additionalConfig; private final List templatableValues = List.of( @@ -103,6 +106,7 @@ public class WebDeliveryMethodNotificationTemplate extends DeliveryMethodNotific .map(config -> (ObjectNode) config); } + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) @Override public NotificationDeliveryMethod getMethod() { return NotificationDeliveryMethod.WEB; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java index fe8971a618..d59ad14812 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.oauth2; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum MapperType { BASIC, CUSTOM, GITHUB, APPLE; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/PlatformType.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/PlatformType.java index 3589bf55b7..76b1160c50 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/PlatformType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/PlatformType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.oauth2; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum PlatformType { WEB, ANDROID, IOS } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/SchemeType.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/SchemeType.java index e836bb0193..e9e10b2136 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/SchemeType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/SchemeType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.oauth2; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum SchemeType { HTTP, HTTPS, MIXED; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/TenantNameStrategyType.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/TenantNameStrategyType.java index 83ed211d6e..666e0e3713 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/TenantNameStrategyType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/TenantNameStrategyType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.oauth2; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum TenantNameStrategyType { DOMAIN, EMAIL, CUSTOM; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ota/ChecksumAlgorithm.java b/common/data/src/main/java/org/thingsboard/server/common/data/ota/ChecksumAlgorithm.java index b4817dee68..49b0a504cf 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ota/ChecksumAlgorithm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ota/ChecksumAlgorithm.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.ota; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum ChecksumAlgorithm { MD5, SHA256, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageType.java b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageType.java index d2fd190635..37eee5ae26 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageType.java @@ -15,8 +15,10 @@ */ package org.thingsboard.server.common.data.ota; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; +@Schema(enumAsRef = true) public enum OtaPackageType { FIRMWARE("fw"), SOFTWARE("sw"); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUpdateStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUpdateStatus.java index 219010b95f..51a406705c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUpdateStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUpdateStatus.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.ota; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum OtaPackageUpdateStatus { QUEUED, INITIATED, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java index 4a4c5d89bb..d528cf4c3d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java @@ -15,12 +15,14 @@ */ package org.thingsboard.server.common.data.plugin; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.RequiredArgsConstructor; import java.io.Serializable; import java.util.Arrays; +@Schema(enumAsRef = true) @RequiredArgsConstructor public enum ComponentLifecycleEvent implements Serializable { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleState.java b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleState.java index f6fa9111ac..0450604d4d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleState.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleState.java @@ -15,9 +15,12 @@ */ package org.thingsboard.server.common.data.plugin; +import io.swagger.v3.oas.annotations.media.Schema; + /** * @author Andrew Shvayka */ +@Schema(enumAsRef = true) public enum ComponentLifecycleState { ACTIVE, SUSPENDED } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentScope.java b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentScope.java index eb7361ddba..40d64d8911 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentScope.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentScope.java @@ -15,9 +15,12 @@ */ package org.thingsboard.server.common.data.plugin; +import io.swagger.v3.oas.annotations.media.Schema; + /** * @author Andrew Shvayka */ +@Schema(enumAsRef = true) public enum ComponentScope { SYSTEM, TENANT } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentType.java b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentType.java index a0389520a5..debe419e01 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentType.java @@ -15,9 +15,12 @@ */ package org.thingsboard.server.common.data.plugin; +import io.swagger.v3.oas.annotations.media.Schema; + /** * @author Andrew Shvayka */ +@Schema(enumAsRef = true) public enum ComponentType { ENRICHMENT, FILTER, TRANSFORMATION, ACTION, EXTERNAL, FLOW diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/AlarmCountQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/AlarmCountQuery.java index f9d2e8d3d8..8431d7a303 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/AlarmCountQuery.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/AlarmCountQuery.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.query; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -31,6 +32,7 @@ import java.util.List; @AllArgsConstructor @Data @ToString +@Schema public class AlarmCountQuery extends EntityCountQuery { private long startTs; private long endTs; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/AliasEntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/AliasEntityType.java index e7ae298d20..e50abd6556 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/AliasEntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/AliasEntityType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.query; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum AliasEntityType { CURRENT_CUSTOMER, CURRENT_TENANT, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/BooleanFilterPredicate.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/BooleanFilterPredicate.java index 74c4f87c25..6b849f83ee 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/BooleanFilterPredicate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/BooleanFilterPredicate.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.query; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data @@ -28,6 +29,7 @@ public class BooleanFilterPredicate implements SimpleKeyFilterPredicate return FilterPredicateType.BOOLEAN; } + @Schema(enumAsRef = true) public enum BooleanOperation { EQUAL, NOT_EQUAL diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/ComplexFilterPredicate.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/ComplexFilterPredicate.java index 4fb663c4c1..b889ca230a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/ComplexFilterPredicate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/ComplexFilterPredicate.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.query; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.util.List; @@ -30,6 +31,7 @@ public class ComplexFilterPredicate implements KeyFilterPredicate { return FilterPredicateType.COMPLEX; } + @Schema(enumAsRef = true) public enum ComplexOperation { AND, OR diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValueSourceType.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValueSourceType.java index 5150e49516..da47712dcc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValueSourceType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValueSourceType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.query; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum DynamicValueSourceType { CURRENT_TENANT, CURRENT_CUSTOMER, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityDataSortOrder.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityDataSortOrder.java index dae84fa598..b1f353b233 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityDataSortOrder.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityDataSortOrder.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.query; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data @@ -34,6 +35,7 @@ public class EntityDataSortOrder { this.direction = direction; } + @Schema(enumAsRef = true) public enum Direction { ASC, DESC } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityFilter.java index 4263a4aeea..627ac63b63 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityFilter.java @@ -19,12 +19,34 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 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.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import static org.thingsboard.server.common.data.query.AliasEntityId.resolveAliasEntityId; +@Schema( + description = "Filter for selecting entities", + discriminatorProperty = "type", + discriminatorMapping = { + @DiscriminatorMapping(value = "singleEntity", schema = SingleEntityFilter.class), + @DiscriminatorMapping(value = "entityList", schema = EntityListFilter.class), + @DiscriminatorMapping(value = "entityName", schema = EntityNameFilter.class), + @DiscriminatorMapping(value = "entityType", schema = EntityTypeFilter.class), + @DiscriminatorMapping(value = "assetType", schema = AssetTypeFilter.class), + @DiscriminatorMapping(value = "deviceType", schema = DeviceTypeFilter.class), + @DiscriminatorMapping(value = "edgeType", schema = EdgeTypeFilter.class), + @DiscriminatorMapping(value = "entityViewType", schema = EntityViewTypeFilter.class), + @DiscriminatorMapping(value = "apiUsageState", schema = ApiUsageStateFilter.class), + @DiscriminatorMapping(value = "relationsQuery", schema = RelationsQueryFilter.class), + @DiscriminatorMapping(value = "assetSearchQuery", schema = AssetSearchQueryFilter.class), + @DiscriminatorMapping(value = "deviceSearchQuery", schema = DeviceSearchQueryFilter.class), + @DiscriminatorMapping(value = "entityViewSearchQuery", schema = EntityViewSearchQueryFilter.class), + @DiscriminatorMapping(value = "edgeSearchQuery", schema = EdgeSearchQueryFilter.class) + } +) @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -49,6 +71,7 @@ import static org.thingsboard.server.common.data.query.AliasEntityId.resolveAlia public interface EntityFilter { @JsonIgnore + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) EntityFilterType getType(); static void resolveEntityFilter(EntityFilter filter, TenantId tenantId, UserId userId, EntityId userOwnerId) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityFilterType.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityFilterType.java index b31a03f863..123db07844 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityFilterType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityFilterType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.query; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum EntityFilterType { SINGLE_ENTITY("singleEntity"), ENTITY_LIST("entityList"), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityKeyType.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityKeyType.java index 648c91df27..3722ea0dc5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityKeyType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityKeyType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.query; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum EntityKeyType { ATTRIBUTE, CLIENT_ATTRIBUTE, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityKeyValueType.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityKeyValueType.java index fa6e60e7aa..e47660b4de 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityKeyValueType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/EntityKeyValueType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.query; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum EntityKeyValueType { STRING, NUMERIC, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/FilterPredicateType.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/FilterPredicateType.java index cd028071c2..4413848cdf 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/FilterPredicateType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/FilterPredicateType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.query; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum FilterPredicateType { STRING, NUMERIC, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/KeyFilterPredicate.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/KeyFilterPredicate.java index 949ccc9bc5..55345d3e6b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/KeyFilterPredicate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/KeyFilterPredicate.java @@ -18,9 +18,21 @@ package org.thingsboard.server.common.data.query; import com.fasterxml.jackson.annotation.JsonIgnore; 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 java.io.Serializable; +@Schema( + description = "Filter predicate for 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 = "COMPLEX", schema = ComplexFilterPredicate.class) + } +) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, @@ -33,6 +45,7 @@ import java.io.Serializable; public interface KeyFilterPredicate extends Serializable { @JsonIgnore + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) FilterPredicateType getType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/NumericFilterPredicate.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/NumericFilterPredicate.java index 0b19d56ea6..2e72741136 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/NumericFilterPredicate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/NumericFilterPredicate.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.query; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data @@ -28,6 +29,7 @@ public class NumericFilterPredicate implements SimpleKeyFilterPredicate return FilterPredicateType.NUMERIC; } + @Schema(enumAsRef = true) public enum NumericOperation { EQUAL, NOT_EQUAL, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/SimpleKeyFilterPredicate.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/SimpleKeyFilterPredicate.java index 2a06eb57c2..5072a43e20 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/SimpleKeyFilterPredicate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/SimpleKeyFilterPredicate.java @@ -15,8 +15,15 @@ */ package org.thingsboard.server.common.data.query; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema( + description = "Base interface for simple key filter predicates", + allOf = { KeyFilterPredicate.class } +) public interface SimpleKeyFilterPredicate extends KeyFilterPredicate { + @Schema(description = "The value associated with the filter predicate") FilterPredicateValue getValue(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/StringFilterPredicate.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/StringFilterPredicate.java index 915dc6ef5f..9270200964 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/StringFilterPredicate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/StringFilterPredicate.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.query; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import lombok.Data; @@ -31,6 +32,7 @@ public class StringFilterPredicate implements SimpleKeyFilterPredicate { return FilterPredicateType.STRING; } + @Schema(enumAsRef = true) public enum StringOperation { EQUAL, NOT_EQUAL, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/queue/ProcessingStrategyType.java b/common/data/src/main/java/org/thingsboard/server/common/data/queue/ProcessingStrategyType.java index 35a2f09b59..461ad5d5a3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/queue/ProcessingStrategyType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/queue/ProcessingStrategyType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.queue; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum ProcessingStrategyType { SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT } \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/queue/SubmitStrategyType.java b/common/data/src/main/java/org/thingsboard/server/common/data/queue/SubmitStrategyType.java index ed04a1b3da..96f9c85d45 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/queue/SubmitStrategyType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/queue/SubmitStrategyType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.queue; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum SubmitStrategyType { BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL } \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntitySearchDirection.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntitySearchDirection.java index 912c8dcc59..a5421cbcc3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntitySearchDirection.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntitySearchDirection.java @@ -15,9 +15,12 @@ */ package org.thingsboard.server.common.data.relation; +import io.swagger.v3.oas.annotations.media.Schema; + /** * Created by ashvayka on 02.05.17. */ +@Schema(enumAsRef = true) public enum EntitySearchDirection { FROM, TO; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java index b5cd6e7a56..8d384012f1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.relation; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum RelationTypeGroup { COMMON, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java index b731fbe125..ac00625f5b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java @@ -15,8 +15,10 @@ */ package org.thingsboard.server.common.data.rpc; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; +@Schema(enumAsRef = true) public enum RpcStatus { QUEUED(true), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainType.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainType.java index 4d6ddb137f..531612fac2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.rule; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum RuleChainType { CORE, EDGE } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleType.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleType.java index c04cf03f38..36a0035028 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleType.java @@ -15,12 +15,15 @@ */ package org.thingsboard.server.common.data.rule; +import io.swagger.v3.oas.annotations.media.Schema; + /** * Defines origin of the rule. * * @author ashvayka * */ +@Schema(enumAsRef = true) public enum RuleType { SYSTEM, USER; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/Scope.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/Scope.java index 6687c6fde3..33416cf712 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/Scope.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/Scope.java @@ -15,12 +15,15 @@ */ package org.thingsboard.server.common.data.rule; +import io.swagger.v3.oas.annotations.media.Schema; + /** * Defines scope of the rule execution in the actor system * * @author ashvayka * */ +@Schema(enumAsRef = true) public enum Scope { SYSTEM, TENANT, CUSTOMER, DEVICE, RULE; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/script/ScriptLanguage.java b/common/data/src/main/java/org/thingsboard/server/common/data/script/ScriptLanguage.java index f1a0a7b8ff..8d993eec31 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/script/ScriptLanguage.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/script/ScriptLanguage.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.script; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum ScriptLanguage { JS, TBEL } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/Authority.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/Authority.java index 171bb18077..1294572db2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/Authority.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/Authority.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.security; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum Authority { SYS_ADMIN(0), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentialsType.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentialsType.java index 3ae2a0380f..a9d94d6010 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentialsType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentialsType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.security; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum DeviceCredentialsType { ACCESS_TOKEN, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/AccountTwoFaSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/AccountTwoFaSettings.java index b24bd68c52..915b918ae1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/AccountTwoFaSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/AccountTwoFaSettings.java @@ -15,12 +15,18 @@ */ package org.thingsboard.server.common.data.security.model.mfa.account; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType; import java.util.LinkedHashMap; @Data +@Schema(description = "Account Two-Factor Authentication Settings") public class AccountTwoFaSettings { + @Schema( + description = "Map of Two-Factor Authentication provider configurations, keyed by provider type", + example = "{\"TOTP\": {...}, \"SMS\": {...}, \"EMAIL\": {...}}" + ) private LinkedHashMap configs; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/TwoFaAccountConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/TwoFaAccountConfig.java index d8783178e0..6199b50816 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/TwoFaAccountConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/TwoFaAccountConfig.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 lombok.Data; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType; @@ -35,6 +37,16 @@ import java.io.Serializable; @Type(name = "EMAIL", value = EmailTwoFaAccountConfig.class), @Type(name = "BACKUP_CODE", value = BackupCodeTwoFaAccountConfig.class) }) +@Schema( + description = "Base configuration for two-factor authentication accounts", + discriminatorProperty = "providerType", + discriminatorMapping = { + @DiscriminatorMapping(value = "TOTP", schema = TotpTwoFaAccountConfig.class), + @DiscriminatorMapping(value = "SMS", schema = SmsTwoFaAccountConfig.class), + @DiscriminatorMapping(value = "EMAIL", schema = EmailTwoFaAccountConfig.class), + @DiscriminatorMapping(value = "BACKUP_CODE", schema = BackupCodeTwoFaAccountConfig.class) + } +) @Data public abstract class TwoFaAccountConfig implements Serializable { @@ -43,6 +55,7 @@ public abstract class TwoFaAccountConfig implements Serializable { @JsonIgnore protected transient boolean serializeHiddenFields; + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Two-Factor Authentication provider type") @JsonIgnore public abstract TwoFaProviderType getProviderType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/TwoFaProviderType.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/TwoFaProviderType.java index fb31135ee5..40319f917b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/TwoFaProviderType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/TwoFaProviderType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.security.model.mfa.provider; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum TwoFaProviderType { TOTP, SMS, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserDashboardAction.java b/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserDashboardAction.java index 9a3e568971..9923728baa 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserDashboardAction.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserDashboardAction.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.settings; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum UserDashboardAction { VISIT, STAR, UNSTAR diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserSettingsType.java b/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserSettingsType.java index 2ecf296a30..8c6d523f21 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserSettingsType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserSettingsType.java @@ -15,8 +15,10 @@ */ package org.thingsboard.server.common.data.settings; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; +@Schema(enumAsRef = true) public enum UserSettingsType { GENERAL, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java index 02f1db557b..57ddff1eb3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java @@ -89,7 +89,8 @@ public class SmppSmsProviderConfiguration implements SmsProviderConfiguration { @Schema(description = "Address range", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String addressRange; - @Schema(allowableValues = {"0-10" ,"13-14"}, + @Schema(minimum = "0", + maximum = "14", description = "0 - SMSC Default Alphabet (ASCII for short and long code and to GSM for toll-free, used as default)\n" + "1 - IA5 (ASCII for short and long code, Latin 9 for toll-free (ISO-8859-9))\n" + "2 - Octet Unspecified (8-bit binary)\n" + @@ -110,6 +111,7 @@ public class SmppSmsProviderConfiguration implements SmsProviderConfiguration { return SmsProviderType.SMPP; } + @Schema(enumAsRef = true) public enum SmppBindType { TX, RX, TRX } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java index 5bba9a578b..44693388cf 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java @@ -19,6 +19,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 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; @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo( @@ -30,9 +32,19 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; @JsonSubTypes.Type(value = TwilioSmsProviderConfiguration.class, name = "TWILIO"), @JsonSubTypes.Type(value = SmppSmsProviderConfiguration.class, name = "SMPP") }) +@Schema( + description = "Base configuration for SMS providers", + discriminatorProperty = "type", + discriminatorMapping = { + @DiscriminatorMapping(value = "AWS_SNS", schema = AwsSnsSmsProviderConfiguration.class), + @DiscriminatorMapping(value = "TWILIO", schema = TwilioSmsProviderConfiguration.class), + @DiscriminatorMapping(value = "SMPP", schema = SmppSmsProviderConfiguration.class) + } +) public interface SmsProviderConfiguration { @JsonIgnore + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) SmsProviderType getType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java index 3046098e57..b169a32c5b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.sms.config; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum SmsProviderType { AWS_SNS, TWILIO, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/EntityExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/EntityExportData.java index 01b07026fa..64ab1f5a49 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/EntityExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/EntityExportData.java @@ -23,6 +23,9 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.DiscriminatorMapping; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; @@ -45,6 +48,17 @@ import java.util.Map; @Type(name = "OTA_PACKAGE", value = OtaPackageExportData.class) }) @JsonInclude(JsonInclude.Include.NON_NULL) +@Schema( + description = "Base export container for ThingsBoard entities", + discriminatorProperty = "entityType", + discriminatorMapping = { + @DiscriminatorMapping(value = "DEVICE", schema = DeviceExportData.class), + @DiscriminatorMapping(value = "RULE_CHAIN", schema = RuleChainExportData.class), + @DiscriminatorMapping(value = "WIDGET_TYPE", schema = WidgetTypeExportData.class), + @DiscriminatorMapping(value = "WIDGETS_BUNDLE", schema = WidgetsBundleExportData.class), + @DiscriminatorMapping(value = "OTA_PACKAGE", schema = OtaPackageExportData.class) + } +) @Data public class EntityExportData> { @@ -61,16 +75,21 @@ public class EntityExportData> { @JsonProperty(index = 2) @JsonTbEntity + @Schema private E entity; @JsonProperty(index = 1) + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) private EntityType entityType; @JsonProperty(index = 100) + @ArraySchema(schema = @Schema(implementation = EntityRelation.class)) private List relations; @JsonProperty(index = 101) + @Schema(description = "Map of attributes where key is the scope of attributes and value is the list of attributes for that scope") private Map> attributes; @JsonProperty(index = 102) @JsonIgnoreProperties({"id", "entityId", "createdTime", "version"}) + @ArraySchema(schema = @Schema(implementation = CalculatedField.class)) private List calculatedFields; public EntityExportData sort() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/create/SyncStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/create/SyncStrategy.java index 4d1305ac23..32e7eb4a42 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/create/SyncStrategy.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/create/SyncStrategy.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.sync.vc.request.create; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum SyncStrategy { MERGE, OVERWRITE diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/create/VersionCreateRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/create/VersionCreateRequest.java index 1939f2e222..a3e3071474 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/create/VersionCreateRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/create/VersionCreateRequest.java @@ -18,8 +18,18 @@ package org.thingsboard.server.common.data.sync.vc.request.create; 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 lombok.Data; +@Schema( + description = "Request for creating a version", + discriminatorProperty = "type", + discriminatorMapping = { + @DiscriminatorMapping(value = "SINGLE_ENTITY", schema = SingleEntityVersionCreateRequest.class), + @DiscriminatorMapping(value = "COMPLEX", schema = ComplexVersionCreateRequest.class) + } +) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ @Type(name = "SINGLE_ENTITY", value = SingleEntityVersionCreateRequest.class), @@ -31,6 +41,7 @@ public abstract class VersionCreateRequest { private String versionName; private String branch; + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Type of the version to create") public abstract VersionCreateRequestType getType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/create/VersionCreateRequestType.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/create/VersionCreateRequestType.java index 97d73b76b1..26d6d6209f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/create/VersionCreateRequestType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/create/VersionCreateRequestType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.sync.vc.request.create; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum VersionCreateRequestType { SINGLE_ENTITY, COMPLEX diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequest.java index dffa3bdf3d..85b2def3e1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequest.java @@ -17,10 +17,20 @@ package org.thingsboard.server.common.data.sync.vc.request.load; 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 lombok.Data; import static com.fasterxml.jackson.annotation.JsonSubTypes.Type; +@Schema( + description = "Request for loading a version", + discriminatorProperty = "type", + discriminatorMapping = { + @DiscriminatorMapping(value = "SINGLE_ENTITY", schema = SingleEntityVersionLoadRequest.class), + @DiscriminatorMapping(value = "ENTITY_TYPE", schema = EntityTypeVersionLoadRequest.class) + } +) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ @Type(name = "SINGLE_ENTITY", value = SingleEntityVersionLoadRequest.class), @@ -31,6 +41,7 @@ public abstract class VersionLoadRequest { private String versionId; + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Type of the version to load") public abstract VersionLoadRequestType getType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequestType.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequestType.java index 14092b1742..447a16c7f7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequestType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequestType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.sync.vc.request.load; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum VersionLoadRequestType { SINGLE_ENTITY, ENTITY_TYPE diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/resource/ResourceType.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/resource/ResourceType.java index 973b4807d3..64aea6ff64 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/resource/ResourceType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/resource/ResourceType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.transport.resource; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum ResourceType { LWM2M_MODEL, JKS, PKCS_12 } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/AuthenticationProtocol.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/AuthenticationProtocol.java index 3ac372e7b4..6bb9e5a757 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/AuthenticationProtocol.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/AuthenticationProtocol.java @@ -15,9 +15,12 @@ */ package org.thingsboard.server.common.data.transport.snmp; +import io.swagger.v3.oas.annotations.media.Schema; + import java.util.Arrays; import java.util.Optional; +@Schema(enumAsRef = true) public enum AuthenticationProtocol { SHA_1("1.3.6.1.6.3.10.1.1.3"), SHA_224("1.3.6.1.6.3.10.1.1.4"), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/PrivacyProtocol.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/PrivacyProtocol.java index d152dbf827..de960057ae 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/PrivacyProtocol.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/PrivacyProtocol.java @@ -15,9 +15,12 @@ */ package org.thingsboard.server.common.data.transport.snmp; +import io.swagger.v3.oas.annotations.media.Schema; + import java.util.Arrays; import java.util.Optional; +@Schema(enumAsRef = true) public enum PrivacyProtocol { DES("1.3.6.1.6.3.10.1.2.2"), AES_128("1.3.6.1.6.3.10.1.2.4"), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java index 796b199825..e26d0a7170 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java @@ -15,9 +15,11 @@ */ package org.thingsboard.server.common.data.transport.snmp; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Getter; +@Schema(enumAsRef = true) @AllArgsConstructor @Getter public enum SnmpCommunicationSpec { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.java index 0fa4d8f053..d38733af24 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.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 org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; import org.thingsboard.server.common.data.transport.snmp.SnmpMapping; import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; @@ -31,6 +33,17 @@ import org.thingsboard.server.common.data.transport.snmp.config.impl.ToDeviceRpc import java.io.Serializable; import java.util.List; +@Schema( + description = "SNMP communication configuration", + discriminatorProperty = "spec", + discriminatorMapping = { + @DiscriminatorMapping(value = "TELEMETRY_QUERYING", schema = TelemetryQueryingSnmpCommunicationConfig.class), + @DiscriminatorMapping(value = "CLIENT_ATTRIBUTES_QUERYING", schema = ClientAttributesQueryingSnmpCommunicationConfig.class), + @DiscriminatorMapping(value = "SHARED_ATTRIBUTES_SETTING", schema = SharedAttributesSettingSnmpCommunicationConfig.class), + @DiscriminatorMapping(value = "TO_DEVICE_RPC_REQUEST", schema = ToDeviceRpcRequestSnmpCommunicationConfig.class), + @DiscriminatorMapping(value = "TO_SERVER_RPC_REQUEST", schema = ToServerRpcRequestSnmpCommunicationConfig.class) + } +) @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "spec") @JsonSubTypes({ @@ -42,6 +55,7 @@ import java.util.List; }) public interface SnmpCommunicationConfig extends Serializable { + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Specification of the SNMP communication") SnmpCommunicationSpec getSpec(); @JsonIgnore diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/DeprecatedFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/DeprecatedFilter.java index d30f22b18e..c65467f6f6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/DeprecatedFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/DeprecatedFilter.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.widget; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum DeprecatedFilter { ALL, ACTUAL, diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index 9a7cfe43ff..6595185a02 100644 --- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -20,6 +20,7 @@ import com.google.gson.JsonParseException; import com.google.gson.JsonParser; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; @@ -46,6 +47,7 @@ import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbTransportService; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.rpc.RpcStatus; From d00309b8dabd1bd7cb775358c18bfb61a8d9377e Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Mon, 9 Feb 2026 13:56:15 +0200 Subject: [PATCH 024/224] added appropriate swagger annotations for correct open-api json --- .../server/config/SwaggerConfiguration.java | 26 +++++++++++++++++++ ...esDeviceProfileProvisionConfiguration.java | 3 +++ ...esDeviceProfileProvisionConfiguration.java | 3 +++ ...apDeviceProfileTransportConfiguration.java | 2 ++ .../profile/CoapDeviceTypeConfiguration.java | 11 ++++++++ .../device/profile/DeviceProfileData.java | 3 ++- .../DeviceProfileProvisionConfiguration.java | 14 ++++++++++ ...edDeviceProfileProvisionConfiguration.java | 3 +++ ...ertificateChainProvisionConfiguration.java | 3 +++ 9 files changed, 67 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java index 967b33299e..658029a2fe 100644 --- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java @@ -391,6 +391,32 @@ public class SwaggerConfiguration { }); } }); + + schemas.values().forEach(schema -> { + if (schema.getProperties() != null) { + schema.getProperties().forEach((propName, propSchema) -> { + if (propSchema instanceof Schema) { + Schema prop = (Schema) propSchema; + + // If property has oneOf, try to find the base discriminated type + if (prop.getOneOf() != null && !prop.getOneOf().isEmpty()) { + String baseType = findBaseTypeForOneOf(schemas, prop.getOneOf()); + + if (baseType != null) { + // Replace oneOf with reference to base type + 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); + } + } + } + }); + } + }); } var sortedSchemas = new TreeMap<>(openAPI.getComponents().getSchemas()); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AllowCreateNewDevicesDeviceProfileProvisionConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AllowCreateNewDevicesDeviceProfileProvisionConfiguration.java index b33b56ba82..53a359bb9f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AllowCreateNewDevicesDeviceProfileProvisionConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AllowCreateNewDevicesDeviceProfileProvisionConfiguration.java @@ -15,15 +15,18 @@ */ package org.thingsboard.server.common.data.device.profile; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.thingsboard.server.common.data.DeviceProfileProvisionType; @Data +@Schema public class AllowCreateNewDevicesDeviceProfileProvisionConfiguration implements DeviceProfileProvisionConfiguration { private final String provisionDeviceSecret; @Override + @Schema(description = "Device profile provision type", requiredMode = Schema.RequiredMode.REQUIRED) public DeviceProfileProvisionType getType() { return DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration.java index 7a1d6d1a95..8fb596a953 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration.java @@ -15,14 +15,17 @@ */ package org.thingsboard.server.common.data.device.profile; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.thingsboard.server.common.data.DeviceProfileProvisionType; @Data +@Schema public class CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration implements DeviceProfileProvisionConfiguration { private final String provisionDeviceSecret; + @Schema(description = "Device profile provision type", requiredMode = Schema.RequiredMode.REQUIRED) @Override public DeviceProfileProvisionType getType() { return DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceProfileTransportConfiguration.java index 0b607892a8..029b49be1c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceProfileTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceProfileTransportConfiguration.java @@ -24,7 +24,9 @@ import org.thingsboard.server.common.data.device.data.PowerSavingConfiguration; @Data public class CoapDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { + @Schema private CoapDeviceTypeConfiguration coapDeviceTypeConfiguration; + @Schema private PowerSavingConfiguration clientSettings; @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Type of the device transport") diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceTypeConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceTypeConfiguration.java index 66907288e9..3ebcdd4f27 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceTypeConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceTypeConfiguration.java @@ -19,10 +19,20 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 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.CoapDeviceType; import java.io.Serializable; +@Schema( + description = "CoAP device type configuration", + discriminatorProperty = "coapDeviceType", + discriminatorMapping = { + @DiscriminatorMapping(value = "DEFAULT", schema = DefaultCoapDeviceTypeConfiguration.class), + @DiscriminatorMapping(value = "EFENTO", schema = EfentoCoapDeviceTypeConfiguration.class) + } +) @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -33,6 +43,7 @@ import java.io.Serializable; @JsonSubTypes.Type(value = EfentoCoapDeviceTypeConfiguration.class, name = "EFENTO")}) public interface CoapDeviceTypeConfiguration extends Serializable { + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "CoAP device type") @JsonIgnore CoapDeviceType getCoapDeviceType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java index 636f546386..c0a9e5646e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.device.profile; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import lombok.Data; @@ -36,7 +37,7 @@ public class DeviceProfileData implements Serializable { @Schema(description = "JSON object of provisioning strategy type per device profile") private DeviceProfileProvisionConfiguration provisionConfiguration; @Valid - @Schema(description = "JSON array of alarm rules configuration per device profile") + @ArraySchema(schema = @Schema(implementation = DeviceProfileAlarm.class)) private List alarms; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileProvisionConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileProvisionConfiguration.java index 45ac5207f5..dcf3cd5ddf 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileProvisionConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileProvisionConfiguration.java @@ -19,10 +19,22 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 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.DeviceProfileProvisionType; import java.io.Serializable; +@Schema( + description = "Device profile provision configuration", + discriminatorProperty = "type", + discriminatorMapping = { + @DiscriminatorMapping(value = "DISABLED", schema = DisabledDeviceProfileProvisionConfiguration.class), + @DiscriminatorMapping(value = "ALLOW_CREATE_NEW_DEVICES", schema = AllowCreateNewDevicesDeviceProfileProvisionConfiguration.class), + @DiscriminatorMapping(value = "CHECK_PRE_PROVISIONED_DEVICES", schema = CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration.class), + @DiscriminatorMapping(value = "X509_CERTIFICATE_CHAIN", schema = X509CertificateChainProvisionConfiguration.class) + } +) @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -35,8 +47,10 @@ import java.io.Serializable; @JsonSubTypes.Type(value = X509CertificateChainProvisionConfiguration.class, name = "X509_CERTIFICATE_CHAIN")}) public interface DeviceProfileProvisionConfiguration extends Serializable { + @Schema(description = "Provision device secret", example = "secret123") String getProvisionDeviceSecret(); + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Device profile provision type") @JsonIgnore DeviceProfileProvisionType getType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DisabledDeviceProfileProvisionConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DisabledDeviceProfileProvisionConfiguration.java index 7492c077c8..139fe88645 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DisabledDeviceProfileProvisionConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DisabledDeviceProfileProvisionConfiguration.java @@ -15,15 +15,18 @@ */ package org.thingsboard.server.common.data.device.profile; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.thingsboard.server.common.data.DeviceProfileProvisionType; @Data +@Schema public class DisabledDeviceProfileProvisionConfiguration implements DeviceProfileProvisionConfiguration { private final String provisionDeviceSecret; @Override + @Schema(description = "Device profile provision type", requiredMode = Schema.RequiredMode.REQUIRED) public DeviceProfileProvisionType getType() { return DeviceProfileProvisionType.DISABLED; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/X509CertificateChainProvisionConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/X509CertificateChainProvisionConfiguration.java index ad0cdaa6bf..a20e5d73e3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/X509CertificateChainProvisionConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/X509CertificateChainProvisionConfiguration.java @@ -15,10 +15,12 @@ */ package org.thingsboard.server.common.data.device.profile; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.DeviceProfileProvisionType; +@Schema @Data @NoArgsConstructor public class X509CertificateChainProvisionConfiguration implements DeviceProfileProvisionConfiguration { @@ -28,6 +30,7 @@ public class X509CertificateChainProvisionConfiguration implements DeviceProfile private boolean allowCreateNewDevicesByX509Certificate; @Override + @Schema(description = "Device profile provision type", requiredMode = Schema.RequiredMode.REQUIRED) public DeviceProfileProvisionType getType() { return DeviceProfileProvisionType.X509_CERTIFICATE_CHAIN; } From b07858b1137d993de1758e8049387073406bc352 Mon Sep 17 00:00:00 2001 From: Vladyslav Prykhodko Date: Mon, 9 Feb 2026 15:33:51 +0200 Subject: [PATCH 025/224] UI: Improved default tenant home dashboard (#15000) --- .../recent-dashboards-widget.component.html | 11 ++++- .../recent-dashboards-widget.component.ts | 45 +++++++++++++++++++ .../dashboard/tenant_admin_home_page.json | 2 +- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html index f832b1982e..e8fba081ee 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html @@ -24,8 +24,15 @@ {{ 'widgets.recent-dashboards.last' | translate }} {{ 'widgets.recent-dashboards.starred' | translate }} - {{ 'dashboard.add' | translate }} + + @if (hasDevice) { + {{ 'dashboard.add' | translate }} + } @else { + {{ 'dashboard.add' | translate }} + } + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts index 899ffa5aea..5dfc520856 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts @@ -48,6 +48,13 @@ import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { MatSort } from '@angular/material/sort'; import { DashboardInfo } from '@shared/models/dashboard.models'; import { DashboardAutocompleteComponent } from '@shared/components/dashboard-autocomplete.component'; +import { UtilsService } from '@core/services/utils.service'; +import { Datasource, DatasourceType, widgetType } from '@shared/models/widget.models'; +import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; +import { formattedDataFormDatasourceData } from '@core/utils'; +import { AliasFilterType } from '@shared/models/alias.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { EntityType } from '@shared/models/entity-type.models'; @Component({ selector: 'tb-recent-dashboards-widget', @@ -78,13 +85,16 @@ export class RecentDashboardsWidgetComponent extends PageComponent implements On starredDashboardValue = null; hasDashboardsAccess = true; + hasDevice = true; dirty = false; public customerId: string; private isFullscreenMode = getCurrentAuthState(this.store).forceFullscreen; + private subscription: IWidgetSubscription; constructor(protected store: Store, private cd: ChangeDetectorRef, + private utils: UtilsService, private userSettingService: UserSettingsService) { super(store); } @@ -96,6 +106,41 @@ export class RecentDashboardsWidgetComponent extends PageComponent implements On this.hasDashboardsAccess = [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER].includes(this.authUser.authority); if (this.hasDashboardsAccess) { this.reload(); + + if (window.location.pathname.startsWith('/home') && this.authUser.authority === Authority.TENANT_ADMIN) { + const ds: Datasource = { + type: DatasourceType.entityCount, + name: '', + entityFilter: { + entityType: EntityType.DEVICE, + type: AliasFilterType.entityType + }, + dataKeys: [this.utils.createKey({ name: 'count'}, DataKeyType.count)] + } + + const apiUsageSubscriptionOptions: WidgetSubscriptionOptions = { + datasources: [ds], + useDashboardTimewindow: false, + type: widgetType.latest, + callbacks: { + onDataUpdated: (subscription) => { + const data = formattedDataFormDatasourceData(subscription.data); + this.hasDevice = (data[0].count || 0) !== 0; + this.cd.detectChanges(); + } + } + }; + this.ctx.subscriptionApi.createSubscription(apiUsageSubscriptionOptions, true).subscribe((subscription) => { + this.subscription = subscription; + }); + } + } + } + + ngOnDestroy() { + super.ngOnDestroy(); + if (this.subscription) { + this.ctx.subscriptionApi.removeSubscription(this.subscription.id); } } diff --git a/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json b/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json index f09ab73de7..9fb2d25cf8 100644 --- a/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json +++ b/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json @@ -224,7 +224,7 @@ "padding": "16px", "settings": { "useMarkdownTextFunction": false, - "markdownTextPattern": "", + "markdownTextPattern": "", "applyDefaultMarkdownStyle": false, "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n gap: 12px;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-item-cards {\n flex: 1;\n display: flex;\n flex-direction: row;\n gap: 12px;\n overflow: hidden;\n}\n\na.tb-item-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n padding: 8px 12px;\n border: 1px solid;\n border-radius: 10px;\n margin-bottom: 12px;\n overflow: hidden;\n justify-content: space-evenly;\n}\n\na.tb-item-card.tb-inactive {\n background: rgba(209, 39, 48, 0.04);\n border-color: rgba(209, 39, 48, 0.06);\n}\n\na.tb-item-card.tb-active {\n background: rgba(48, 86, 128, 0.04);\n border-color: rgba(48, 86, 128, 0.12);\n}\n\na.tb-item-card.tb-total {\n background: rgba(0, 0, 0, 0.01);\n border-color: rgba(0, 0, 0, 0.05);\n}\n\n.tb-item-title-container {\n display: grid;\n}\n\n.tb-item-title {\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis; \n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-item-title.tb-home-widget-link:after {\n position: absolute;\n right: 0;\n}\n\na.tb-item-card:hover .tb-item-title.tb-home-widget-link:after { \n color: rgba(0, 0, 0, 0.38);\n}\n\na.tb-item-card:hover {\n box-shadow: 0px 4px 10px rgba(23, 33, 90, 0.08);\n}\n\n.tb-count-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 24px;\n line-height: 36px;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.87);\n}\n\n@media screen and (max-width: 959px) {\n .tb-item-cards {\n flex-direction: column;\n }\n a.tb-item-card {\n margin-bottom: 0;\n }\n}\n\n@media screen and (max-width: 1279px) {\n a.tb-item-card {\n flex-direction: row;\n align-items: center;\n }\n .tb-item-title.tb-home-widget-link:after {\n position: relative;\n }\n .tb-count-container {\n align-items: flex-end;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-item-title {\n font-size: 11px;\n line-height: 16px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n a.tb-item-card {\n padding: 4px 8px;\n margin-bottom: 6px;\n }\n a.tb-item-card:hover {\n box-shadow: 0px 2px 5px rgba(23, 33, 90, 0.08);\n }\n}\n" }, From e0151095f743dbd2dcc499b5654a6f51c2b7a82c Mon Sep 17 00:00:00 2001 From: Vladyslav Prykhodko Date: Mon, 9 Feb 2026 15:40:32 +0200 Subject: [PATCH 026/224] Changed default "Add" button style in entity tables (#14984) * UI: Use accent text button as default "Add" style in entity tables * UI: Change default text button style in entity table * UI: Change default text button style in image gallery --------- Co-authored-by: Viacheslav Klimov --- .../alarm-rules/alarm-rules-table-config.ts | 4 +- .../api-key/api-keys-table-config.ts | 1 - .../calculated-fields-table-config.ts | 2 + .../entity/entities-table.component.html | 104 ++++++++++++------ .../vc/entity-versions-table.component.html | 18 +-- .../entity/entities-table-config.models.ts | 2 +- .../ai-model/ai-model-table-config.resolve.ts | 1 - .../mobile-app-table-config.resolver.ts | 1 - .../mobile-bundle-table-config.resolve.ts | 1 - .../recipient-table-config.resolver.ts | 1 - .../rule/rule-table-config.resolver.ts | 1 - .../template-table-config.resolver.ts | 1 - .../image/image-gallery.component.html | 6 +- .../app/shared/models/entity-type.models.ts | 1 + .../assets/locale/locale.constant-en_US.json | 1 + 15 files changed, 94 insertions(+), 51 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts index cadac572c6..039ee7d5fd 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts @@ -106,6 +106,8 @@ export class AlarmRulesTableConfig extends EntityTableConfig { this.entityType = EntityType.API_KEY; this.detailsPanelEnabled = false; - this.addAsTextButton = true; this.pageMode = false; this.entityTranslations = entityTypeTranslations.get(EntityType.API_KEY); diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts index efb767f92e..aac49a3f37 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts @@ -110,6 +110,8 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig -
- - - - - - - - + - - -
@@ -118,6 +94,72 @@ matTooltipPosition="above"> search +
+ + + + + + + + + + + + + + + + +
+ + + diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html b/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html index cc3fb23da5..829c1414db 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html @@ -32,7 +32,8 @@ -
+
-
+
diff --git a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts index 913e754fb7..336ac035da 100644 --- a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts +++ b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts @@ -175,7 +175,7 @@ export class EntityTableConfig, P extends PageLink = P selectionEnabled = true; searchEnabled = true; addEnabled = true; - addAsTextButton = false; + addAsTextButton = true; entitiesDeleteEnabled = true; detailsPanelEnabled = true; hideDetailsTabsOnEdit = true; diff --git a/ui-ngx/src/app/modules/home/pages/ai-model/ai-model-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/ai-model/ai-model-table-config.resolve.ts index 2a73488d7e..7c78bd7c02 100644 --- a/ui-ngx/src/app/modules/home/pages/ai-model/ai-model-table-config.resolve.ts +++ b/ui-ngx/src/app/modules/home/pages/ai-model/ai-model-table-config.resolve.ts @@ -47,7 +47,6 @@ export class AiModelsTableConfigResolver { ) { this.config.selectionEnabled = true; this.config.entityType = EntityType.AI_MODEL; - this.config.addAsTextButton = true; this.config.rowPointer = true; this.config.detailsPanelEnabled = false; this.config.entityTranslations = entityTypeTranslations.get(EntityType.AI_MODEL); diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts index 13698ac4a5..274edf5933 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts @@ -57,7 +57,6 @@ export class MobileAppTableConfigResolver { ) { this.config.selectionEnabled = false; this.config.entityType = EntityType.MOBILE_APP; - this.config.addAsTextButton = true; this.config.entitiesDeleteEnabled = false; this.config.rowPointer = true; this.config.entityTranslations = entityTypeTranslations.get(EntityType.MOBILE_APP); diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts index 69be801980..ba20624852 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts @@ -63,7 +63,6 @@ export class MobileBundleTableConfigResolver { ) { this.config.selectionEnabled = false; this.config.entityType = EntityType.MOBILE_APP_BUNDLE; - this.config.addAsTextButton = true; this.config.rowPointer = true; this.config.detailsPanelEnabled = false; this.config.entityTranslations = entityTypeTranslations.get(EntityType.MOBILE_APP_BUNDLE); diff --git a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts index c2fc08fb7c..8e729e94ef 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts @@ -48,7 +48,6 @@ export class RecipientTableConfigResolver { this.config.entityType = EntityType.NOTIFICATION_TARGET; this.config.detailsPanelEnabled = false; - this.config.addAsTextButton = true; this.config.rowPointer = true; this.config.entityTranslations = entityTypeTranslations.get(EntityType.NOTIFICATION_TARGET); diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts index 066db92c0a..f71197ae95 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts @@ -49,7 +49,6 @@ export class RuleTableConfigResolver { this.config.entityType = EntityType.NOTIFICATION_RULE; this.config.detailsPanelEnabled = false; - this.config.addAsTextButton = true; this.config.rowPointer = true; this.config.entityTranslations = entityTypeTranslations.get(EntityType.NOTIFICATION_RULE); diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/template-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/template/template-table-config.resolver.ts index 0ee0b921f4..b1bc3eef3e 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/template-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/template/template-table-config.resolver.ts @@ -47,7 +47,6 @@ export class TemplateTableConfigResolver { this.config.entityType = EntityType.NOTIFICATION_TEMPLATE; this.config.detailsPanelEnabled = false; - this.config.addAsTextButton = true; this.config.rowPointer = true; this.config.entityTranslations = entityTypeTranslations.get(EntityType.NOTIFICATION_TEMPLATE); diff --git a/ui-ngx/src/app/shared/components/image/image-gallery.component.html b/ui-ngx/src/app/shared/components/image/image-gallery.component.html index f442556b18..b31dc16920 100644 --- a/ui-ngx/src/app/shared/components/image/image-gallery.component.html +++ b/ui-ngx/src/app/shared/components/image/image-gallery.component.html @@ -74,10 +74,10 @@ mdi:file-import