From e09aef8d224781d5ab1267c1d2909a76c1721724 Mon Sep 17 00:00:00 2001 From: thingsboard Date: Wed, 20 Jul 2022 17:43:46 +0300 Subject: [PATCH 001/488] added ability to create custom attrs subscribe topic --- .../mqtt/AbstractMqttIntegrationTest.java | 2 +- .../MqttDeviceProfileTransportConfiguration.java | 5 ++++- .../transport/mqtt/MqttTransportHandler.java | 5 +++++ .../transport/mqtt/session/DeviceSessionCtx.java | 14 ++++++++++---- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java index 9fbdff3fa5..ec64ce4af3 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java @@ -106,7 +106,7 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(config.getTelemetryTopicFilter()); } if (StringUtils.hasLength(config.getAttributesTopicFilter())) { - mqttDeviceProfileTransportConfiguration.setDeviceAttributesTopic(config.getAttributesTopicFilter()); + mqttDeviceProfileTransportConfiguration.setDeviceAttributesPublishTopic(config.getAttributesTopicFilter()); } mqttDeviceProfileTransportConfiguration.setSendAckOnValidationException(config.isSendAckOnValidationException()); TransportPayloadTypeConfiguration transportPayloadTypeConfiguration; 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 1a4d2c72ad..8c1493bce7 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 @@ -25,7 +25,10 @@ public class MqttDeviceProfileTransportConfiguration implements DeviceProfileTra @NoXss private String deviceTelemetryTopic = MqttTopics.DEVICE_TELEMETRY_TOPIC; @NoXss - private String deviceAttributesTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC; + private String deviceAttributesPublishTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC; + @NoXss + private String deviceAttributesSubscribeTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC;//todo + private TransportPayloadTypeConfiguration transportPayloadTypeConfiguration; private boolean sendAckOnValidationException; diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index f37715efc8..288f32ec4e 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -622,6 +622,11 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement for (MqttTopicSubscription subscription : mqttMsg.payload().topicSubscriptions()) { String topic = subscription.topicName(); MqttQoS reqQoS = subscription.qualityOfService(); + if (deviceSessionCtx.isDeviceSubscriptionAttributesTopic(topic)){ + processAttributesSubscribe(grantedQoSList, topic, reqQoS, TopicType.V1); + activityReported = true; + continue; + } try { switch (topic) { case MqttTopics.DEVICE_ATTRIBUTES_TOPIC: { diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java index 2163610b38..4b69f75f06 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java @@ -75,7 +75,8 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { private boolean provisionOnly = false; private volatile MqttTopicFilter telemetryTopicFilter = MqttTopicFilterFactory.getDefaultTelemetryFilter(); - private volatile MqttTopicFilter attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter(); + private volatile MqttTopicFilter attributesPublishTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter(); + private volatile MqttTopicFilter attributesSubscribeTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter(); private volatile TransportPayloadType payloadType = TransportPayloadType.JSON; private volatile Descriptors.Descriptor attributesDynamicMessageDescriptor; private volatile Descriptors.Descriptor telemetryDynamicMessageDescriptor; @@ -105,7 +106,11 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { } public boolean isDeviceAttributesTopic(String topicName) { - return attributesTopicFilter.filter(topicName); + return attributesPublishTopicFilter.filter(topicName); + } + + public boolean isDeviceSubscriptionAttributesTopic(String topicName) { + return attributesSubscribeTopicFilter.filter(topicName); } public MqttTransportAdaptor getPayloadAdaptor() { @@ -156,7 +161,8 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttConfig.getTransportPayloadTypeConfiguration(); payloadType = transportPayloadTypeConfiguration.getTransportPayloadType(); telemetryTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceTelemetryTopic()); - attributesTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesTopic()); + attributesPublishTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesPublishTopic()); + attributesSubscribeTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesSubscribeTopic()); sendAckOnValidationException = mqttConfig.isSendAckOnValidationException(); if (TransportPayloadType.PROTOBUF.equals(payloadType)) { ProtoTransportPayloadConfiguration protoTransportPayloadConfig = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; @@ -166,7 +172,7 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { } } else { telemetryTopicFilter = MqttTopicFilterFactory.getDefaultTelemetryFilter(); - attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter(); + attributesPublishTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter(); payloadType = TransportPayloadType.JSON; sendAckOnValidationException = false; } From 7cb65c5e159adc2bb3887cbdaf53d200a4ca0c24 Mon Sep 17 00:00:00 2001 From: thingsboard Date: Fri, 22 Jul 2022 16:45:26 +0300 Subject: [PATCH 002/488] added tests --- .../server/controller/AbstractWebTest.java | 13 +++++++++- .../BaseDeviceProfileControllerTest.java | 24 ++++++++++++++++++- .../MqttAttributesUpdatesIntegrationTest.java | 16 +++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 677303cc15..91ff2df22f 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -452,7 +452,18 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected MqttDeviceProfileTransportConfiguration createMqttDeviceProfileTransportConfiguration(TransportPayloadTypeConfiguration transportPayloadTypeConfiguration, boolean sendAckOnValidationException) { MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration(); mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(MqttTopics.DEVICE_TELEMETRY_TOPIC); - mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(MqttTopics.DEVICE_ATTRIBUTES_TOPIC); + mqttDeviceProfileTransportConfiguration.setDeviceAttributesPublishTopic(MqttTopics.DEVICE_ATTRIBUTES_TOPIC); //todo + mqttDeviceProfileTransportConfiguration.setSendAckOnValidationException(sendAckOnValidationException); + mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration); + return mqttDeviceProfileTransportConfiguration; + } + + protected MqttDeviceProfileTransportConfiguration createMqttDeviceProfileTransportConfiguration(TransportPayloadTypeConfiguration transportPayloadTypeConfiguration, boolean sendAckOnValidationException, + String telemetryTopic, String attributesPublishTopic, String attributesSubscribeTopic) { + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration(); + mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(telemetryTopic); + mqttDeviceProfileTransportConfiguration.setDeviceAttributesPublishTopic(attributesPublishTopic); + mqttDeviceProfileTransportConfiguration.setDeviceAttributesSubscribeTopic(attributesSubscribeTopic); mqttDeviceProfileTransportConfiguration.setSendAckOnValidationException(sendAckOnValidationException); mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration); return mqttDeviceProfileTransportConfiguration; diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java index d0c4a1d7ce..578a410a88 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java @@ -74,7 +74,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; -public abstract class BaseDeviceProfileControllerTest extends AbstractControllerTest { +public abstract class BaseDeviceProfileControllerTest extends AbstractControllerTest { private IdComparator idComparator = new IdComparator<>(); private IdComparator deviceProfileInfoIdComparator = new IdComparator<>(); @@ -1124,6 +1124,28 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController Assert.assertEquals(savedDeviceProfile, foundDeviceProfile); } + @Test + public void testSaveDeviceProfileWorks() throws Exception { //todo + JsonTransportPayloadConfiguration jsonTransportPayloadConfiguration = new JsonTransportPayloadConfiguration(); + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = + this.createMqttDeviceProfileTransportConfiguration(jsonTransportPayloadConfiguration, true, + "v1/devices/me/telemetry", "v1/devices/me/attributes", "v1/devices/me/subscribeattributes"); + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", + mqttDeviceProfileTransportConfiguration); + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); + Assert.assertNotNull(savedDeviceProfile); + Assert.assertEquals(savedDeviceProfile.getTransportType(), DeviceTransportType.MQTT); + Assert.assertTrue(savedDeviceProfile.getProfileData().getTransportConfiguration() instanceof MqttDeviceProfileTransportConfiguration); + MqttDeviceProfileTransportConfiguration transportConfiguration = + (MqttDeviceProfileTransportConfiguration) savedDeviceProfile.getProfileData().getTransportConfiguration(); + Assert.assertTrue(transportConfiguration.isSendAckOnValidationException()); + DeviceProfile foundDeviceProfile = + doGet("/api/deviceProfile/" + savedDeviceProfile.getId().getId().toString(), DeviceProfile.class); + Assert.assertEquals(savedDeviceProfile.getProfileData().getTransportConfiguration(), + foundDeviceProfile.getProfileData().getTransportConfiguration()); + Assert.assertEquals(savedDeviceProfile, foundDeviceProfile); + } + private DeviceProfile testSaveDeviceProfileWithProtoPayloadType(String schema) throws Exception { ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = this.createProtoTransportPayloadConfiguration(schema, schema, null, null); MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration, false); diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesIntegrationTest.java index 01a33c5b5c..0de72a1aa5 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesIntegrationTest.java @@ -18,7 +18,10 @@ package org.thingsboard.server.transport.mqtt.attributes.updates; import lombok.extern.slf4j.Slf4j; import org.junit.Before; import org.junit.Test; +import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.TransportPayloadType; +import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties; import org.thingsboard.server.transport.mqtt.attributes.AbstractMqttAttributesIntegrationTest; @@ -46,6 +49,19 @@ public class MqttAttributesUpdatesIntegrationTest extends AbstractMqttAttributes processJsonTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_TOPIC); } + @Test + public void testJsonSubscribeToAttributesUpdatesFromTheServerOnCustomTopic() throws Exception { + String customTopic = "v1/devices/me/subscribeattributes"; + JsonTransportPayloadConfiguration jsonTransportPayloadConfiguration = new JsonTransportPayloadConfiguration(); + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = + this.createMqttDeviceProfileTransportConfiguration(jsonTransportPayloadConfiguration, true, + "v1/devices/me/telemetry", "v1/devices/me/attributes", customTopic); + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", + mqttDeviceProfileTransportConfiguration); + doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); + processJsonTestSubscribeToAttributesUpdates(customTopic); + } + @Test public void testJsonSubscribeToAttributesUpdatesFromTheServerOnShortTopic() throws Exception { processJsonTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_SHORT_TOPIC); From 62eb43cf0ae5ccafc8a2b8a566f57b990ceff147 Mon Sep 17 00:00:00 2001 From: AndriiD Date: Mon, 25 Jul 2022 13:15:24 +0300 Subject: [PATCH 003/488] small test fix --- .../org/thingsboard/server/controller/AbstractWebTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 31e3df10bb..24f1d647af 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -445,7 +445,8 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected MqttDeviceProfileTransportConfiguration createMqttDeviceProfileTransportConfiguration(TransportPayloadTypeConfiguration transportPayloadTypeConfiguration, boolean sendAckOnValidationException) { MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration(); mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(MqttTopics.DEVICE_TELEMETRY_TOPIC); - mqttDeviceProfileTransportConfiguration.setDeviceAttributesPublishTopic(MqttTopics.DEVICE_ATTRIBUTES_TOPIC); //todo + mqttDeviceProfileTransportConfiguration.setDeviceAttributesPublishTopic(MqttTopics.DEVICE_ATTRIBUTES_TOPIC); + mqttDeviceProfileTransportConfiguration.setDeviceAttributesSubscribeTopic(MqttTopics.DEVICE_ATTRIBUTES_TOPIC); mqttDeviceProfileTransportConfiguration.setSendAckOnValidationException(sendAckOnValidationException); mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration); return mqttDeviceProfileTransportConfiguration; From e64d76a997c8d7935604eab0f0c5397e5ed2023d Mon Sep 17 00:00:00 2001 From: AndriiD Date: Tue, 26 Jul 2022 09:01:38 +0300 Subject: [PATCH 004/488] small test fix --- .../updates/MqttAttributesUpdatesIntegrationTest.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesIntegrationTest.java index 0de72a1aa5..7f736be109 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesIntegrationTest.java @@ -18,6 +18,7 @@ package org.thingsboard.server.transport.mqtt.attributes.updates; import lombok.extern.slf4j.Slf4j; import org.junit.Before; import org.junit.Test; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.TransportPayloadType; import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; @@ -51,15 +52,19 @@ public class MqttAttributesUpdatesIntegrationTest extends AbstractMqttAttributes @Test public void testJsonSubscribeToAttributesUpdatesFromTheServerOnCustomTopic() throws Exception { + Device tmp = savedDevice; String customTopic = "v1/devices/me/subscribeattributes"; JsonTransportPayloadConfiguration jsonTransportPayloadConfiguration = new JsonTransportPayloadConfiguration(); MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(jsonTransportPayloadConfiguration, true, "v1/devices/me/telemetry", "v1/devices/me/attributes", customTopic); - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", + DeviceProfile deviceProfile = this.createDeviceProfile("New device Profile", mqttDeviceProfileTransportConfiguration); - doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); + DeviceProfile savedProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); + savedDevice.setDeviceProfileId(savedProfile.getId()); + doPost("/api/device", savedDevice); processJsonTestSubscribeToAttributesUpdates(customTopic); + savedDevice = tmp; } @Test From 98d18f0f85482e1ac02e4b01fede74965d7cf3fa Mon Sep 17 00:00:00 2001 From: AndriiD Date: Thu, 28 Jul 2022 10:42:16 +0300 Subject: [PATCH 005/488] pr fixes --- .../org/thingsboard/server/controller/AbstractWebTest.java | 5 ++--- .../server/controller/BaseDeviceProfileControllerTest.java | 2 +- .../server/transport/mqtt/AbstractMqttIntegrationTest.java | 7 +------ .../profile/MqttDeviceProfileTransportConfiguration.java | 4 ++-- .../server/transport/mqtt/session/DeviceSessionCtx.java | 2 +- 5 files changed, 7 insertions(+), 13 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 24f1d647af..b32633f536 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -69,7 +69,6 @@ import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeCon import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.HasId; -import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.id.UserId; @@ -445,7 +444,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected MqttDeviceProfileTransportConfiguration createMqttDeviceProfileTransportConfiguration(TransportPayloadTypeConfiguration transportPayloadTypeConfiguration, boolean sendAckOnValidationException) { MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration(); mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(MqttTopics.DEVICE_TELEMETRY_TOPIC); - mqttDeviceProfileTransportConfiguration.setDeviceAttributesPublishTopic(MqttTopics.DEVICE_ATTRIBUTES_TOPIC); + mqttDeviceProfileTransportConfiguration.setDeviceAttributesTopic(MqttTopics.DEVICE_ATTRIBUTES_TOPIC); mqttDeviceProfileTransportConfiguration.setDeviceAttributesSubscribeTopic(MqttTopics.DEVICE_ATTRIBUTES_TOPIC); mqttDeviceProfileTransportConfiguration.setSendAckOnValidationException(sendAckOnValidationException); mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration); @@ -456,7 +455,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { String telemetryTopic, String attributesPublishTopic, String attributesSubscribeTopic) { MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration(); mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(telemetryTopic); - mqttDeviceProfileTransportConfiguration.setDeviceAttributesPublishTopic(attributesPublishTopic); + mqttDeviceProfileTransportConfiguration.setDeviceAttributesTopic(attributesPublishTopic); mqttDeviceProfileTransportConfiguration.setDeviceAttributesSubscribeTopic(attributesSubscribeTopic); mqttDeviceProfileTransportConfiguration.setSendAckOnValidationException(sendAckOnValidationException); mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java index 189ba4693f..64f2963f05 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java @@ -1068,7 +1068,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractControlle } @Test - public void testSaveDeviceProfileWorks() throws Exception { //todo + public void testSaveDeviceProfileWorks() throws Exception { JsonTransportPayloadConfiguration jsonTransportPayloadConfiguration = new JsonTransportPayloadConfiguration(); MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(jsonTransportPayloadConfiguration, true, diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java index ec64ce4af3..c1812c4728 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java @@ -17,11 +17,6 @@ package org.thingsboard.server.transport.mqtt; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; -import org.eclipse.paho.client.mqttv3.MqttAsyncClient; -import org.eclipse.paho.client.mqttv3.MqttConnectOptions; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; import org.springframework.test.context.TestPropertySource; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.Device; @@ -106,7 +101,7 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(config.getTelemetryTopicFilter()); } if (StringUtils.hasLength(config.getAttributesTopicFilter())) { - mqttDeviceProfileTransportConfiguration.setDeviceAttributesPublishTopic(config.getAttributesTopicFilter()); + mqttDeviceProfileTransportConfiguration.setDeviceAttributesTopic(config.getAttributesTopicFilter()); } mqttDeviceProfileTransportConfiguration.setSendAckOnValidationException(config.isSendAckOnValidationException()); TransportPayloadTypeConfiguration transportPayloadTypeConfiguration; 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 8c1493bce7..5bb97a8f23 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 @@ -25,9 +25,9 @@ public class MqttDeviceProfileTransportConfiguration implements DeviceProfileTra @NoXss private String deviceTelemetryTopic = MqttTopics.DEVICE_TELEMETRY_TOPIC; @NoXss - private String deviceAttributesPublishTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC; + private String deviceAttributesTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC; @NoXss - private String deviceAttributesSubscribeTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC;//todo + private String deviceAttributesSubscribeTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC; private TransportPayloadTypeConfiguration transportPayloadTypeConfiguration; private boolean sendAckOnValidationException; diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java index 4b69f75f06..f686b6d777 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java @@ -161,7 +161,7 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttConfig.getTransportPayloadTypeConfiguration(); payloadType = transportPayloadTypeConfiguration.getTransportPayloadType(); telemetryTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceTelemetryTopic()); - attributesPublishTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesPublishTopic()); + attributesPublishTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesTopic()); attributesSubscribeTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesSubscribeTopic()); sendAckOnValidationException = mqttConfig.isSendAckOnValidationException(); if (TransportPayloadType.PROTOBUF.equals(payloadType)) { From d805a07d41b3803c3dd0536456cd0cb380c36630 Mon Sep 17 00:00:00 2001 From: AndriiD Date: Wed, 3 Aug 2022 13:05:49 +0300 Subject: [PATCH 006/488] added new input --- ...profile-transport-configuration.component.html | 15 +++++++++++++++ ...e-profile-transport-configuration.component.ts | 1 + .../src/assets/locale/locale.constant-cs_CZ.json | 2 ++ .../src/assets/locale/locale.constant-en_US.json | 2 ++ .../src/assets/locale/locale.constant-es_ES.json | 2 ++ .../src/assets/locale/locale.constant-fr_FR.json | 2 ++ .../src/assets/locale/locale.constant-ko_KR.json | 2 ++ .../src/assets/locale/locale.constant-sl_SI.json | 2 ++ .../src/assets/locale/locale.constant-tr_TR.json | 2 ++ .../src/assets/locale/locale.constant-zh_CN.json | 2 ++ 10 files changed, 32 insertions(+) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html index 00ecd46582..2d0a91c10d 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html @@ -50,6 +50,21 @@ {{ 'device-profile.not-valid-multi-character' | translate}} + + device-profile.attributes-subscribe-topic-filter + + + {{ 'device-profile.attributes-subscribe-topic-filter-required' | translate}} + + + {{ 'device-profile.not-valid-single-character' | translate}} + + + {{ 'device-profile.not-valid-multi-character' | translate}} + + {{ 'device-profile.mqtt-device-topic-filters-unique' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts index b51b42f1d2..8ec2e633a4 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts @@ -91,6 +91,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control ngOnInit() { this.mqttDeviceProfileTransportConfigurationFormGroup = this.fb.group({ deviceAttributesTopic: [null, [Validators.required, this.validationMQTTTopic()]], + deviceAttributesSubscribeTopic: [null, [Validators.required, this.validationMQTTTopic()]], deviceTelemetryTopic: [null, [Validators.required, this.validationMQTTTopic()]], sendAckOnValidationException: [false, Validators.required], transportPayloadTypeConfiguration: this.fb.group({ diff --git a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json index a194bf3a7b..a2e148a4d1 100644 --- a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json +++ b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json @@ -1105,7 +1105,9 @@ "telemetry-topic-filter": "Filtr fronty telemetrie", "telemetry-topic-filter-required": "Filtr fronty telemetrie je povinný.", "attributes-topic-filter": "Filtr atributů fronty", + "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", "attributes-topic-filter-required": "Filtr atributů fronty je povinný.", + "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "telemetry-proto-schema": "Proto schéma telemetrie", "telemetry-proto-schema-required": "Proto schéma telemetrie je povinné.", "attributes-proto-schema": "Atributy proto schémata", diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index fd1bc2d688..a723347e7a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1299,7 +1299,9 @@ "telemetry-topic-filter": "Telemetry topic filter", "telemetry-topic-filter-required": "Telemetry topic filter is required.", "attributes-topic-filter": "Attributes topic filter", + "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", "attributes-topic-filter-required": "Attributes topic filter is required.", + "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "telemetry-proto-schema": "Telemetry proto schema", "telemetry-proto-schema-required": "Telemetry proto schema is required.", "attributes-proto-schema": "Attributes proto schema", diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index 68305d1352..fc4cc34d6c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -1299,7 +1299,9 @@ "telemetry-topic-filter": "Filtro de topic en telemetría", "telemetry-topic-filter-required": "Se requiere filtro de topic (telemetría).", "attributes-topic-filter": "Filtro de topic en atributos", + "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", "attributes-topic-filter-required": "Se requiere filtro de topic (atributos).", + "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "telemetry-proto-schema": "Proto esquema de telemetría", "telemetry-proto-schema-required": "Se requiere proto esquema de telemetría.", "attributes-proto-schema": "Proto esquema de atributos", diff --git a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json index ce66154f10..a7c7e9c132 100644 --- a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json +++ b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json @@ -1130,7 +1130,9 @@ "telemetry-topic-filter": "Filtre de sujets de télémétrie", "telemetry-topic-filter-required": "Filtre de sujets de télémétrie est requis.", "attributes-topic-filter": "Filtre de sujets d'attributs", + "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", "attributes-topic-filter-required": "Filtre de sujets d'attributs est requis.", + "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "telemetry-proto-schema": "Schéma proto de télémétrie", "telemetry-proto-schema-required": "Schéma proto de télémétrie est requis.", "attributes-proto-schema": "Schéma proto d'attributs", diff --git a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json index 1d05b49950..50a79fbb53 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json +++ b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json @@ -977,7 +977,9 @@ "telemetry-topic-filter": "Telemetry topic filter", "telemetry-topic-filter-required": "Telemetry topic filter is required.", "attributes-topic-filter": "Attributes topic filter", + "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", "attributes-topic-filter-required": "Attributes topic filter is required.", + "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "telemetry-proto-schema": "Telemetry proto schema", "telemetry-proto-schema-required": "Telemetry proto schema is required.", "attributes-proto-schema": "Attributes proto schema", diff --git a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json index 04d7bcd25f..c9ab61160c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json +++ b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json @@ -977,7 +977,9 @@ "telemetry-topic-filter": "Telemetry topic filter", "telemetry-topic-filter-required": "Telemetry topic filter is required.", "attributes-topic-filter": "Attributes topic filter", + "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", "attributes-topic-filter-required": "Attributes topic filter is required.", + "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "telemetry-proto-schema": "Telemetry proto schema", "telemetry-proto-schema-required": "Telemetry proto schema is required.", "attributes-proto-schema": "Attributes proto schema", diff --git a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json index 174c648e7a..fcf7c7c07c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json +++ b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json @@ -1108,7 +1108,9 @@ "telemetry-topic-filter": "Telemetri konu filtresi", "telemetry-topic-filter-required": "Telemetri konu filtresi gerekli.", "attributes-topic-filter": "Öznitelikler konu filtresi", + "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", "attributes-topic-filter-required": "Öznitelikler konu filtresi gerekli.", + "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "telemetry-proto-schema": "Telemetri proto şeması", "telemetry-proto-schema-required": "Telemetri proto şeması gerekli.", "attributes-proto-schema": "Öznitelikler proto şeması", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index 0a8f8d4c84..170098f76b 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -885,7 +885,9 @@ "attributes-proto-schema": "Attributes proto schema", "attributes-proto-schema-required": "Attributes proto schema 必填。", "attributes-topic-filter": "Attributes topic filter", + "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", "attributes-topic-filter-required": "Attributes topic 筛选器必填。", + "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "clear-alarm-rule": "清除报警规则", "coap-device-payload-type": "CoAP 设备消息 Payload", "coap-device-type": "CoAP 设备类型", From b21f2cb38bd075623f7850357b8224ecfce905c5 Mon Sep 17 00:00:00 2001 From: AndriiD Date: Thu, 11 Aug 2022 12:45:16 +0300 Subject: [PATCH 007/488] updated labels for topic names input fields --- ui-ngx/src/assets/locale/locale.constant-cs_CZ.json | 4 ++-- ui-ngx/src/assets/locale/locale.constant-en_US.json | 4 ++-- ui-ngx/src/assets/locale/locale.constant-es_ES.json | 4 ++-- ui-ngx/src/assets/locale/locale.constant-fr_FR.json | 4 ++-- ui-ngx/src/assets/locale/locale.constant-ko_KR.json | 4 ++-- ui-ngx/src/assets/locale/locale.constant-sl_SI.json | 4 ++-- ui-ngx/src/assets/locale/locale.constant-tr_TR.json | 4 ++-- ui-ngx/src/assets/locale/locale.constant-zh_CN.json | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json index a2e148a4d1..f8e0265b56 100644 --- a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json +++ b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json @@ -1104,9 +1104,9 @@ "support-level-wildcards": "Jsou podporovány jednoúrovňové [+] a víceúrovňové [#] zástupné znaky.", "telemetry-topic-filter": "Filtr fronty telemetrie", "telemetry-topic-filter-required": "Filtr fronty telemetrie je povinný.", - "attributes-topic-filter": "Filtr atributů fronty", + "attributes-topic-filter": "Attributes publish topic filter", "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", - "attributes-topic-filter-required": "Filtr atributů fronty je povinný.", + "attributes-topic-filter-required": "Attributes publish topic filter is required.", "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "telemetry-proto-schema": "Proto schéma telemetrie", "telemetry-proto-schema-required": "Proto schéma telemetrie je povinné.", diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index a723347e7a..3e696c1d7d 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1298,9 +1298,9 @@ "support-level-wildcards": "Single [+] and multi-level [#] wildcards supported.", "telemetry-topic-filter": "Telemetry topic filter", "telemetry-topic-filter-required": "Telemetry topic filter is required.", - "attributes-topic-filter": "Attributes topic filter", + "attributes-topic-filter": "Attributes publish topic filter", "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", - "attributes-topic-filter-required": "Attributes topic filter is required.", + "attributes-topic-filter-required": "Attributes publish topic filter is required.", "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "telemetry-proto-schema": "Telemetry proto schema", "telemetry-proto-schema-required": "Telemetry proto schema is required.", diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index fc4cc34d6c..07d2e84f16 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -1298,9 +1298,9 @@ "support-level-wildcards": "Se soportan los wilcards únicos [+] y multi-nivel [#].", "telemetry-topic-filter": "Filtro de topic en telemetría", "telemetry-topic-filter-required": "Se requiere filtro de topic (telemetría).", - "attributes-topic-filter": "Filtro de topic en atributos", + "attributes-topic-filter": "Attributes publish topic filter", "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", - "attributes-topic-filter-required": "Se requiere filtro de topic (atributos).", + "attributes-topic-filter-required": "Attributes publish topic filter is required.", "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "telemetry-proto-schema": "Proto esquema de telemetría", "telemetry-proto-schema-required": "Se requiere proto esquema de telemetría.", diff --git a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json index a7c7e9c132..11570609d4 100644 --- a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json +++ b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json @@ -1129,9 +1129,9 @@ "support-level-wildcards": "[+] unique et wildcards de [#] multi-niveaux supportés.", "telemetry-topic-filter": "Filtre de sujets de télémétrie", "telemetry-topic-filter-required": "Filtre de sujets de télémétrie est requis.", - "attributes-topic-filter": "Filtre de sujets d'attributs", + "attributes-topic-filter": "Attributes publish topic filter", "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", - "attributes-topic-filter-required": "Filtre de sujets d'attributs est requis.", + "attributes-topic-filter-required": "Attributes publish topic filter is required.", "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "telemetry-proto-schema": "Schéma proto de télémétrie", "telemetry-proto-schema-required": "Schéma proto de télémétrie est requis.", diff --git a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json index 50a79fbb53..734c3bbb1f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json +++ b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json @@ -976,9 +976,9 @@ "support-level-wildcards": "Single [+] and multi-level [#] wildcards supported.", "telemetry-topic-filter": "Telemetry topic filter", "telemetry-topic-filter-required": "Telemetry topic filter is required.", - "attributes-topic-filter": "Attributes topic filter", + "attributes-topic-filter": "Attributes publish topic filter", "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", - "attributes-topic-filter-required": "Attributes topic filter is required.", + "attributes-topic-filter-required": "Attributes publish topic filter is required.", "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "telemetry-proto-schema": "Telemetry proto schema", "telemetry-proto-schema-required": "Telemetry proto schema is required.", diff --git a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json index c9ab61160c..e199935337 100644 --- a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json +++ b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json @@ -976,9 +976,9 @@ "support-level-wildcards": "Single [+] and multi-level [#] wildcards supported.", "telemetry-topic-filter": "Telemetry topic filter", "telemetry-topic-filter-required": "Telemetry topic filter is required.", - "attributes-topic-filter": "Attributes topic filter", + "attributes-topic-filter": "Attributes publish topic filter", "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", - "attributes-topic-filter-required": "Attributes topic filter is required.", + "attributes-topic-filter-required": "Attributes publish topic filter is required.", "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "telemetry-proto-schema": "Telemetry proto schema", "telemetry-proto-schema-required": "Telemetry proto schema is required.", diff --git a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json index fcf7c7c07c..110e5583d8 100644 --- a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json +++ b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json @@ -1107,9 +1107,9 @@ "support-level-wildcards": "Tekli [+] ve çoklu [#] joker karakter destekler.", "telemetry-topic-filter": "Telemetri konu filtresi", "telemetry-topic-filter-required": "Telemetri konu filtresi gerekli.", - "attributes-topic-filter": "Öznitelikler konu filtresi", + "attributes-topic-filter": "Attributes publish topic filter", "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", - "attributes-topic-filter-required": "Öznitelikler konu filtresi gerekli.", + "attributes-topic-filter-required": "Attributes publish topic filter is required.", "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "telemetry-proto-schema": "Telemetri proto şeması", "telemetry-proto-schema-required": "Telemetri proto şeması gerekli.", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index 170098f76b..a7549ffc7e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -884,9 +884,9 @@ "all-device-profiles": "全部", "attributes-proto-schema": "Attributes proto schema", "attributes-proto-schema-required": "Attributes proto schema 必填。", - "attributes-topic-filter": "Attributes topic filter", + "attributes-topic-filter": "Attributes publish topic filter", "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", - "attributes-topic-filter-required": "Attributes topic 筛选器必填。", + "attributes-topic-filter-required": "Attributes publish topic filter is required.", "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", "clear-alarm-rule": "清除报警规则", "coap-device-payload-type": "CoAP 设备消息 Payload", From 6233574cf5eb9212606d8f46490b5c01fa73e487 Mon Sep 17 00:00:00 2001 From: AndriiD Date: Wed, 17 Aug 2022 11:19:38 +0300 Subject: [PATCH 008/488] removed redundant try-catches --- .../server/controller/AdminController.java | 158 ++++-------- .../server/controller/AlarmController.java | 46 +--- .../server/controller/AssetController.java | 217 +++++++--------- .../server/controller/AuditLogController.java | 56 ++-- .../server/controller/AuthController.java | 233 ++++++++--------- .../ComponentDescriptorController.java | 24 +- .../server/controller/CustomerController.java | 58 ++--- .../controller/DashboardController.java | 240 ++++++++---------- .../server/controller/DeviceController.java | 225 +++++++--------- .../controller/DeviceProfileController.java | 50 +--- .../server/controller/EdgeController.java | 231 +++++++---------- .../controller/EdgeEventController.java | 14 +- .../controller/EntityQueryController.java | 30 +-- .../controller/EntityRelationController.java | 83 ++---- .../controller/EntityViewController.java | 185 ++++++-------- .../server/controller/EventController.java | 10 +- .../server/controller/Lwm2mController.java | 6 +- .../OAuth2ConfigTemplateController.java | 26 +- .../server/controller/OAuth2Controller.java | 56 ++-- .../controller/OtaPackageController.java | 62 ++--- .../server/controller/RpcV2Controller.java | 100 ++++---- .../controller/RuleChainController.java | 223 +++++++--------- .../controller/TbResourceController.java | 66 ++--- .../controller/TelemetryController.java | 128 +++------- .../server/controller/TenantController.java | 38 +-- .../controller/TenantProfileController.java | 82 ++---- .../server/controller/UserController.java | 160 +++++------- .../controller/WidgetTypeController.java | 145 +++++------ .../controller/WidgetsBundleController.java | 38 +-- 29 files changed, 1115 insertions(+), 1875 deletions(-) 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 54cbdd9b0f..4c0954c660 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -81,16 +81,12 @@ public class AdminController extends BaseController { public AdminSettings getAdminSettings( @ApiParam(value = "A string value of the key (e.g. 'general' or 'mail').") @PathVariable("key") String key) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); - AdminSettings adminSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key), "No Administration settings found for key: " + key); - if (adminSettings.getKey().equals("mail")) { - ((ObjectNode) adminSettings.getJsonValue()).remove("password"); - } - return adminSettings; - } catch (Exception e) { - throw handleException(e); + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); + AdminSettings adminSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key), "No Administration settings found for key: " + key); + if (adminSettings.getKey().equals("mail")) { + ((ObjectNode) adminSettings.getJsonValue()).remove("password"); } + return adminSettings; } @@ -104,20 +100,16 @@ public class AdminController extends BaseController { public AdminSettings saveAdminSettings( @ApiParam(value = "A JSON value representing the Administration Settings.") @RequestBody AdminSettings adminSettings) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE); - adminSettings.setTenantId(getTenantId()); - adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings)); - if (adminSettings.getKey().equals("mail")) { - mailService.updateMailConfiguration(); - ((ObjectNode) adminSettings.getJsonValue()).remove("password"); - } else if (adminSettings.getKey().equals("sms")) { - smsService.updateSmsConfiguration(); - } - return adminSettings; - } catch (Exception e) { - throw handleException(e); + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE); + adminSettings.setTenantId(getTenantId()); + adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings)); + if (adminSettings.getKey().equals("mail")) { + mailService.updateMailConfiguration(); + ((ObjectNode) adminSettings.getJsonValue()).remove("password"); + } else if (adminSettings.getKey().equals("sms")) { + smsService.updateSmsConfiguration(); } + return adminSettings; } @ApiOperation(value = "Get the Security Settings object", @@ -126,12 +118,8 @@ public class AdminController extends BaseController { @RequestMapping(value = "/securitySettings", method = RequestMethod.GET) @ResponseBody public SecuritySettings getSecuritySettings() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); - return checkNotNull(systemSecurityService.getSecuritySettings(TenantId.SYS_TENANT_ID)); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); + return checkNotNull(systemSecurityService.getSecuritySettings(TenantId.SYS_TENANT_ID)); } @ApiOperation(value = "Update Security Settings (saveSecuritySettings)", @@ -142,13 +130,9 @@ public class AdminController extends BaseController { public SecuritySettings saveSecuritySettings( @ApiParam(value = "A JSON value representing the Security Settings.") @RequestBody SecuritySettings securitySettings) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE); - securitySettings = checkNotNull(systemSecurityService.saveSecuritySettings(TenantId.SYS_TENANT_ID, securitySettings)); - return securitySettings; - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE); + securitySettings = checkNotNull(systemSecurityService.saveSecuritySettings(TenantId.SYS_TENANT_ID, securitySettings)); + return securitySettings; } @ApiOperation(value = "Send test email (sendTestMail)", @@ -159,19 +143,15 @@ public class AdminController extends BaseController { public void sendTestMail( @ApiParam(value = "A JSON value representing the Mail Settings.") @RequestBody AdminSettings adminSettings) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); - adminSettings = checkNotNull(adminSettings); - if (adminSettings.getKey().equals("mail")) { - if (!adminSettings.getJsonValue().has("password")) { - AdminSettings mailSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail")); - ((ObjectNode) adminSettings.getJsonValue()).put("password", mailSettings.getJsonValue().get("password").asText()); - } - String email = getCurrentUser().getEmail(); - mailService.sendTestMail(adminSettings.getJsonValue(), email); + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); + adminSettings = checkNotNull(adminSettings); + if (adminSettings.getKey().equals("mail")) { + if (!adminSettings.getJsonValue().has("password")) { + AdminSettings mailSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail")); + ((ObjectNode) adminSettings.getJsonValue()).put("password", mailSettings.getJsonValue().get("password").asText()); } - } catch (Exception e) { - throw handleException(e); + String email = getCurrentUser().getEmail(); + mailService.sendTestMail(adminSettings.getJsonValue(), email); } } @@ -183,12 +163,8 @@ public class AdminController extends BaseController { public void sendTestSms( @ApiParam(value = "A JSON value representing the Test SMS request.") @RequestBody TestSmsRequest testSmsRequest) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); - smsService.sendTestSms(testSmsRequest); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); + smsService.sendTestSms(testSmsRequest); } @ApiOperation(value = "Get repository settings (getRepositorySettings)", @@ -197,16 +173,12 @@ public class AdminController extends BaseController { @GetMapping("/repositorySettings") @ResponseBody public RepositorySettings getRepositorySettings() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); - RepositorySettings versionControlSettings = checkNotNull(versionControlService.getVersionControlSettings(getTenantId())); - versionControlSettings.setPassword(null); - versionControlSettings.setPrivateKey(null); - versionControlSettings.setPrivateKeyPassword(null); - return versionControlSettings; - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); + RepositorySettings versionControlSettings = checkNotNull(versionControlService.getVersionControlSettings(getTenantId())); + versionControlSettings.setPassword(null); + versionControlSettings.setPrivateKey(null); + versionControlSettings.setPrivateKeyPassword(null); + return versionControlSettings; } @ApiOperation(value = "Check repository settings exists (repositorySettingsExists)", @@ -215,12 +187,8 @@ public class AdminController extends BaseController { @GetMapping("/repositorySettings/exists") @ResponseBody public Boolean repositorySettingsExists() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); - return versionControlService.getVersionControlSettings(getTenantId()) != null; - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); + return versionControlService.getVersionControlSettings(getTenantId()) != null; } @ApiOperation(value = "Creates or Updates the repository settings (saveRepositorySettings)", @@ -244,13 +212,9 @@ public class AdminController extends BaseController { @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/repositorySettings", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.OK) - public DeferredResult deleteRepositorySettings() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.DELETE); - return wrapFuture(versionControlService.deleteVersionControlSettings(getTenantId())); - } catch (Exception e) { - throw handleException(e); - } + public DeferredResult deleteRepositorySettings() throws Exception { + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.DELETE); + return wrapFuture(versionControlService.deleteVersionControlSettings(getTenantId())); } @@ -260,14 +224,10 @@ public class AdminController extends BaseController { @RequestMapping(value = "/repositorySettings/checkAccess", method = RequestMethod.POST) public DeferredResult checkRepositoryAccess( @ApiParam(value = "A JSON value representing the Repository Settings.") - @RequestBody RepositorySettings settings) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); - settings = checkNotNull(settings); - return wrapFuture(versionControlService.checkVersionControlAccess(getTenantId(), settings)); - } catch (Exception e) { - throw handleException(e); - } + @RequestBody RepositorySettings settings) throws Exception { + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); + settings = checkNotNull(settings); + return wrapFuture(versionControlService.checkVersionControlAccess(getTenantId(), settings)); } @ApiOperation(value = "Get auto commit settings (getAutoCommitSettings)", @@ -276,12 +236,8 @@ public class AdminController extends BaseController { @GetMapping("/autoCommitSettings") @ResponseBody public AutoCommitSettings getAutoCommitSettings() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); - return checkNotNull(autoCommitSettingsService.get(getTenantId())); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); + return checkNotNull(autoCommitSettingsService.get(getTenantId())); } @ApiOperation(value = "Check auto commit settings exists (autoCommitSettingsExists)", @@ -290,12 +246,8 @@ public class AdminController extends BaseController { @GetMapping("/autoCommitSettings/exists") @ResponseBody public Boolean autoCommitSettingsExists() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); - return autoCommitSettingsService.get(getTenantId()) != null; - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); + return autoCommitSettingsService.get(getTenantId()) != null; } @ApiOperation(value = "Creates or Updates the auto commit settings (saveAutoCommitSettings)", @@ -314,12 +266,8 @@ public class AdminController extends BaseController { @RequestMapping(value = "/autoCommitSettings", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.OK) public void deleteAutoCommitSettings() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.DELETE); - autoCommitSettingsService.delete(getTenantId()); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.DELETE); + autoCommitSettingsService.delete(getTenantId()); } @ApiOperation(value = "Check for new Platform Releases (checkUpdates)", @@ -329,11 +277,7 @@ public class AdminController extends BaseController { @RequestMapping(value = "/updates", method = RequestMethod.GET) @ResponseBody public UpdateMessage checkUpdates() throws ThingsboardException { - try { - return updateService.checkUpdates(); - } catch (Exception e) { - throw handleException(e); - } + return updateService.checkUpdates(); } } diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index d2e11086c7..82020be202 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -48,6 +48,8 @@ import org.thingsboard.server.service.entitiy.alarm.TbAlarmService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; +import java.util.concurrent.ExecutionException; + import static org.thingsboard.server.controller.ControllerConstants.ALARM_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.ALARM_INFO_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.ALARM_SORT_PROPERTY_ALLOWABLE_VALUES; @@ -93,12 +95,8 @@ public class AlarmController extends BaseController { public Alarm getAlarmById(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); - try { - AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); - return checkAlarmId(alarmId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); + return checkAlarmId(alarmId, Operation.READ); } @ApiOperation(value = "Get Alarm Info (getAlarmInfoById)", @@ -110,12 +108,8 @@ public class AlarmController extends BaseController { public AlarmInfo getAlarmInfoById(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); - try { - AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); - return checkAlarmInfoId(alarmId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); + return checkAlarmInfoId(alarmId, Operation.READ); } @ApiOperation(value = "Create or update Alarm (saveAlarm)", @@ -210,7 +204,7 @@ public class AlarmController extends BaseController { @RequestParam(required = false) Long endTime, @ApiParam(value = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION) @RequestParam(required = false) Boolean fetchOriginator - ) throws ThingsboardException { + ) throws ThingsboardException, ExecutionException, InterruptedException { checkParameter("EntityId", strEntityId); checkParameter("EntityType", strEntityType); EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); @@ -223,11 +217,7 @@ public class AlarmController extends BaseController { checkEntityId(entityId, Operation.READ); TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - try { - return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); } @ApiOperation(value = "Get All Alarms (getAllAlarms)", @@ -260,7 +250,7 @@ public class AlarmController extends BaseController { @RequestParam(required = false) Long endTime, @ApiParam(value = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION) @RequestParam(required = false) Boolean fetchOriginator - ) throws ThingsboardException { + ) throws ThingsboardException, ExecutionException, InterruptedException { AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus); AlarmStatus alarmStatus = StringUtils.isEmpty(status) ? null : AlarmStatus.valueOf(status); if (alarmSearchStatus != null && alarmStatus != null) { @@ -269,14 +259,10 @@ public class AlarmController extends BaseController { } TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - try { - if (getCurrentUser().isCustomerUser()) { - return checkNotNull(alarmService.findCustomerAlarms(getCurrentUser().getTenantId(), getCurrentUser().getCustomerId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); - } else { - return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); - } - } catch (Exception e) { - throw handleException(e); + if (getCurrentUser().isCustomerUser()) { + return checkNotNull(alarmService.findCustomerAlarms(getCurrentUser().getTenantId(), getCurrentUser().getCustomerId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); + } else { + return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); } } @@ -307,11 +293,7 @@ public class AlarmController extends BaseController { "and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } checkEntityId(entityId, Operation.READ); - try { - return alarmService.findHighestAlarmSeverity(getCurrentUser().getTenantId(), entityId, alarmSearchStatus, alarmStatus); - } catch (Exception e) { - throw handleException(e); - } + return alarmService.findHighestAlarmSeverity(getCurrentUser().getTenantId(), entityId, alarmSearchStatus, alarmStatus); } } 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 75e77d4719..3b0e8d6858 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -60,6 +60,7 @@ import org.thingsboard.server.service.security.permission.Resource; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.thingsboard.server.controller.ControllerConstants.ASSET_ID_PARAM_DESCRIPTION; @@ -108,12 +109,8 @@ public class AssetController extends BaseController { public Asset getAssetById(@ApiParam(value = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException { checkParameter(ASSET_ID, strAssetId); - try { - AssetId assetId = new AssetId(toUUID(strAssetId)); - return checkAssetId(assetId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + AssetId assetId = new AssetId(toUUID(strAssetId)); + return checkAssetId(assetId, Operation.READ); } @ApiOperation(value = "Get Asset Info (getAssetInfoById)", @@ -127,12 +124,8 @@ public class AssetController extends BaseController { public AssetInfo getAssetInfoById(@ApiParam(value = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException { checkParameter(ASSET_ID, strAssetId); - try { - AssetId assetId = new AssetId(toUUID(strAssetId)); - return checkAssetInfoId(assetId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + AssetId assetId = new AssetId(toUUID(strAssetId)); + return checkAssetInfoId(assetId, Operation.READ); } @ApiOperation(value = "Create Or Update Asset (saveAsset)", @@ -231,16 +224,12 @@ public class AssetController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(assetService.findAssetsByTenantIdAndType(tenantId, type, pageLink)); - } else { - return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(assetService.findAssetsByTenantIdAndType(tenantId, type, pageLink)); + } else { + return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink)); } } @@ -263,16 +252,12 @@ public class AssetController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(assetService.findAssetInfosByTenantIdAndType(tenantId, type, pageLink)); - } else { - return checkNotNull(assetService.findAssetInfosByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(assetService.findAssetInfosByTenantIdAndType(tenantId, type, pageLink)); + } else { + return checkNotNull(assetService.findAssetInfosByTenantId(tenantId, pageLink)); } } @@ -285,12 +270,8 @@ public class AssetController extends BaseController { public Asset getTenantAsset( @ApiParam(value = ASSET_NAME_DESCRIPTION) @RequestParam String assetName) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(assetService.findAssetByTenantIdAndName(tenantId, assetName)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(assetService.findAssetByTenantIdAndName(tenantId, assetName)); } @ApiOperation(value = "Get Customer Assets (getCustomerAssets)", @@ -315,18 +296,14 @@ public class AssetController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); - } else { - return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); + } else { + return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); } } @@ -352,18 +329,14 @@ public class AssetController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(assetService.findAssetInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); - } else { - return checkNotNull(assetService.findAssetInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(assetService.findAssetInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); + } else { + return checkNotNull(assetService.findAssetInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink)); } } @@ -374,26 +347,22 @@ public class AssetController extends BaseController { @ResponseBody public List getAssetsByIds( @ApiParam(value = "A list of assets ids, separated by comma ','") - @RequestParam("assetIds") String[] strAssetIds) throws ThingsboardException { + @RequestParam("assetIds") String[] strAssetIds) throws ThingsboardException, ExecutionException, InterruptedException { checkArrayParameter("assetIds", strAssetIds); - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - CustomerId customerId = user.getCustomerId(); - List assetIds = new ArrayList<>(); - for (String strAssetId : strAssetIds) { - assetIds.add(new AssetId(toUUID(strAssetId))); - } - ListenableFuture> assets; - if (customerId == null || customerId.isNullUid()) { - assets = assetService.findAssetsByTenantIdAndIdsAsync(tenantId, assetIds); - } else { - assets = assetService.findAssetsByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, assetIds); - } - return checkNotNull(assets.get()); - } catch (Exception e) { - throw handleException(e); + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + CustomerId customerId = user.getCustomerId(); + List assetIds = new ArrayList<>(); + for (String strAssetId : strAssetIds) { + assetIds.add(new AssetId(toUUID(strAssetId))); } + ListenableFuture> assets; + if (customerId == null || customerId.isNullUid()) { + assets = assetService.findAssetsByTenantIdAndIdsAsync(tenantId, assetIds); + } else { + assets = assetService.findAssetsByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, assetIds); + } + return checkNotNull(assets.get()); } @ApiOperation(value = "Find related assets (findByQuery)", @@ -403,25 +372,21 @@ public class AssetController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/assets", method = RequestMethod.POST) @ResponseBody - public List findByQuery(@RequestBody AssetSearchQuery query) throws ThingsboardException { + public List findByQuery(@RequestBody AssetSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException { checkNotNull(query); checkNotNull(query.getParameters()); checkNotNull(query.getAssetTypes()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); - try { - List assets = checkNotNull(assetService.findAssetsByQuery(getTenantId(), query).get()); - assets = assets.stream().filter(asset -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, Operation.READ, asset.getId(), asset); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - return assets; - } catch (Exception e) { - throw handleException(e); - } + List assets = checkNotNull(assetService.findAssetsByQuery(getTenantId(), query).get()); + assets = assets.stream().filter(asset -> { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, Operation.READ, asset.getId(), asset); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + return assets; } @ApiOperation(value = "Get Asset Types (getAssetTypes)", @@ -429,15 +394,11 @@ public class AssetController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/asset/types", method = RequestMethod.GET) @ResponseBody - public List getAssetTypes() throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - ListenableFuture> assetTypes = assetService.findAssetTypesByTenantId(tenantId); - return checkNotNull(assetTypes.get()); - } catch (Exception e) { - throw handleException(e); - } + public List getAssetTypes() throws ThingsboardException, ExecutionException, InterruptedException { + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + ListenableFuture> assetTypes = assetService.findAssetTypesByTenantId(tenantId); + return checkNotNull(assetTypes.get()); } @ApiOperation(value = "Assign asset to edge (assignAssetToEdge)", @@ -513,33 +474,29 @@ public class AssetController extends BaseController { @ApiParam(value = "Timestamp. Assets with creation time after it won't be queried") @RequestParam(required = false) Long endTime) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - checkEdgeId(edgeId, Operation.READ); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - PageData nonFilteredResult; - if (type != null && type.trim().length() > 0) { - nonFilteredResult = assetService.findAssetsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink); - } else { - nonFilteredResult = assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); - } - List filteredAssets = nonFilteredResult.getData().stream().filter(asset -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, Operation.READ, asset.getId(), asset); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - PageData filteredResult = new PageData<>(filteredAssets, - nonFilteredResult.getTotalPages(), - nonFilteredResult.getTotalElements(), - nonFilteredResult.hasNext()); - return checkNotNull(filteredResult); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + checkEdgeId(edgeId, Operation.READ); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + PageData nonFilteredResult; + if (type != null && type.trim().length() > 0) { + nonFilteredResult = assetService.findAssetsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink); + } else { + nonFilteredResult = assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); } + List filteredAssets = nonFilteredResult.getData().stream().filter(asset -> { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, Operation.READ, asset.getId(), asset); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + PageData filteredResult = new PageData<>(filteredAssets, + nonFilteredResult.getTotalPages(), + nonFilteredResult.getTotalElements(), + nonFilteredResult.hasNext()); + return checkNotNull(filteredResult); } @ApiOperation(value = "Import the bulk of assets (processAssetsBulkImport)", 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 db78b87f70..385271fc94 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java @@ -97,15 +97,11 @@ public class AuditLogController extends BaseController { @RequestParam(required = false) Long endTime, @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION) @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException { - try { - checkParameter("CustomerId", strCustomerId); - TenantId tenantId = getCurrentUser().getTenantId(); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - List actionTypes = parseActionTypesStr(actionTypesStr); - return checkNotNull(auditLogService.findAuditLogsByTenantIdAndCustomerId(tenantId, new CustomerId(UUID.fromString(strCustomerId)), actionTypes, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + checkParameter("CustomerId", strCustomerId); + TenantId tenantId = getCurrentUser().getTenantId(); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + List actionTypes = parseActionTypesStr(actionTypesStr); + return checkNotNull(auditLogService.findAuditLogsByTenantIdAndCustomerId(tenantId, new CustomerId(UUID.fromString(strCustomerId)), actionTypes, pageLink)); } @ApiOperation(value = "Get audit logs by user id (getAuditLogsByUserId)", @@ -135,15 +131,11 @@ public class AuditLogController extends BaseController { @RequestParam(required = false) Long endTime, @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION) @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException { - try { - checkParameter("UserId", strUserId); - TenantId tenantId = getCurrentUser().getTenantId(); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - List actionTypes = parseActionTypesStr(actionTypesStr); - return checkNotNull(auditLogService.findAuditLogsByTenantIdAndUserId(tenantId, new UserId(UUID.fromString(strUserId)), actionTypes, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + checkParameter("UserId", strUserId); + TenantId tenantId = getCurrentUser().getTenantId(); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + List actionTypes = parseActionTypesStr(actionTypesStr); + return checkNotNull(auditLogService.findAuditLogsByTenantIdAndUserId(tenantId, new UserId(UUID.fromString(strUserId)), actionTypes, pageLink)); } @ApiOperation(value = "Get audit logs by entity id (getAuditLogsByEntityId)", @@ -176,16 +168,12 @@ public class AuditLogController extends BaseController { @RequestParam(required = false) Long endTime, @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION) @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException { - try { - checkParameter("EntityId", strEntityId); - checkParameter("EntityType", strEntityType); - TenantId tenantId = getCurrentUser().getTenantId(); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - List actionTypes = parseActionTypesStr(actionTypesStr); - return checkNotNull(auditLogService.findAuditLogsByTenantIdAndEntityId(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), actionTypes, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + checkParameter("EntityId", strEntityId); + checkParameter("EntityType", strEntityType); + TenantId tenantId = getCurrentUser().getTenantId(); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + List actionTypes = parseActionTypesStr(actionTypesStr); + return checkNotNull(auditLogService.findAuditLogsByTenantIdAndEntityId(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), actionTypes, pageLink)); } @ApiOperation(value = "Get all audit logs (getAuditLogs)", @@ -212,14 +200,10 @@ public class AuditLogController extends BaseController { @RequestParam(required = false) Long endTime, @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION) @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - List actionTypes = parseActionTypesStr(actionTypesStr); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - return checkNotNull(auditLogService.findAuditLogsByTenantId(tenantId, actionTypes, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + List actionTypes = parseActionTypesStr(actionTypesStr); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + return checkNotNull(auditLogService.findAuditLogsByTenantId(tenantId, actionTypes, pageLink)); } private List parseActionTypesStr(String actionTypesStr) { 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 72be55a135..c0e092a35d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java @@ -86,12 +86,8 @@ public class AuthController extends BaseController { @RequestMapping(value = "/auth/user", method = RequestMethod.GET) public @ResponseBody User getUser() throws ThingsboardException { - try { - SecurityUser securityUser = getCurrentUser(); - return userService.findUserById(securityUser.getTenantId(), securityUser.getId()); - } catch (Exception e) { - throw handleException(e); - } + SecurityUser securityUser = getCurrentUser(); + return userService.findUserById(securityUser.getTenantId(), securityUser.getId()); } @ApiOperation(value = "Logout (logout)", @@ -111,31 +107,27 @@ public class AuthController extends BaseController { public ObjectNode changePassword( @ApiParam(value = "Change Password Request") @RequestBody ChangePasswordRequest changePasswordRequest) throws ThingsboardException { - try { - String currentPassword = changePasswordRequest.getCurrentPassword(); - String newPassword = changePasswordRequest.getNewPassword(); - SecurityUser securityUser = getCurrentUser(); - UserCredentials userCredentials = userService.findUserCredentialsByUserId(TenantId.SYS_TENANT_ID, securityUser.getId()); - if (!passwordEncoder.matches(currentPassword, userCredentials.getPassword())) { - throw new ThingsboardException("Current password doesn't match!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - systemSecurityService.validatePassword(securityUser.getTenantId(), newPassword, userCredentials); - if (passwordEncoder.matches(newPassword, userCredentials.getPassword())) { - throw new ThingsboardException("New password should be different from existing!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - userCredentials.setPassword(passwordEncoder.encode(newPassword)); - userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials); + String currentPassword = changePasswordRequest.getCurrentPassword(); + String newPassword = changePasswordRequest.getNewPassword(); + SecurityUser securityUser = getCurrentUser(); + UserCredentials userCredentials = userService.findUserCredentialsByUserId(TenantId.SYS_TENANT_ID, securityUser.getId()); + if (!passwordEncoder.matches(currentPassword, userCredentials.getPassword())) { + throw new ThingsboardException("Current password doesn't match!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } + systemSecurityService.validatePassword(securityUser.getTenantId(), newPassword, userCredentials); + if (passwordEncoder.matches(newPassword, userCredentials.getPassword())) { + throw new ThingsboardException("New password should be different from existing!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } + userCredentials.setPassword(passwordEncoder.encode(newPassword)); + userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials); - sendEntityNotificationMsg(getTenantId(), userCredentials.getUserId(), EdgeEventActionType.CREDENTIALS_UPDATED); + sendEntityNotificationMsg(getTenantId(), userCredentials.getUserId(), EdgeEventActionType.CREDENTIALS_UPDATED); - eventPublisher.publishEvent(new UserAuthDataChangedEvent(securityUser.getId())); - ObjectNode response = JacksonUtil.newObjectNode(); - response.put("token", tokenFactory.createAccessJwtToken(securityUser).getToken()); - response.put("refreshToken", tokenFactory.createRefreshToken(securityUser).getToken()); - return response; - } catch (Exception e) { - throw handleException(e); - } + eventPublisher.publishEvent(new UserAuthDataChangedEvent(securityUser.getId())); + ObjectNode response = JacksonUtil.newObjectNode(); + response.put("token", tokenFactory.createAccessJwtToken(securityUser).getToken()); + response.put("refreshToken", tokenFactory.createRefreshToken(securityUser).getToken()); + return response; } @ApiOperation(value = "Get the current User password policy (getUserPasswordPolicy)", @@ -143,13 +135,9 @@ public class AuthController extends BaseController { @RequestMapping(value = "/noauth/userPasswordPolicy", method = RequestMethod.GET) @ResponseBody public UserPasswordPolicy getUserPasswordPolicy() throws ThingsboardException { - try { - SecuritySettings securitySettings = - checkNotNull(systemSecurityService.getSecuritySettings(TenantId.SYS_TENANT_ID)); - return securitySettings.getPasswordPolicy(); - } catch (Exception e) { - throw handleException(e); - } + SecuritySettings securitySettings = + checkNotNull(systemSecurityService.getSecuritySettings(TenantId.SYS_TENANT_ID)); + return securitySettings.getPasswordPolicy(); } @ApiOperation(value = "Check Activate User Token (checkActivateToken)", @@ -244,37 +232,33 @@ public class AuthController extends BaseController { @RequestBody ActivateUserRequest activateRequest, @RequestParam(required = false, defaultValue = "true") boolean sendActivationMail, HttpServletRequest request) throws ThingsboardException { - try { - String activateToken = activateRequest.getActivateToken(); - String password = activateRequest.getPassword(); - systemSecurityService.validatePassword(TenantId.SYS_TENANT_ID, password, null); - String encodedPassword = passwordEncoder.encode(password); - UserCredentials credentials = userService.activateUserCredentials(TenantId.SYS_TENANT_ID, activateToken, encodedPassword); - User user = userService.findUserById(TenantId.SYS_TENANT_ID, credentials.getUserId()); - UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); - SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); - userService.setUserCredentialsEnabled(user.getTenantId(), user.getId(), true); - String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request); - String loginUrl = String.format("%s/login", baseUrl); - String email = user.getEmail(); + String activateToken = activateRequest.getActivateToken(); + String password = activateRequest.getPassword(); + systemSecurityService.validatePassword(TenantId.SYS_TENANT_ID, password, null); + String encodedPassword = passwordEncoder.encode(password); + UserCredentials credentials = userService.activateUserCredentials(TenantId.SYS_TENANT_ID, activateToken, encodedPassword); + User user = userService.findUserById(TenantId.SYS_TENANT_ID, credentials.getUserId()); + UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); + SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); + userService.setUserCredentialsEnabled(user.getTenantId(), user.getId(), true); + String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request); + String loginUrl = String.format("%s/login", baseUrl); + String email = user.getEmail(); - if (sendActivationMail) { - try { - mailService.sendAccountActivatedEmail(loginUrl, email); - } catch (Exception e) { - log.info("Unable to send account activation email [{}]", e.getMessage()); - } + if (sendActivationMail) { + try { + mailService.sendAccountActivatedEmail(loginUrl, email); + } catch (Exception e) { + log.info("Unable to send account activation email [{}]", e.getMessage()); } + } - sendEntityNotificationMsg(user.getTenantId(), user.getId(), EdgeEventActionType.CREDENTIALS_UPDATED); + sendEntityNotificationMsg(user.getTenantId(), user.getId(), EdgeEventActionType.CREDENTIALS_UPDATED); - JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); - JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); + JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); + JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); - return new JwtTokenPair(accessToken.getToken(), refreshToken.getToken()); - } catch (Exception e) { - throw handleException(e); - } + return new JwtTokenPair(accessToken.getToken(), refreshToken.getToken()); } @ApiOperation(value = "Reset password (resetPassword)", @@ -288,87 +272,78 @@ public class AuthController extends BaseController { @ApiParam(value = "Reset password request.") @RequestBody ResetPasswordRequest resetPasswordRequest, HttpServletRequest request) throws ThingsboardException { - try { - String resetToken = resetPasswordRequest.getResetToken(); - String password = resetPasswordRequest.getPassword(); - UserCredentials userCredentials = userService.findUserCredentialsByResetToken(TenantId.SYS_TENANT_ID, resetToken); - if (userCredentials != null) { - systemSecurityService.validatePassword(TenantId.SYS_TENANT_ID, password, userCredentials); - if (passwordEncoder.matches(password, userCredentials.getPassword())) { - throw new ThingsboardException("New password should be different from existing!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - String encodedPassword = passwordEncoder.encode(password); - userCredentials.setPassword(encodedPassword); - userCredentials.setResetToken(null); - userCredentials = userService.replaceUserCredentials(TenantId.SYS_TENANT_ID, userCredentials); - User user = userService.findUserById(TenantId.SYS_TENANT_ID, userCredentials.getUserId()); - UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); - SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled(), principal); - String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request); - String loginUrl = String.format("%s/login", baseUrl); - String email = user.getEmail(); - mailService.sendPasswordWasResetEmail(loginUrl, email); + String resetToken = resetPasswordRequest.getResetToken(); + String password = resetPasswordRequest.getPassword(); + UserCredentials userCredentials = userService.findUserCredentialsByResetToken(TenantId.SYS_TENANT_ID, resetToken); + if (userCredentials != null) { + systemSecurityService.validatePassword(TenantId.SYS_TENANT_ID, password, userCredentials); + if (passwordEncoder.matches(password, userCredentials.getPassword())) { + throw new ThingsboardException("New password should be different from existing!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } + String encodedPassword = passwordEncoder.encode(password); + userCredentials.setPassword(encodedPassword); + userCredentials.setResetToken(null); + userCredentials = userService.replaceUserCredentials(TenantId.SYS_TENANT_ID, userCredentials); + User user = userService.findUserById(TenantId.SYS_TENANT_ID, userCredentials.getUserId()); + UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); + SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled(), principal); + String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request); + String loginUrl = String.format("%s/login", baseUrl); + String email = user.getEmail(); + mailService.sendPasswordWasResetEmail(loginUrl, email); - eventPublisher.publishEvent(new UserAuthDataChangedEvent(securityUser.getId())); - JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); - JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); + eventPublisher.publishEvent(new UserAuthDataChangedEvent(securityUser.getId())); + JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); + JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); - return new JwtTokenPair(accessToken.getToken(), refreshToken.getToken()); - } else { - throw new ThingsboardException("Invalid reset token!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - } catch (Exception e) { - throw handleException(e); + return new JwtTokenPair(accessToken.getToken(), refreshToken.getToken()); + } else { + throw new ThingsboardException("Invalid reset token!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } } private void logLogoutAction(HttpServletRequest request) throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); - RestAuthenticationDetails details = new RestAuthenticationDetails(request); - String clientAddress = details.getClientAddress(); - String browser = "Unknown"; - String os = "Unknown"; - String device = "Unknown"; - if (details.getUserAgent() != null) { - Client userAgent = details.getUserAgent(); - if (userAgent.userAgent != null) { - browser = userAgent.userAgent.family; - if (userAgent.userAgent.major != null) { - browser += " " + userAgent.userAgent.major; - if (userAgent.userAgent.minor != null) { - browser += "." + userAgent.userAgent.minor; - if (userAgent.userAgent.patch != null) { - browser += "." + userAgent.userAgent.patch; - } + SecurityUser user = getCurrentUser(); + RestAuthenticationDetails details = new RestAuthenticationDetails(request); + String clientAddress = details.getClientAddress(); + String browser = "Unknown"; + String os = "Unknown"; + String device = "Unknown"; + if (details.getUserAgent() != null) { + Client userAgent = details.getUserAgent(); + if (userAgent.userAgent != null) { + browser = userAgent.userAgent.family; + if (userAgent.userAgent.major != null) { + browser += " " + userAgent.userAgent.major; + if (userAgent.userAgent.minor != null) { + browser += "." + userAgent.userAgent.minor; + if (userAgent.userAgent.patch != null) { + browser += "." + userAgent.userAgent.patch; } } } - if (userAgent.os != null) { - os = userAgent.os.family; - if (userAgent.os.major != null) { - os += " " + userAgent.os.major; - if (userAgent.os.minor != null) { - os += "." + userAgent.os.minor; - if (userAgent.os.patch != null) { - os += "." + userAgent.os.patch; - if (userAgent.os.patchMinor != null) { - os += "." + userAgent.os.patchMinor; - } + } + if (userAgent.os != null) { + os = userAgent.os.family; + if (userAgent.os.major != null) { + os += " " + userAgent.os.major; + if (userAgent.os.minor != null) { + os += "." + userAgent.os.minor; + if (userAgent.os.patch != null) { + os += "." + userAgent.os.patch; + if (userAgent.os.patchMinor != null) { + os += "." + userAgent.os.patchMinor; } } } } - if (userAgent.device != null) { - device = userAgent.device.family; - } } - auditLogService.logEntityAction( - user.getTenantId(), user.getCustomerId(), user.getId(), - user.getName(), user.getId(), null, ActionType.LOGOUT, null, clientAddress, browser, os, device); - - } catch (Exception e) { - throw handleException(e); + if (userAgent.device != null) { + device = userAgent.device.family; + } } + auditLogService.logEntityAction( + user.getTenantId(), user.getCustomerId(), user.getId(), + user.getName(), user.getId(), null, ActionType.LOGOUT, null, clientAddress, browser, os, device); } } 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 b8e5da25de..af0f7eea14 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java @@ -57,11 +57,7 @@ public class ComponentDescriptorController extends BaseController { @ApiParam(value = "Component Descriptor class name", required = true) @PathVariable("componentDescriptorClazz") String strComponentDescriptorClazz) throws ThingsboardException { checkParameter("strComponentDescriptorClazz", strComponentDescriptorClazz); - try { - return checkComponentDescriptorByClazz(strComponentDescriptorClazz); - } catch (Exception e) { - throw handleException(e); - } + return checkComponentDescriptorByClazz(strComponentDescriptorClazz); } @ApiOperation(value = "Get Component Descriptors (getComponentDescriptorsByType)", @@ -76,11 +72,7 @@ public class ComponentDescriptorController extends BaseController { @ApiParam(value = "Type of the Rule Chain", allowableValues = "CORE,EDGE") @RequestParam(value = "ruleChainType", required = false) String strRuleChainType) throws ThingsboardException { checkParameter("componentType", strComponentType); - try { - return checkComponentDescriptorsByType(ComponentType.valueOf(strComponentType), getRuleChainType(strRuleChainType)); - } catch (Exception e) { - throw handleException(e); - } + return checkComponentDescriptorsByType(ComponentType.valueOf(strComponentType), getRuleChainType(strRuleChainType)); } @ApiOperation(value = "Get Component Descriptors (getComponentDescriptorsByTypes)", @@ -95,15 +87,11 @@ public class ComponentDescriptorController extends BaseController { @ApiParam(value = "Type of the Rule Chain", allowableValues = "CORE,EDGE") @RequestParam(value = "ruleChainType", required = false) String strRuleChainType) throws ThingsboardException { checkArrayParameter("componentTypes", strComponentTypes); - try { - Set componentTypes = new HashSet<>(); - for (String strComponentType : strComponentTypes) { - componentTypes.add(ComponentType.valueOf(strComponentType)); - } - return checkComponentDescriptorsByTypes(componentTypes, getRuleChainType(strRuleChainType)); - } catch (Exception e) { - throw handleException(e); + Set componentTypes = new HashSet<>(); + for (String strComponentType : strComponentTypes) { + componentTypes.add(ComponentType.valueOf(strComponentType)); } + return checkComponentDescriptorsByTypes(componentTypes, getRuleChainType(strRuleChainType)); } private RuleChainType getRuleChainType(String strRuleChainType) { 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 591eaa1318..856940b105 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -79,16 +79,12 @@ public class CustomerController extends BaseController { @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION) @PathVariable(CUSTOMER_ID) String strCustomerId) throws ThingsboardException { checkParameter(CUSTOMER_ID, strCustomerId); - try { - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - Customer customer = checkCustomerId(customerId, Operation.READ); - if (!customer.getAdditionalInfo().isNull()) { - processDashboardIdFromAdditionalInfo((ObjectNode) customer.getAdditionalInfo(), HOME_DASHBOARD); - } - return customer; - } catch (Exception e) { - throw handleException(e); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + Customer customer = checkCustomerId(customerId, Operation.READ); + if (!customer.getAdditionalInfo().isNull()) { + processDashboardIdFromAdditionalInfo((ObjectNode) customer.getAdditionalInfo(), HOME_DASHBOARD); } + return customer; } @@ -102,17 +98,13 @@ public class CustomerController extends BaseController { @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION) @PathVariable(CUSTOMER_ID) String strCustomerId) throws ThingsboardException { checkParameter(CUSTOMER_ID, strCustomerId); - try { - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - Customer customer = checkCustomerId(customerId, Operation.READ); - ObjectMapper objectMapper = new ObjectMapper(); - ObjectNode infoObject = objectMapper.createObjectNode(); - infoObject.put("title", customer.getTitle()); - infoObject.put(IS_PUBLIC, customer.isPublic()); - return infoObject; - } catch (Exception e) { - throw handleException(e); - } + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + Customer customer = checkCustomerId(customerId, Operation.READ); + ObjectMapper objectMapper = new ObjectMapper(); + ObjectNode infoObject = objectMapper.createObjectNode(); + infoObject.put("title", customer.getTitle()); + infoObject.put(IS_PUBLIC, customer.isPublic()); + return infoObject; } @ApiOperation(value = "Get Customer Title (getCustomerTitleById)", @@ -125,13 +117,9 @@ public class CustomerController extends BaseController { @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION) @PathVariable(CUSTOMER_ID) String strCustomerId) throws ThingsboardException { checkParameter(CUSTOMER_ID, strCustomerId); - try { - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - Customer customer = checkCustomerId(customerId, Operation.READ); - return customer.getTitle(); - } catch (Exception e) { - throw handleException(e); - } + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + Customer customer = checkCustomerId(customerId, Operation.READ); + return customer.getTitle(); } @ApiOperation(value = "Create or update Customer (saveCustomer)", @@ -182,13 +170,9 @@ public class CustomerController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(customerService.findCustomersByTenantId(tenantId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(customerService.findCustomersByTenantId(tenantId, pageLink)); } @ApiOperation(value = "Get Tenant Customer by Customer title (getTenantCustomer)", @@ -199,11 +183,7 @@ public class CustomerController extends BaseController { public Customer getTenantCustomer( @ApiParam(value = "A string value representing the Customer title.") @RequestParam String customerTitle) throws ThingsboardException { - try { TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(customerService.findCustomerByTenantIdAndTitle(tenantId, customerTitle), "Customer with title [" + customerTitle + "] is not found"); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(customerService.findCustomerByTenantIdAndTitle(tenantId, customerTitle), "Customer with title [" + customerTitle + "] is not found"); } } 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 9ceeb0d14c..4588cf96fd 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -140,12 +140,8 @@ public class DashboardController extends BaseController { @ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION) @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { checkParameter(DASHBOARD_ID, strDashboardId); - try { - DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); - return checkDashboardInfoId(dashboardId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); + return checkDashboardInfoId(dashboardId, Operation.READ); } @ApiOperation(value = "Get Dashboard (getDashboardById)", @@ -159,12 +155,8 @@ public class DashboardController extends BaseController { @ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION) @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { checkParameter(DASHBOARD_ID, strDashboardId); - try { - DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); - return checkDashboardId(dashboardId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); + return checkDashboardId(dashboardId, Operation.READ); } @ApiOperation(value = "Create Or Update Dashboard (saveDashboard)", @@ -362,14 +354,10 @@ public class DashboardController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); - checkTenantId(tenantId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); + checkTenantId(tenantId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink)); } @ApiOperation(value = "Get Tenant Dashboards (getTenantDashboards)", @@ -392,16 +380,12 @@ public class DashboardController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (mobile != null && mobile) { - return checkNotNull(dashboardService.findMobileDashboardsByTenantId(tenantId, pageLink)); - } else { - return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (mobile != null && mobile) { + return checkNotNull(dashboardService.findMobileDashboardsByTenantId(tenantId, pageLink)); + } else { + return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink)); } } @@ -428,18 +412,14 @@ public class DashboardController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter(CUSTOMER_ID, strCustomerId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (mobile != null && mobile) { - return checkNotNull(dashboardService.findMobileDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } else { - return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (mobile != null && mobile) { + return checkNotNull(dashboardService.findMobileDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); + } else { + return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); } } @@ -453,31 +433,27 @@ public class DashboardController extends BaseController { @RequestMapping(value = "/dashboard/home", method = RequestMethod.GET) @ResponseBody public HomeDashboard getHomeDashboard() throws ThingsboardException { - try { - SecurityUser securityUser = getCurrentUser(); - if (securityUser.isSystemAdmin()) { - return null; + SecurityUser securityUser = getCurrentUser(); + if (securityUser.isSystemAdmin()) { + return null; + } + User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId()); + JsonNode additionalInfo = user.getAdditionalInfo(); + HomeDashboard homeDashboard; + homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); + if (homeDashboard == null) { + if (securityUser.isCustomerUser()) { + Customer customer = customerService.findCustomerById(securityUser.getTenantId(), securityUser.getCustomerId()); + additionalInfo = customer.getAdditionalInfo(); + homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); } - User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId()); - JsonNode additionalInfo = user.getAdditionalInfo(); - HomeDashboard homeDashboard; - homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); if (homeDashboard == null) { - if (securityUser.isCustomerUser()) { - Customer customer = customerService.findCustomerById(securityUser.getTenantId(), securityUser.getCustomerId()); - additionalInfo = customer.getAdditionalInfo(); - homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); - } - if (homeDashboard == null) { - Tenant tenant = tenantService.findTenantById(securityUser.getTenantId()); - additionalInfo = tenant.getAdditionalInfo(); - homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); - } + Tenant tenant = tenantService.findTenantById(securityUser.getTenantId()); + additionalInfo = tenant.getAdditionalInfo(); + homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); } - return homeDashboard; - } catch (Exception e) { - throw handleException(e); } + return homeDashboard; } @ApiOperation(value = "Get Home Dashboard Info (getHomeDashboardInfo)", @@ -490,31 +466,27 @@ public class DashboardController extends BaseController { @RequestMapping(value = "/dashboard/home/info", method = RequestMethod.GET) @ResponseBody public HomeDashboardInfo getHomeDashboardInfo() throws ThingsboardException { - try { - SecurityUser securityUser = getCurrentUser(); - if (securityUser.isSystemAdmin()) { - return null; + SecurityUser securityUser = getCurrentUser(); + if (securityUser.isSystemAdmin()) { + return null; + } + User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId()); + JsonNode additionalInfo = user.getAdditionalInfo(); + HomeDashboardInfo homeDashboardInfo; + homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo); + if (homeDashboardInfo == null) { + if (securityUser.isCustomerUser()) { + Customer customer = customerService.findCustomerById(securityUser.getTenantId(), securityUser.getCustomerId()); + additionalInfo = customer.getAdditionalInfo(); + homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo); } - User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId()); - JsonNode additionalInfo = user.getAdditionalInfo(); - HomeDashboardInfo homeDashboardInfo; - homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo); if (homeDashboardInfo == null) { - if (securityUser.isCustomerUser()) { - Customer customer = customerService.findCustomerById(securityUser.getTenantId(), securityUser.getCustomerId()); - additionalInfo = customer.getAdditionalInfo(); - homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo); - } - if (homeDashboardInfo == null) { - Tenant tenant = tenantService.findTenantById(securityUser.getTenantId()); - additionalInfo = tenant.getAdditionalInfo(); - homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo); - } + Tenant tenant = tenantService.findTenantById(securityUser.getTenantId()); + additionalInfo = tenant.getAdditionalInfo(); + homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo); } - return homeDashboardInfo; - } catch (Exception e) { - throw handleException(e); } + return homeDashboardInfo; } @ApiOperation(value = "Get Tenant Home Dashboard Info (getTenantHomeDashboardInfo)", @@ -525,22 +497,18 @@ public class DashboardController extends BaseController { @RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.GET) @ResponseBody public HomeDashboardInfo getTenantHomeDashboardInfo() throws ThingsboardException { - try { - Tenant tenant = tenantService.findTenantById(getTenantId()); - JsonNode additionalInfo = tenant.getAdditionalInfo(); - DashboardId dashboardId = null; - boolean hideDashboardToolbar = true; - if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID) && !additionalInfo.get(HOME_DASHBOARD_ID).isNull()) { - String strDashboardId = additionalInfo.get(HOME_DASHBOARD_ID).asText(); - dashboardId = new DashboardId(toUUID(strDashboardId)); - if (additionalInfo.has(HOME_DASHBOARD_HIDE_TOOLBAR)) { - hideDashboardToolbar = additionalInfo.get(HOME_DASHBOARD_HIDE_TOOLBAR).asBoolean(); - } + Tenant tenant = tenantService.findTenantById(getTenantId()); + JsonNode additionalInfo = tenant.getAdditionalInfo(); + DashboardId dashboardId = null; + boolean hideDashboardToolbar = true; + if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID) && !additionalInfo.get(HOME_DASHBOARD_ID).isNull()) { + String strDashboardId = additionalInfo.get(HOME_DASHBOARD_ID).asText(); + dashboardId = new DashboardId(toUUID(strDashboardId)); + if (additionalInfo.has(HOME_DASHBOARD_HIDE_TOOLBAR)) { + hideDashboardToolbar = additionalInfo.get(HOME_DASHBOARD_HIDE_TOOLBAR).asBoolean(); } - return new HomeDashboardInfo(dashboardId, hideDashboardToolbar); - } catch (Exception e) { - throw handleException(e); } + return new HomeDashboardInfo(dashboardId, hideDashboardToolbar); } @ApiOperation(value = "Update Tenant Home Dashboard Info (getTenantHomeDashboardInfo)", @@ -554,27 +522,23 @@ public class DashboardController extends BaseController { @ApiParam(value = "A JSON object that represents home dashboard id and other parameters", required = true) @RequestBody HomeDashboardInfo homeDashboardInfo) throws ThingsboardException { - try { - if (homeDashboardInfo.getDashboardId() != null) { - checkDashboardId(homeDashboardInfo.getDashboardId(), Operation.READ); - } - Tenant tenant = tenantService.findTenantById(getTenantId()); - JsonNode additionalInfo = tenant.getAdditionalInfo(); - if (additionalInfo == null || !(additionalInfo instanceof ObjectNode)) { - additionalInfo = JacksonUtil.OBJECT_MAPPER.createObjectNode(); - } - if (homeDashboardInfo.getDashboardId() != null) { - ((ObjectNode) additionalInfo).put(HOME_DASHBOARD_ID, homeDashboardInfo.getDashboardId().getId().toString()); - ((ObjectNode) additionalInfo).put(HOME_DASHBOARD_HIDE_TOOLBAR, homeDashboardInfo.isHideDashboardToolbar()); - } else { - ((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_ID); - ((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_HIDE_TOOLBAR); - } - tenant.setAdditionalInfo(additionalInfo); - tenantService.saveTenant(tenant); - } catch (Exception e) { - throw handleException(e); + if (homeDashboardInfo.getDashboardId() != null) { + checkDashboardId(homeDashboardInfo.getDashboardId(), Operation.READ); + } + Tenant tenant = tenantService.findTenantById(getTenantId()); + JsonNode additionalInfo = tenant.getAdditionalInfo(); + if (additionalInfo == null || !(additionalInfo instanceof ObjectNode)) { + additionalInfo = JacksonUtil.OBJECT_MAPPER.createObjectNode(); + } + if (homeDashboardInfo.getDashboardId() != null) { + ((ObjectNode) additionalInfo).put(HOME_DASHBOARD_ID, homeDashboardInfo.getDashboardId().getId().toString()); + ((ObjectNode) additionalInfo).put(HOME_DASHBOARD_HIDE_TOOLBAR, homeDashboardInfo.isHideDashboardToolbar()); + } else { + ((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_ID); + ((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_HIDE_TOOLBAR); } + tenant.setAdditionalInfo(additionalInfo); + tenantService.saveTenant(tenant); } private HomeDashboardInfo extractHomeDashboardInfoFromAdditionalInfo(JsonNode additionalInfo) { @@ -681,28 +645,24 @@ public class DashboardController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("edgeId", strEdgeId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - checkEdgeId(edgeId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - PageData nonFilteredResult = dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); - List filteredDashboards = nonFilteredResult.getData().stream().filter(dashboardInfo -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.DASHBOARD, Operation.READ, dashboardInfo.getId(), dashboardInfo); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - PageData filteredResult = new PageData<>(filteredDashboards, - nonFilteredResult.getTotalPages(), - nonFilteredResult.getTotalElements(), - nonFilteredResult.hasNext()); - return checkNotNull(filteredResult); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + checkEdgeId(edgeId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + PageData nonFilteredResult = dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); + List filteredDashboards = nonFilteredResult.getData().stream().filter(dashboardInfo -> { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.DASHBOARD, Operation.READ, dashboardInfo.getId(), dashboardInfo); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + PageData filteredResult = new PageData<>(filteredDashboards, + nonFilteredResult.getTotalPages(), + nonFilteredResult.getTotalElements(), + nonFilteredResult.hasNext()); + return checkNotNull(filteredResult); } private Set customerIdFromStr(String[] strCustomerIds) { 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 ae1558381f..8155e18384 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -77,6 +77,7 @@ import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_AUTHORITY_PARAGRAPH; @@ -308,16 +309,12 @@ public class DeviceController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(deviceService.findDevicesByTenantIdAndType(tenantId, type, pageLink)); - } else { - return checkNotNull(deviceService.findDevicesByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(deviceService.findDevicesByTenantIdAndType(tenantId, type, pageLink)); + } else { + return checkNotNull(deviceService.findDevicesByTenantId(tenantId, pageLink)); } } @@ -343,19 +340,15 @@ public class DeviceController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder ) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(deviceService.findDeviceInfosByTenantIdAndType(tenantId, type, pageLink)); - } else if (deviceProfileId != null && deviceProfileId.length() > 0) { - DeviceProfileId profileId = new DeviceProfileId(toUUID(deviceProfileId)); - return checkNotNull(deviceService.findDeviceInfosByTenantIdAndDeviceProfileId(tenantId, profileId, pageLink)); - } else { - return checkNotNull(deviceService.findDeviceInfosByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(deviceService.findDeviceInfosByTenantIdAndType(tenantId, type, pageLink)); + } else if (deviceProfileId != null && deviceProfileId.length() > 0) { + DeviceProfileId profileId = new DeviceProfileId(toUUID(deviceProfileId)); + return checkNotNull(deviceService.findDeviceInfosByTenantIdAndDeviceProfileId(tenantId, profileId, pageLink)); + } else { + return checkNotNull(deviceService.findDeviceInfosByTenantId(tenantId, pageLink)); } } @@ -368,12 +361,8 @@ public class DeviceController extends BaseController { public Device getTenantDevice( @ApiParam(value = DEVICE_NAME_DESCRIPTION) @RequestParam String deviceName) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(deviceService.findDeviceByTenantIdAndName(tenantId, deviceName)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(deviceService.findDeviceByTenantIdAndName(tenantId, deviceName)); } @ApiOperation(value = "Get Customer Devices (getCustomerDevices)", @@ -398,18 +387,14 @@ public class DeviceController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); - } else { - return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); + } else { + return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, pageLink)); } } @@ -437,21 +422,17 @@ public class DeviceController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); - } else if (deviceProfileId != null && deviceProfileId.length() > 0) { - DeviceProfileId profileId = new DeviceProfileId(toUUID(deviceProfileId)); - return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerIdAndDeviceProfileId(tenantId, customerId, profileId, pageLink)); - } else { - return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); + } else if (deviceProfileId != null && deviceProfileId.length() > 0) { + DeviceProfileId profileId = new DeviceProfileId(toUUID(deviceProfileId)); + return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerIdAndDeviceProfileId(tenantId, customerId, profileId, pageLink)); + } else { + return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink)); } } @@ -462,26 +443,22 @@ public class DeviceController extends BaseController { @ResponseBody public List getDevicesByIds( @ApiParam(value = "A list of devices ids, separated by comma ','") - @RequestParam("deviceIds") String[] strDeviceIds) throws ThingsboardException { + @RequestParam("deviceIds") String[] strDeviceIds) throws ThingsboardException, ExecutionException, InterruptedException { checkArrayParameter("deviceIds", strDeviceIds); - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - CustomerId customerId = user.getCustomerId(); - List deviceIds = new ArrayList<>(); - for (String strDeviceId : strDeviceIds) { - deviceIds.add(new DeviceId(toUUID(strDeviceId))); - } - ListenableFuture> devices; - if (customerId == null || customerId.isNullUid()) { - devices = deviceService.findDevicesByTenantIdAndIdsAsync(tenantId, deviceIds); - } else { - devices = deviceService.findDevicesByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, deviceIds); - } - return checkNotNull(devices.get()); - } catch (Exception e) { - throw handleException(e); + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + CustomerId customerId = user.getCustomerId(); + List deviceIds = new ArrayList<>(); + for (String strDeviceId : strDeviceIds) { + deviceIds.add(new DeviceId(toUUID(strDeviceId))); + } + ListenableFuture> devices; + if (customerId == null || customerId.isNullUid()) { + devices = deviceService.findDevicesByTenantIdAndIdsAsync(tenantId, deviceIds); + } else { + devices = deviceService.findDevicesByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, deviceIds); } + return checkNotNull(devices.get()); } @ApiOperation(value = "Find related devices (findByQuery)", @@ -493,25 +470,21 @@ public class DeviceController extends BaseController { @ResponseBody public List findByQuery( @ApiParam(value = "The device search query JSON") - @RequestBody DeviceSearchQuery query) throws ThingsboardException { + @RequestBody DeviceSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException { checkNotNull(query); checkNotNull(query.getParameters()); checkNotNull(query.getDeviceTypes()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); - try { - List devices = checkNotNull(deviceService.findDevicesByQuery(getCurrentUser().getTenantId(), query).get()); - devices = devices.stream().filter(device -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, Operation.READ, device.getId(), device); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - return devices; - } catch (Exception e) { - throw handleException(e); - } + List devices = checkNotNull(deviceService.findDevicesByQuery(getCurrentUser().getTenantId(), query).get()); + devices = devices.stream().filter(device -> { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, Operation.READ, device.getId(), device); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + return devices; } @ApiOperation(value = "Get Device Types (getDeviceTypes)", @@ -520,15 +493,11 @@ public class DeviceController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/device/types", method = RequestMethod.GET) @ResponseBody - public List getDeviceTypes() throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - ListenableFuture> deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId); - return checkNotNull(deviceTypes.get()); - } catch (Exception e) { - throw handleException(e); - } + public List getDeviceTypes() throws ThingsboardException, ExecutionException, InterruptedException { + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + ListenableFuture> deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId); + return checkNotNull(deviceTypes.get()); } @ApiOperation(value = "Claim device (claimDevice)", @@ -722,33 +691,29 @@ public class DeviceController extends BaseController { @ApiParam(value = "Timestamp. Devices with creation time after it won't be queried") @RequestParam(required = false) Long endTime) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - checkEdgeId(edgeId, Operation.READ); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - PageData nonFilteredResult; - if (type != null && type.trim().length() > 0) { - nonFilteredResult = deviceService.findDevicesByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink); - } else { - nonFilteredResult = deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink); - } - List filteredDevices = nonFilteredResult.getData().stream().filter(device -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, Operation.READ, device.getId(), device); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - PageData filteredResult = new PageData<>(filteredDevices, - nonFilteredResult.getTotalPages(), - nonFilteredResult.getTotalElements(), - nonFilteredResult.hasNext()); - return checkNotNull(filteredResult); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + checkEdgeId(edgeId, Operation.READ); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + PageData nonFilteredResult; + if (type != null && type.trim().length() > 0) { + nonFilteredResult = deviceService.findDevicesByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink); + } else { + nonFilteredResult = deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink); } + List filteredDevices = nonFilteredResult.getData().stream().filter(device -> { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, Operation.READ, device.getId(), device); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + PageData filteredResult = new PageData<>(filteredDevices, + nonFilteredResult.getTotalPages(), + nonFilteredResult.getTotalElements(), + nonFilteredResult.hasNext()); + return checkNotNull(filteredResult); } @ApiOperation(value = "Count devices by device profile (countByDeviceProfileAndEmptyOtaPackage)", @@ -766,14 +731,10 @@ public class DeviceController extends BaseController { @PathVariable("deviceProfileId") String deviceProfileId) throws ThingsboardException { checkParameter("OtaPackageType", otaPackageType); checkParameter("DeviceProfileId", deviceProfileId); - try { - return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage( - getTenantId(), - new DeviceProfileId(UUID.fromString(deviceProfileId)), - OtaPackageType.valueOf(otaPackageType)); - } catch (Exception e) { - throw handleException(e); - } + return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage( + getTenantId(), + new DeviceProfileId(UUID.fromString(deviceProfileId)), + OtaPackageType.valueOf(otaPackageType)); } @ApiOperation(value = "Import the bulk of devices (processDevicesBulkImport)", 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 e45a195a2d..c2a6e9610e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -87,12 +87,8 @@ public class DeviceProfileController extends BaseController { @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException { checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId); - try { - DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); - return checkDeviceProfileId(deviceProfileId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); + return checkDeviceProfileId(deviceProfileId, Operation.READ); } @ApiOperation(value = "Get Device Profile Info (getDeviceProfileInfoById)", @@ -106,12 +102,8 @@ public class DeviceProfileController extends BaseController { @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException { checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId); - try { - DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); - return new DeviceProfileInfo(checkDeviceProfileId(deviceProfileId, Operation.READ)); - } catch (Exception e) { - throw handleException(e); - } + DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); + return new DeviceProfileInfo(checkDeviceProfileId(deviceProfileId, Operation.READ)); } @ApiOperation(value = "Get Default Device Profile (getDefaultDeviceProfileInfo)", @@ -122,11 +114,7 @@ public class DeviceProfileController extends BaseController { @RequestMapping(value = "/deviceProfileInfo/default", method = RequestMethod.GET) @ResponseBody public DeviceProfileInfo getDefaultDeviceProfileInfo() throws ThingsboardException { - try { - return checkNotNull(deviceProfileService.findDefaultDeviceProfileInfo(getTenantId())); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(deviceProfileService.findDefaultDeviceProfileInfo(getTenantId())); } @ApiOperation(value = "Get time-series keys (getTimeseriesKeys)", @@ -150,11 +138,7 @@ public class DeviceProfileController extends BaseController { deviceProfileId = null; } - try { - return timeseriesService.findAllKeysByDeviceProfileId(getTenantId(), deviceProfileId); - } catch (Exception e) { - throw handleException(e); - } + return timeseriesService.findAllKeysByDeviceProfileId(getTenantId(), deviceProfileId); } @ApiOperation(value = "Get attribute keys (getAttributesKeys)", @@ -178,11 +162,7 @@ public class DeviceProfileController extends BaseController { deviceProfileId = null; } - try { - return attributesService.findAllKeysByDeviceProfileId(getTenantId(), deviceProfileId); - } catch (Exception e) { - throw handleException(e); - } + return attributesService.findAllKeysByDeviceProfileId(getTenantId(), deviceProfileId); } @ApiOperation(value = "Create Or Update Device Profile (saveDeviceProfile)", @@ -256,12 +236,8 @@ public class DeviceProfileController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(deviceProfileService.findDeviceProfiles(getTenantId(), pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(deviceProfileService.findDeviceProfiles(getTenantId(), pageLink)); } @ApiOperation(value = "Get Device Profiles for transport type (getDeviceProfileInfos)", @@ -284,11 +260,7 @@ public class DeviceProfileController extends BaseController { @RequestParam(required = false) String sortOrder, @ApiParam(value = "Type of the transport", allowableValues = TRANSPORT_TYPE_ALLOWABLE_VALUES) @RequestParam(required = false) String transportType) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(deviceProfileService.findDeviceProfileInfos(getTenantId(), pageLink, transportType)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(deviceProfileService.findDeviceProfileInfos(getTenantId(), pageLink, transportType)); } } 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 59c9eb39c1..0ac0cea893 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -61,6 +61,7 @@ import org.thingsboard.server.service.security.permission.Resource; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION; @@ -111,12 +112,8 @@ public class EdgeController extends BaseController { public Edge getEdgeById(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); - try { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - return checkEdgeId(edgeId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + return checkEdgeId(edgeId, Operation.READ); } @ApiOperation(value = "Get Edge Info (getEdgeInfoById)", @@ -128,12 +125,8 @@ public class EdgeController extends BaseController { public EdgeInfo getEdgeInfoById(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); - try { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - return checkEdgeInfoId(edgeId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + return checkEdgeInfoId(edgeId, Operation.READ); } @ApiOperation(value = "Create Or Update Edge (saveEdge)", @@ -198,13 +191,9 @@ public class EdgeController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink)); } @ApiOperation(value = "Assign edge to customer (assignEdgeToCustomer)", @@ -280,16 +269,12 @@ public class EdgeController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(edgeService.findEdgesByTenantIdAndType(tenantId, type, pageLink)); - } else { - return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(edgeService.findEdgesByTenantIdAndType(tenantId, type, pageLink)); + } else { + return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink)); } } @@ -313,16 +298,12 @@ public class EdgeController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(edgeService.findEdgeInfosByTenantIdAndType(tenantId, type, pageLink)); - } else { - return checkNotNull(edgeService.findEdgeInfosByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(edgeService.findEdgeInfosByTenantIdAndType(tenantId, type, pageLink)); + } else { + return checkNotNull(edgeService.findEdgeInfosByTenantId(tenantId, pageLink)); } } @@ -335,12 +316,8 @@ public class EdgeController extends BaseController { @ResponseBody public Edge getTenantEdge(@ApiParam(value = "Unique name of the edge", required = true) @RequestParam String edgeName) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(edgeService.findEdgeByTenantIdAndName(tenantId, edgeName)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(edgeService.findEdgeByTenantIdAndName(tenantId, edgeName)); } @ApiOperation(value = "Set root rule chain for provided edge (setEdgeRootRuleChain)", @@ -386,22 +363,18 @@ public class EdgeController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - PageData result; - if (type != null && type.trim().length() > 0) { - result = edgeService.findEdgesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink); - } else { - result = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink); - } - return checkNotNull(result); - } catch (Exception e) { - throw handleException(e); + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + PageData result; + if (type != null && type.trim().length() > 0) { + result = edgeService.findEdgesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink); + } else { + result = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink); } + return checkNotNull(result); } @ApiOperation(value = "Get Customer Edge Infos (getCustomerEdgeInfos)", @@ -426,22 +399,18 @@ public class EdgeController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - PageData result; - if (type != null && type.trim().length() > 0) { - result = edgeService.findEdgeInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink); - } else { - result = edgeService.findEdgeInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink); - } - return checkNotNull(result); - } catch (Exception e) { - throw handleException(e); + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + PageData result; + if (type != null && type.trim().length() > 0) { + result = edgeService.findEdgeInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink); + } else { + result = edgeService.findEdgeInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink); } + return checkNotNull(result); } @ApiOperation(value = "Get Edges By Ids (getEdgesByIds)", @@ -452,27 +421,23 @@ public class EdgeController extends BaseController { @ResponseBody public List getEdgesByIds( @ApiParam(value = "A list of edges ids, separated by comma ','", required = true) - @RequestParam("edgeIds") String[] strEdgeIds) throws ThingsboardException { + @RequestParam("edgeIds") String[] strEdgeIds) throws ThingsboardException, ExecutionException, InterruptedException { checkArrayParameter("edgeIds", strEdgeIds); - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - CustomerId customerId = user.getCustomerId(); - List edgeIds = new ArrayList<>(); - for (String strEdgeId : strEdgeIds) { - edgeIds.add(new EdgeId(toUUID(strEdgeId))); - } - ListenableFuture> edgesFuture; - if (customerId == null || customerId.isNullUid()) { - edgesFuture = edgeService.findEdgesByTenantIdAndIdsAsync(tenantId, edgeIds); - } else { - edgesFuture = edgeService.findEdgesByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, edgeIds); - } - List edges = edgesFuture.get(); - return checkNotNull(edges); - } catch (Exception e) { - throw handleException(e); + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + CustomerId customerId = user.getCustomerId(); + List edgeIds = new ArrayList<>(); + for (String strEdgeId : strEdgeIds) { + edgeIds.add(new EdgeId(toUUID(strEdgeId))); + } + ListenableFuture> edgesFuture; + if (customerId == null || customerId.isNullUid()) { + edgesFuture = edgeService.findEdgesByTenantIdAndIdsAsync(tenantId, edgeIds); + } else { + edgesFuture = edgeService.findEdgesByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, edgeIds); } + List edges = edgesFuture.get(); + return checkNotNull(edges); } @ApiOperation(value = "Find related edges (findByQuery)", @@ -483,27 +448,23 @@ public class EdgeController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edges", method = RequestMethod.POST) @ResponseBody - public List findByQuery(@RequestBody EdgeSearchQuery query) throws ThingsboardException { + public List findByQuery(@RequestBody EdgeSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException { checkNotNull(query); checkNotNull(query.getParameters()); checkNotNull(query.getEdgeTypes()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - List edges = checkNotNull(edgeService.findEdgesByQuery(tenantId, query).get()); - edges = edges.stream().filter(edge -> { - try { - accessControlService.checkPermission(user, Resource.EDGE, Operation.READ, edge.getId(), edge); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - return edges; - } catch (Exception e) { - throw handleException(e); - } + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + List edges = checkNotNull(edgeService.findEdgesByQuery(tenantId, query).get()); + edges = edges.stream().filter(edge -> { + try { + accessControlService.checkPermission(user, Resource.EDGE, Operation.READ, edge.getId(), edge); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + return edges; } @ApiOperation(value = "Get Edge Types (getEdgeTypes)", @@ -513,15 +474,11 @@ public class EdgeController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edge/types", method = RequestMethod.GET) @ResponseBody - public List getEdgeTypes() throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - ListenableFuture> edgeTypes = edgeService.findEdgeTypesByTenantId(tenantId); - return checkNotNull(edgeTypes.get()); - } catch (Exception e) { - throw handleException(e); - } + public List getEdgeTypes() throws ThingsboardException, ExecutionException, InterruptedException { + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + ListenableFuture> edgeTypes = edgeService.findEdgeTypesByTenantId(tenantId); + return checkNotNull(edgeTypes.get()); } @ApiOperation(value = "Sync edge (syncEdge)", @@ -532,18 +489,14 @@ public class EdgeController extends BaseController { public void syncEdge(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("edgeId") String strEdgeId) throws ThingsboardException { checkParameter("edgeId", strEdgeId); - try { - if (isEdgesEnabled()) { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - edgeId = checkNotNull(edgeId); - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - edgeGrpcService.startSyncProcess(tenantId, edgeId); - } else { - throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL); - } - } catch (Exception e) { - throw handleException(e); + if (isEdgesEnabled()) { + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + edgeId = checkNotNull(edgeId); + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + edgeGrpcService.startSyncProcess(tenantId, edgeId); + } else { + throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL); } } @@ -554,15 +507,11 @@ public class EdgeController extends BaseController { @ResponseBody public String findMissingToRelatedRuleChains(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("edgeId") String strEdgeId) throws ThingsboardException { - try { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - edgeId = checkNotNull(edgeId); - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - return edgeService.findMissingToRelatedRuleChains(tenantId, edgeId, TbRuleChainInputNode.class.getName()); - } catch (Exception e) { - throw handleException(e); - } + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + edgeId = checkNotNull(edgeId); + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + return edgeService.findMissingToRelatedRuleChains(tenantId, edgeId, TbRuleChainInputNode.class.getName()); } @ApiOperation(value = "Import the bulk of edges (processEdgesBulkImport)", diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java index 5dd43736a8..6281336185 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java @@ -81,14 +81,10 @@ public class EdgeEventController extends BaseController { @ApiParam(value = "Timestamp. Edge events with creation time after it won't be queried") @RequestParam(required = false) Long endTime) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - checkEdgeId(edgeId, Operation.READ); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - return checkNotNull(edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, false)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + checkEdgeId(edgeId, Operation.READ); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + return checkNotNull(edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, false)); } } diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java index bc6c628bff..9b7e5c1ba4 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java @@ -61,11 +61,7 @@ public class EntityQueryController extends BaseController { @ApiParam(value = "A JSON value representing the entity count query. See API call notes above for more details.") @RequestBody EntityCountQuery query) throws ThingsboardException { checkNotNull(query); - try { - return this.entityQueryService.countEntitiesByQuery(getCurrentUser(), query); - } catch (Exception e) { - throw handleException(e); - } + return this.entityQueryService.countEntitiesByQuery(getCurrentUser(), query); } @ApiOperation(value = "Find Entity Data by Query", notes = ENTITY_DATA_QUERY_DESCRIPTION) @@ -76,11 +72,7 @@ public class EntityQueryController extends BaseController { @ApiParam(value = "A JSON value representing the entity data query. See API call notes above for more details.") @RequestBody EntityDataQuery query) throws ThingsboardException { checkNotNull(query); - try { - return this.entityQueryService.findEntityDataByQuery(getCurrentUser(), query); - } catch (Exception e) { - throw handleException(e); - } + return this.entityQueryService.findEntityDataByQuery(getCurrentUser(), query); } @ApiOperation(value = "Find Alarms by Query", notes = ALARM_DATA_QUERY_DESCRIPTION) @@ -91,11 +83,7 @@ public class EntityQueryController extends BaseController { @ApiParam(value = "A JSON value representing the alarm data query. See API call notes above for more details.") @RequestBody AlarmDataQuery query) throws ThingsboardException { checkNotNull(query); - try { - return this.entityQueryService.findAlarmDataByQuery(getCurrentUser(), query); - } catch (Exception e) { - throw handleException(e); - } + return this.entityQueryService.findAlarmDataByQuery(getCurrentUser(), query); } @ApiOperation(value = "Find Entity Keys by Query", @@ -112,15 +100,11 @@ public class EntityQueryController extends BaseController { @RequestParam("attributes") boolean isAttributes) throws ThingsboardException { TenantId tenantId = getTenantId(); checkNotNull(query); - try { - EntityDataPageLink pageLink = query.getPageLink(); - if (pageLink.getPageSize() > MAX_PAGE_SIZE) { - pageLink.setPageSize(MAX_PAGE_SIZE); - } - return entityQueryService.getKeysByQuery(getCurrentUser(), tenantId, query, isTimeseries, isAttributes); - } catch (Exception e) { - throw handleException(e); + EntityDataPageLink pageLink = query.getPageLink(); + if (pageLink.getPageSize() > MAX_PAGE_SIZE) { + pageLink.setPageSize(MAX_PAGE_SIZE); } + return entityQueryService.getKeysByQuery(getCurrentUser(), tenantId, query, isTimeseries, isAttributes); } } 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 efc4237b3f..46c68d0be8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -41,6 +41,7 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.thingsboard.server.controller.ControllerConstants.ENTITY_ID_PARAM_DESCRIPTION; @@ -143,21 +144,17 @@ public class EntityRelationController extends BaseController { @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId, @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType) throws ThingsboardException { - try { - checkParameter(FROM_ID, strFromId); - checkParameter(FROM_TYPE, strFromType); - checkParameter(RELATION_TYPE, strRelationType); - checkParameter(TO_ID, strToId); - checkParameter(TO_TYPE, strToType); - EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); - EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId); - checkEntityId(fromId, Operation.READ); - checkEntityId(toId, Operation.READ); - RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); - return checkNotNull(relationService.getRelation(getTenantId(), fromId, toId, strRelationType, typeGroup)); - } catch (Exception e) { - throw handleException(e); - } + checkParameter(FROM_ID, strFromId); + checkParameter(FROM_TYPE, strFromType); + checkParameter(RELATION_TYPE, strRelationType); + checkParameter(TO_ID, strToId); + checkParameter(TO_TYPE, strToType); + EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); + EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId); + checkEntityId(fromId, Operation.READ); + checkEntityId(toId, Operation.READ); + RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); + return checkNotNull(relationService.getRelation(getTenantId(), fromId, toId, strRelationType, typeGroup)); } @ApiOperation(value = "Get List of Relations (findByFrom)", @@ -176,11 +173,7 @@ public class EntityRelationController extends BaseController { EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); checkEntityId(entityId, Operation.READ); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findByFrom(getTenantId(), entityId, typeGroup))); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findByFrom(getTenantId(), entityId, typeGroup))); } @ApiOperation(value = "Get List of Relation Infos (findInfoByFrom)", @@ -193,17 +186,13 @@ public class EntityRelationController extends BaseController { public List findInfoByFrom(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId, @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType, @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) - @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { + @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException, ExecutionException, InterruptedException { checkParameter(FROM_ID, strFromId); checkParameter(FROM_TYPE, strFromType); EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); checkEntityId(entityId, Operation.READ); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByFrom(getTenantId(), entityId, typeGroup).get())); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByFrom(getTenantId(), entityId, typeGroup).get())); } @ApiOperation(value = "Get List of Relations (findByFrom)", @@ -224,11 +213,7 @@ public class EntityRelationController extends BaseController { EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); checkEntityId(entityId, Operation.READ); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findByFromAndType(getTenantId(), entityId, strRelationType, typeGroup))); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findByFromAndType(getTenantId(), entityId, strRelationType, typeGroup))); } @ApiOperation(value = "Get List of Relations (findByTo)", @@ -247,11 +232,7 @@ public class EntityRelationController extends BaseController { EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId); checkEntityId(entityId, Operation.READ); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findByTo(getTenantId(), entityId, typeGroup))); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findByTo(getTenantId(), entityId, typeGroup))); } @ApiOperation(value = "Get List of Relation Infos (findInfoByTo)", @@ -264,17 +245,13 @@ public class EntityRelationController extends BaseController { public List findInfoByTo(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId, @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType, @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) - @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { + @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException, ExecutionException, InterruptedException { checkParameter(TO_ID, strToId); checkParameter(TO_TYPE, strToType); EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId); checkEntityId(entityId, Operation.READ); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByTo(getTenantId(), entityId, typeGroup).get())); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByTo(getTenantId(), entityId, typeGroup).get())); } @ApiOperation(value = "Get List of Relations (findByTo)", @@ -295,11 +272,7 @@ public class EntityRelationController extends BaseController { EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId); checkEntityId(entityId, Operation.READ); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findByToAndType(getTenantId(), entityId, strRelationType, typeGroup))); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findByToAndType(getTenantId(), entityId, strRelationType, typeGroup))); } @ApiOperation(value = "Find related entities (findByQuery)", @@ -310,16 +283,12 @@ public class EntityRelationController extends BaseController { @RequestMapping(value = "/relations", method = RequestMethod.POST) @ResponseBody public List findByQuery(@ApiParam(value = "A JSON value representing the entity relations query object.", required = true) - @RequestBody EntityRelationsQuery query) throws ThingsboardException { + @RequestBody EntityRelationsQuery query) throws ThingsboardException, ExecutionException, InterruptedException { checkNotNull(query); checkNotNull(query.getParameters()); checkNotNull(query.getFilters()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findByQuery(getTenantId(), query).get())); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findByQuery(getTenantId(), query).get())); } @ApiOperation(value = "Find related entity infos (findInfoByQuery)", @@ -330,16 +299,12 @@ public class EntityRelationController extends BaseController { @RequestMapping(value = "/relations/info", method = RequestMethod.POST) @ResponseBody public List findInfoByQuery(@ApiParam(value = "A JSON value representing the entity relations query object.", required = true) - @RequestBody EntityRelationsQuery query) throws ThingsboardException { + @RequestBody EntityRelationsQuery query) throws ThingsboardException, ExecutionException, InterruptedException { checkNotNull(query); checkNotNull(query.getParameters()); checkNotNull(query.getFilters()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByQuery(getTenantId(), query).get())); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByQuery(getTenantId(), query).get())); } private void checkCanCreateRelation(EntityId entityId) throws ThingsboardException { 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 0f299fefae..9ab8ab80a9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -54,6 +54,7 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID; @@ -105,11 +106,7 @@ public class EntityViewController extends BaseController { @ApiParam(value = ENTITY_VIEW_ID_PARAM_DESCRIPTION) @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { checkParameter(ENTITY_VIEW_ID, strEntityViewId); - try { - return checkEntityViewId(new EntityViewId(toUUID(strEntityViewId)), Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + return checkEntityViewId(new EntityViewId(toUUID(strEntityViewId)), Operation.READ); } @ApiOperation(value = "Get Entity View info (getEntityViewInfoById)", @@ -123,12 +120,8 @@ public class EntityViewController extends BaseController { @ApiParam(value = ENTITY_VIEW_ID_PARAM_DESCRIPTION) @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { checkParameter(ENTITY_VIEW_ID, strEntityViewId); - try { - EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); - return checkEntityViewInfoId(entityViewId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); + return checkEntityViewInfoId(entityViewId, Operation.READ); } @ApiOperation(value = "Save or update entity view (saveEntityView)", @@ -177,12 +170,8 @@ public class EntityViewController extends BaseController { public EntityView getTenantEntityView( @ApiParam(value = "Entity View name") @RequestParam String entityViewName) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(entityViewService.findEntityViewByTenantIdAndName(tenantId, entityViewName)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(entityViewService.findEntityViewByTenantIdAndName(tenantId, entityViewName)); } @ApiOperation(value = "Assign Entity View to customer (assignEntityViewToCustomer)", @@ -249,18 +238,14 @@ public class EntityViewController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter(CUSTOMER_ID, strCustomerId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId, customerId, pageLink, type)); - } else { - return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId, customerId, pageLink, type)); + } else { + return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); } } @@ -286,18 +271,14 @@ public class EntityViewController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); - } else { - return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); + } else { + return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink)); } } @@ -320,17 +301,13 @@ public class EntityViewController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - - if (type != null && type.trim().length() > 0) { - return checkNotNull(entityViewService.findEntityViewByTenantIdAndType(tenantId, pageLink, type)); - } else { - return checkNotNull(entityViewService.findEntityViewByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + + if (type != null && type.trim().length() > 0) { + return checkNotNull(entityViewService.findEntityViewByTenantIdAndType(tenantId, pageLink, type)); + } else { + return checkNotNull(entityViewService.findEntityViewByTenantId(tenantId, pageLink)); } } @@ -353,16 +330,12 @@ public class EntityViewController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndType(tenantId, type, pageLink)); - } else { - return checkNotNull(entityViewService.findEntityViewInfosByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndType(tenantId, type, pageLink)); + } else { + return checkNotNull(entityViewService.findEntityViewInfosByTenantId(tenantId, pageLink)); } } @@ -375,25 +348,21 @@ public class EntityViewController extends BaseController { @ResponseBody public List findByQuery( @ApiParam(value = "The entity view search query JSON") - @RequestBody EntityViewSearchQuery query) throws ThingsboardException { + @RequestBody EntityViewSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException { checkNotNull(query); checkNotNull(query.getParameters()); checkNotNull(query.getEntityViewTypes()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); - try { - List entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(getTenantId(), query).get()); - entityViews = entityViews.stream().filter(entityView -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - return entityViews; - } catch (Exception e) { - throw handleException(e); - } + List entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(getTenantId(), query).get()); + entityViews = entityViews.stream().filter(entityView -> { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + return entityViews; } @ApiOperation(value = "Get Entity View Types (getEntityViewTypes)", @@ -402,15 +371,11 @@ public class EntityViewController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/entityView/types", method = RequestMethod.GET) @ResponseBody - public List getEntityViewTypes() throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - ListenableFuture> entityViewTypes = entityViewService.findEntityViewTypesByTenantId(tenantId); - return checkNotNull(entityViewTypes.get()); - } catch (Exception e) { - throw handleException(e); - } + public List getEntityViewTypes() throws ThingsboardException, ExecutionException, InterruptedException { + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + ListenableFuture> entityViewTypes = entityViewService.findEntityViewTypesByTenantId(tenantId); + return checkNotNull(entityViewTypes.get()); } @ApiOperation(value = "Make entity view publicly available (assignEntityViewToPublicCustomer)", @@ -497,32 +462,28 @@ public class EntityViewController extends BaseController { @RequestParam(required = false) Long startTime, @RequestParam(required = false) Long endTime) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - checkEdgeId(edgeId, Operation.READ); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - PageData nonFilteredResult; - if (type != null && type.trim().length() > 0) { - nonFilteredResult = entityViewService.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink); - } else { - nonFilteredResult = entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); - } - List filteredEntityViews = nonFilteredResult.getData().stream().filter(entityView -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - PageData filteredResult = new PageData<>(filteredEntityViews, - nonFilteredResult.getTotalPages(), - nonFilteredResult.getTotalElements(), - nonFilteredResult.hasNext()); - return checkNotNull(filteredResult); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + checkEdgeId(edgeId, Operation.READ); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + PageData nonFilteredResult; + if (type != null && type.trim().length() > 0) { + nonFilteredResult = entityViewService.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink); + } else { + nonFilteredResult = entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); } + List filteredEntityViews = nonFilteredResult.getData().stream().filter(entityView -> { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + PageData filteredResult = new PageData<>(filteredEntityViews, + nonFilteredResult.getTotalPages(), + nonFilteredResult.getTotalElements(), + nonFilteredResult.hasNext()); + return checkNotNull(filteredResult); } } diff --git a/application/src/main/java/org/thingsboard/server/controller/EventController.java b/application/src/main/java/org/thingsboard/server/controller/EventController.java index db040599fa..42e2af5fed 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EventController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EventController.java @@ -248,14 +248,10 @@ public class EventController extends BaseController { @RequestBody EventFilter eventFilter) throws ThingsboardException { checkParameter("EntityId", strEntityId); checkParameter("EntityType", strEntityType); - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); - checkEntityId(entityId, Operation.WRITE); + EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); + checkEntityId(entityId, Operation.WRITE); - eventService.removeEvents(getTenantId(), entityId, eventFilter, startTime, endTime); - } catch (Exception e) { - throw handleException(e); - } + eventService.removeEvents(getTenantId(), entityId, eventFilter, startTime, endTime); } private static EventType resolveEventType(String eventType) throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java index 8a47ed2c15..883bdb806d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java +++ b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java @@ -64,11 +64,7 @@ public class Lwm2mController extends BaseController { public LwM2MServerSecurityConfigDefault getLwm2mBootstrapSecurityInfo( @ApiParam(value = IS_BOOTSTRAP_SERVER_PARAM_DESCRIPTION) @PathVariable(IS_BOOTSTRAP_SERVER) boolean bootstrapServer) throws ThingsboardException { - try { - return lwM2MService.getServerSecurityInfo(bootstrapServer); - } catch (Exception e) { - throw handleException(e); - } + return lwM2MService.getServerSecurityInfo(bootstrapServer); } @ApiOperation(hidden = true, value = "Save device with credentials (Deprecated)") diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java index f08f8f380a..599b519cd9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java @@ -54,12 +54,8 @@ public class OAuth2ConfigTemplateController extends BaseController { @RequestMapping(method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) public OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(@RequestBody OAuth2ClientRegistrationTemplate clientRegistrationTemplate) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.WRITE); - return oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.WRITE); + return oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); } @ApiOperation(value = "Delete OAuth2 client registration template by id (deleteClientRegistrationTemplate)" + SYSTEM_AUTHORITY_PARAGRAPH, @@ -70,13 +66,9 @@ public class OAuth2ConfigTemplateController extends BaseController { public void deleteClientRegistrationTemplate(@ApiParam(value = "String representation of client registration template id to delete", example = "139b1f81-2f5d-11ec-9dbe-9b627e1a88f4") @PathVariable(CLIENT_REGISTRATION_TEMPLATE_ID) String strClientRegistrationTemplateId) throws ThingsboardException { checkParameter(CLIENT_REGISTRATION_TEMPLATE_ID, strClientRegistrationTemplateId); - try { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.DELETE); - OAuth2ClientRegistrationTemplateId clientRegistrationTemplateId = new OAuth2ClientRegistrationTemplateId(toUUID(strClientRegistrationTemplateId)); - oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(clientRegistrationTemplateId); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.DELETE); + OAuth2ClientRegistrationTemplateId clientRegistrationTemplateId = new OAuth2ClientRegistrationTemplateId(toUUID(strClientRegistrationTemplateId)); + oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(clientRegistrationTemplateId); } @ApiOperation(value = "Get the list of all OAuth2 client registration templates (getClientRegistrationTemplates)" + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, @@ -85,12 +77,8 @@ public class OAuth2ConfigTemplateController extends BaseController { @RequestMapping(method = RequestMethod.GET, produces = "application/json") @ResponseBody public List getClientRegistrationTemplates() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.READ); - return oAuth2ConfigTemplateService.findAllClientRegistrationTemplates(); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.READ); + return oAuth2ConfigTemplateService.findAllClientRegistrationTemplates(); } } 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 5904fab60d..9edf74c96b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -69,25 +69,21 @@ public class OAuth2Controller extends BaseController { "If platform type is not one of allowable values - it will just be ignored", allowableValues = "WEB, ANDROID, IOS") @RequestParam(required = false) String platform) throws ThingsboardException { - try { - if (log.isDebugEnabled()) { - log.debug("Executing getOAuth2Clients: [{}][{}][{}]", request.getScheme(), request.getServerName(), request.getServerPort()); - Enumeration headerNames = request.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String header = headerNames.nextElement(); - log.debug("Header: {} {}", header, request.getHeader(header)); - } + if (log.isDebugEnabled()) { + log.debug("Executing getOAuth2Clients: [{}][{}][{}]", request.getScheme(), request.getServerName(), request.getServerPort()); + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String header = headerNames.nextElement(); + log.debug("Header: {} {}", header, request.getHeader(header)); } - PlatformType platformType = null; - if (StringUtils.isNotEmpty(platform)) { - try { - platformType = PlatformType.valueOf(platform); - } catch (Exception e) {} - } - return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request), pkgName, platformType); - } catch (Exception e) { - throw handleException(e); } + PlatformType platformType = null; + if (StringUtils.isNotEmpty(platform)) { + try { + platformType = PlatformType.valueOf(platform); + } catch (Exception e) {} + } + return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request), pkgName, platformType); } @ApiOperation(value = "Get current OAuth2 settings (getCurrentOAuth2Info)", notes = SYSTEM_AUTHORITY_PARAGRAPH) @@ -95,12 +91,8 @@ public class OAuth2Controller extends BaseController { @RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json") @ResponseBody public OAuth2Info getCurrentOAuth2Info() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); - return oAuth2Service.findOAuth2Info(); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); + return oAuth2Service.findOAuth2Info(); } @ApiOperation(value = "Save OAuth2 settings (saveOAuth2Info)", notes = SYSTEM_AUTHORITY_PARAGRAPH) @@ -108,13 +100,9 @@ public class OAuth2Controller extends BaseController { @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) public OAuth2Info saveOAuth2Info(@RequestBody OAuth2Info oauth2Info) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.WRITE); - oAuth2Service.saveOAuth2Info(oauth2Info); - return oAuth2Service.findOAuth2Info(); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.WRITE); + oAuth2Service.saveOAuth2Info(oauth2Info); + return oAuth2Service.findOAuth2Info(); } @ApiOperation(value = "Get OAuth2 log in processing URL (getLoginProcessingUrl)", notes = "Returns the URL enclosed in " + @@ -125,12 +113,8 @@ public class OAuth2Controller extends BaseController { @RequestMapping(value = "/oauth2/loginProcessingUrl", method = RequestMethod.GET) @ResponseBody public String getLoginProcessingUrl() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); - return "\"" + oAuth2Configuration.getLoginProcessingUrl() + "\""; - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); + return "\"" + oAuth2Configuration.getLoginProcessingUrl() + "\""; } } diff --git a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java index 0040187f02..a673507c81 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java @@ -87,24 +87,20 @@ public class OtaPackageController extends BaseController { public ResponseEntity downloadOtaPackage(@ApiParam(value = OTA_PACKAGE_ID_PARAM_DESCRIPTION) @PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException { checkParameter(OTA_PACKAGE_ID, strOtaPackageId); - try { - OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); - OtaPackage otaPackage = checkOtaPackageId(otaPackageId, Operation.READ); - - if (otaPackage.hasUrl()) { - return ResponseEntity.badRequest().build(); - } + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + OtaPackage otaPackage = checkOtaPackageId(otaPackageId, Operation.READ); - ByteArrayResource resource = new ByteArrayResource(otaPackage.getData().array()); - return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackage.getFileName()) - .header("x-filename", otaPackage.getFileName()) - .contentLength(resource.contentLength()) - .contentType(parseMediaType(otaPackage.getContentType())) - .body(resource); - } catch (Exception e) { - throw handleException(e); + if (otaPackage.hasUrl()) { + return ResponseEntity.badRequest().build(); } + + ByteArrayResource resource = new ByteArrayResource(otaPackage.getData().array()); + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackage.getFileName()) + .header("x-filename", otaPackage.getFileName()) + .contentLength(resource.contentLength()) + .contentType(parseMediaType(otaPackage.getContentType())) + .body(resource); } @ApiOperation(value = "Get OTA Package Info (getOtaPackageInfoById)", @@ -117,12 +113,8 @@ public class OtaPackageController extends BaseController { public OtaPackageInfo getOtaPackageInfoById(@ApiParam(value = OTA_PACKAGE_ID_PARAM_DESCRIPTION) @PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException { checkParameter(OTA_PACKAGE_ID, strOtaPackageId); - try { - OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); - return checkNotNull(otaPackageService.findOtaPackageInfoById(getTenantId(), otaPackageId)); - } catch (Exception e) { - throw handleException(e); - } + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + return checkNotNull(otaPackageService.findOtaPackageInfoById(getTenantId(), otaPackageId)); } @ApiOperation(value = "Get OTA Package (getOtaPackageById)", @@ -135,12 +127,8 @@ public class OtaPackageController extends BaseController { public OtaPackage getOtaPackageById(@ApiParam(value = OTA_PACKAGE_ID_PARAM_DESCRIPTION) @PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException { checkParameter(OTA_PACKAGE_ID, strOtaPackageId); - try { - OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); - return checkOtaPackageId(otaPackageId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + return checkOtaPackageId(otaPackageId, Operation.READ); } @ApiOperation(value = "Create Or Update OTA Package Info (saveOtaPackageInfo)", @@ -204,12 +192,8 @@ public class OtaPackageController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantId(getTenantId(), pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantId(getTenantId(), pageLink)); } @ApiOperation(value = "Get OTA Package Infos (getOtaPackages)", @@ -235,13 +219,9 @@ public class OtaPackageController extends BaseController { @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("deviceProfileId", strDeviceProfileId); checkParameter("type", strType); - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(), - new DeviceProfileId(toUUID(strDeviceProfileId)), OtaPackageType.valueOf(strType), pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(), + new DeviceProfileId(toUUID(strDeviceProfileId)), OtaPackageType.valueOf(strType), pageLink)); } @ApiOperation(value = "Delete OTA Package (deleteOtaPackage)", diff --git a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java index 300d522dbe..2394270118 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java @@ -159,12 +159,8 @@ public class RpcV2Controller extends AbstractRpcController { @ApiParam(value = RPC_ID_PARAM_DESCRIPTION, required = true) @PathVariable(RPC_ID) String strRpc) throws ThingsboardException { checkParameter("RpcId", strRpc); - try { - RpcId rpcId = new RpcId(UUID.fromString(strRpc)); - return checkRpcId(rpcId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + RpcId rpcId = new RpcId(UUID.fromString(strRpc)); + return checkRpcId(rpcId, Operation.READ); } @ApiOperation(value = "Get persistent RPC requests", notes = "Allows to query RPC calls for specific device using pagination." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @@ -187,43 +183,39 @@ public class RpcV2Controller extends AbstractRpcController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("DeviceId", strDeviceId); - try { - if (rpcStatus != null && rpcStatus.equals(RpcStatus.DELETED)) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "RpcStatus: DELETED"); - } - - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - DeviceId deviceId = new DeviceId(UUID.fromString(strDeviceId)); - final DeferredResult response = new DeferredResult<>(); + if (rpcStatus != null && rpcStatus.equals(RpcStatus.DELETED)) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "RpcStatus: DELETED"); + } - accessValidator.validate(getCurrentUser(), Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<>() { - @Override - public void onSuccess(@Nullable DeferredResult result) { - PageData rpcCalls; - if (rpcStatus != null) { - rpcCalls = rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink); - } else { - rpcCalls = rpcService.findAllByDeviceId(tenantId, deviceId, pageLink); - } - response.setResult(new ResponseEntity<>(rpcCalls, HttpStatus.OK)); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + DeviceId deviceId = new DeviceId(UUID.fromString(strDeviceId)); + final DeferredResult response = new DeferredResult<>(); + + accessValidator.validate(getCurrentUser(), Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<>() { + @Override + public void onSuccess(@Nullable DeferredResult result) { + PageData rpcCalls; + if (rpcStatus != null) { + rpcCalls = rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink); + } else { + rpcCalls = rpcService.findAllByDeviceId(tenantId, deviceId, pageLink); } + response.setResult(new ResponseEntity<>(rpcCalls, HttpStatus.OK)); + } - @Override - public void onFailure(Throwable e) { - ResponseEntity entity; - if (e instanceof ToErrorResponseEntity) { - entity = ((ToErrorResponseEntity) e).toErrorResponseEntity(); - } else { - entity = new ResponseEntity(HttpStatus.UNAUTHORIZED); - } - response.setResult(entity); + @Override + public void onFailure(Throwable e) { + ResponseEntity entity; + if (e instanceof ToErrorResponseEntity) { + entity = ((ToErrorResponseEntity) e).toErrorResponseEntity(); + } else { + entity = new ResponseEntity(HttpStatus.UNAUTHORIZED); } - })); - return response; - } catch (Exception e) { - throw handleException(e); - } + response.setResult(entity); + } + })); + return response; } @ApiOperation(value = "Delete persistent RPC", notes = "Deletes the persistent RPC request." + TENANT_AUTHORITY_PARAGRAPH) @@ -234,25 +226,21 @@ public class RpcV2Controller extends AbstractRpcController { @ApiParam(value = RPC_ID_PARAM_DESCRIPTION, required = true) @PathVariable(RPC_ID) String strRpc) throws ThingsboardException { checkParameter("RpcId", strRpc); - try { - RpcId rpcId = new RpcId(UUID.fromString(strRpc)); - Rpc rpc = checkRpcId(rpcId, Operation.DELETE); - - if (rpc != null) { - if (rpc.getStatus().equals(RpcStatus.QUEUED)) { - RemoveRpcActorMsg removeMsg = new RemoveRpcActorMsg(getTenantId(), rpc.getDeviceId(), rpc.getUuidId()); - log.trace("[{}] Forwarding msg {} to queue actor!", rpc.getDeviceId(), rpc); - tbClusterService.pushMsgToCore(removeMsg, null); - } + RpcId rpcId = new RpcId(UUID.fromString(strRpc)); + Rpc rpc = checkRpcId(rpcId, Operation.DELETE); + + if (rpc != null) { + if (rpc.getStatus().equals(RpcStatus.QUEUED)) { + RemoveRpcActorMsg removeMsg = new RemoveRpcActorMsg(getTenantId(), rpc.getDeviceId(), rpc.getUuidId()); + log.trace("[{}] Forwarding msg {} to queue actor!", rpc.getDeviceId(), rpc); + tbClusterService.pushMsgToCore(removeMsg, null); + } - rpcService.deleteRpc(getTenantId(), rpcId); - rpc.setStatus(RpcStatus.DELETED); + rpcService.deleteRpc(getTenantId(), rpcId); + rpc.setStatus(RpcStatus.DELETED); - TbMsg msg = TbMsg.newMsg(RPC_DELETED, rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc)); - tbClusterService.pushMsgToRuleEngine(getTenantId(), rpc.getDeviceId(), msg, null); - } - } catch (Exception e) { - throw handleException(e); + TbMsg msg = TbMsg.newMsg(RPC_DELETED, rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc)); + tbClusterService.pushMsgToRuleEngine(getTenantId(), rpc.getDeviceId(), msg, null); } } } 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 5c79f9eca7..1d11ad12a6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -157,12 +157,8 @@ public class RuleChainController extends BaseController { @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION) @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); - try { - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - return checkRuleChain(ruleChainId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + return checkRuleChain(ruleChainId, Operation.READ); } @ApiOperation(value = "Get Rule Chain output labels (getRuleChainOutputLabels)", @@ -175,13 +171,9 @@ public class RuleChainController extends BaseController { @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION) @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); - try { - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - checkRuleChain(ruleChainId, Operation.READ); - return tbRuleChainService.getRuleChainOutputLabels(getTenantId(), ruleChainId); - } catch (Exception e) { - throw handleException(e); - } + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + checkRuleChain(ruleChainId, Operation.READ); + return tbRuleChainService.getRuleChainOutputLabels(getTenantId(), ruleChainId); } @ApiOperation(value = "Get output labels usage (getRuleChainOutputLabelsUsage)", @@ -194,13 +186,9 @@ public class RuleChainController extends BaseController { @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION) @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); - try { - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - checkRuleChain(ruleChainId, Operation.READ); - return tbRuleChainService.getOutputLabelUsage(getCurrentUser().getTenantId(), ruleChainId); - } catch (Exception e) { - throw handleException(e); - } + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + checkRuleChain(ruleChainId, Operation.READ); + return tbRuleChainService.getOutputLabelUsage(getCurrentUser().getTenantId(), ruleChainId); } @ApiOperation(value = "Get Rule Chain (getRuleChainById)", @@ -212,13 +200,9 @@ public class RuleChainController extends BaseController { @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION) @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); - try { - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - checkRuleChain(ruleChainId, Operation.READ); - return ruleChainService.loadRuleChainMetaData(getTenantId(), ruleChainId); - } catch (Exception e) { - throw handleException(e); - } + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + checkRuleChain(ruleChainId, Operation.READ); + return ruleChainService.loadRuleChainMetaData(getTenantId(), ruleChainId); } @ApiOperation(value = "Create Or Update Rule Chain (saveRuleChain)", @@ -310,17 +294,13 @@ public class RuleChainController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - RuleChainType type = RuleChainType.CORE; - if (typeStr != null && typeStr.trim().length() > 0) { - type = RuleChainType.valueOf(typeStr); - } - return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, type, pageLink)); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + RuleChainType type = RuleChainType.CORE; + if (typeStr != null && typeStr.trim().length() > 0) { + type = RuleChainType.valueOf(typeStr); } + return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, type, pageLink)); } @ApiOperation(value = "Delete rule chain (deleteRuleChain)", @@ -348,25 +328,21 @@ public class RuleChainController extends BaseController { @ApiParam(value = RULE_NODE_ID_PARAM_DESCRIPTION) @PathVariable(RULE_NODE_ID) String strRuleNodeId) throws ThingsboardException { checkParameter(RULE_NODE_ID, strRuleNodeId); - try { - RuleNodeId ruleNodeId = new RuleNodeId(toUUID(strRuleNodeId)); - checkRuleNode(ruleNodeId, Operation.READ); - TenantId tenantId = getCurrentUser().getTenantId(); - List events = eventService.findLatestEvents(tenantId, ruleNodeId, EventType.DEBUG_RULE_NODE, 2); - JsonNode result = null; - if (events != null) { - for (EventInfo event : events) { - JsonNode body = event.getBody(); - if (body.has("type") && body.get("type").asText().equals("IN")) { - result = body; - break; - } + RuleNodeId ruleNodeId = new RuleNodeId(toUUID(strRuleNodeId)); + checkRuleNode(ruleNodeId, Operation.READ); + TenantId tenantId = getCurrentUser().getTenantId(); + List events = eventService.findLatestEvents(tenantId, ruleNodeId, EventType.DEBUG_RULE_NODE, 2); + JsonNode result = null; + if (events != null) { + for (EventInfo event : events) { + JsonNode body = event.getBody(); + if (body.has("type") && body.get("type").asText().equals("IN")) { + result = body; + break; } } - return result; - } catch (Exception e) { - throw handleException(e); } + return result; } @@ -378,6 +354,9 @@ public class RuleChainController extends BaseController { public JsonNode testScript( @ApiParam(value = "Test JS request. See API call description above.") @RequestBody JsonNode inputParams) throws ThingsboardException { + ScriptEngine engine = null; + String output = ""; + String errorText = ""; try { String script = inputParams.get("script").asText(); String scriptType = inputParams.get("scriptType").asText(); @@ -389,52 +368,44 @@ public class RuleChainController extends BaseController { Map metadata = objectMapper.convertValue(metadataJson, new TypeReference>() { }); String msgType = inputParams.get("msgType").asText(); - String output = ""; - String errorText = ""; - ScriptEngine engine = null; - try { - engine = new RuleNodeJsScriptEngine(getTenantId(), jsInvokeService, getCurrentUser().getId(), script, argNames); - TbMsg inMsg = TbMsg.newMsg(msgType, null, new TbMsgMetaData(metadata), TbMsgDataType.JSON, data); - switch (scriptType) { - case "update": - output = msgToOutput(engine.executeUpdateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS)); - break; - case "generate": - output = msgToOutput(engine.executeGenerateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS)); - break; - case "filter": - boolean result = engine.executeFilterAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); - output = Boolean.toString(result); - break; - case "switch": - Set states = engine.executeSwitchAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); - output = objectMapper.writeValueAsString(states); - break; - case "json": - JsonNode json = engine.executeJsonAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); - output = objectMapper.writeValueAsString(json); - break; - case "string": - output = engine.executeToStringAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); - break; - default: - throw new IllegalArgumentException("Unsupported script type: " + scriptType); - } - } catch (Exception e) { - log.error("Error evaluating JS function", e); - errorText = e.getMessage(); - } finally { - if (engine != null) { - engine.destroy(); - } + engine = new RuleNodeJsScriptEngine(getTenantId(), jsInvokeService, getCurrentUser().getId(), script, argNames); + TbMsg inMsg = TbMsg.newMsg(msgType, null, new TbMsgMetaData(metadata), TbMsgDataType.JSON, data); + switch (scriptType) { + case "update": + output = msgToOutput(engine.executeUpdateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS)); + break; + case "generate": + output = msgToOutput(engine.executeGenerateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS)); + break; + case "filter": + boolean result = engine.executeFilterAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); + output = Boolean.toString(result); + break; + case "switch": + Set states = engine.executeSwitchAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); + output = objectMapper.writeValueAsString(states); + break; + case "json": + JsonNode json = engine.executeJsonAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); + output = objectMapper.writeValueAsString(json); + break; + case "string": + output = engine.executeToStringAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); + break; + default: + throw new IllegalArgumentException("Unsupported script type: " + scriptType); } - ObjectNode result = objectMapper.createObjectNode(); - result.put("output", output); - result.put("error", errorText); - return result; } catch (Exception e) { - throw handleException(e); + log.error("Error evaluating JS function", e); + } finally { + if (engine != null) { + engine.destroy(); + } } + ObjectNode result = objectMapper.createObjectNode(); + result.put("output", output); + result.put("error", errorText); + return result; } @ApiOperation(value = "Export Rule Chains", notes = "Exports all tenant rule chains as one JSON." + TENANT_AUTHORITY_PARAGRAPH) @@ -444,13 +415,9 @@ public class RuleChainController extends BaseController { public RuleChainData exportRuleChains( @ApiParam(value = "A limit of rule chains to export.", required = true) @RequestParam("limit") int limit) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = new PageLink(limit); - return checkNotNull(ruleChainService.exportTenantRuleChains(tenantId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = new PageLink(limit); + return checkNotNull(ruleChainService.exportTenantRuleChains(tenantId, pageLink)); } @ApiOperation(value = "Import Rule Chains", notes = "Imports all tenant rule chains as one JSON." + TENANT_AUTHORITY_PARAGRAPH) @@ -462,19 +429,15 @@ public class RuleChainController extends BaseController { @RequestBody RuleChainData ruleChainData, @ApiParam(value = "Enables overwrite for existing rule chains with the same name.") @RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - List importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite); - for (RuleChainImportResult importResult : importResults) { - if (importResult.getError() == null) { - tbClusterService.broadcastEntityStateChangeEvent(importResult.getTenantId(), importResult.getRuleChainId(), - importResult.isUpdated() ? ComponentLifecycleEvent.UPDATED : ComponentLifecycleEvent.CREATED); - } + TenantId tenantId = getCurrentUser().getTenantId(); + List importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite); + for (RuleChainImportResult importResult : importResults) { + if (importResult.getError() == null) { + tbClusterService.broadcastEntityStateChangeEvent(importResult.getTenantId(), importResult.getRuleChainId(), + importResult.isUpdated() ? ComponentLifecycleEvent.UPDATED : ComponentLifecycleEvent.CREATED); } - return importResults; - } catch (Exception e) { - throw handleException(e); } + return importResults; } private String msgToOutput(TbMsg msg) throws Exception { @@ -572,15 +535,11 @@ public class RuleChainController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - checkEdgeId(edgeId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + checkEdgeId(edgeId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink)); } @ApiOperation(value = "Set Edge Template Root Rule Chain (setEdgeTemplateRootRuleChain)", @@ -632,17 +591,13 @@ public class RuleChainController extends BaseController { @RequestMapping(value = "/ruleChain/autoAssignToEdgeRuleChains", method = RequestMethod.GET) @ResponseBody public List getAutoAssignToEdgeRuleChains() throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - List result = new ArrayList<>(); - PageDataIterableByTenant autoAssignRuleChainsIterator = - new PageDataIterableByTenant<>(ruleChainService::findAutoAssignToEdgeRuleChainsByTenantId, tenantId, DEFAULT_PAGE_SIZE); - for (RuleChain ruleChain : autoAssignRuleChainsIterator) { - result.add(ruleChain); - } - return checkNotNull(result); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + List result = new ArrayList<>(); + PageDataIterableByTenant autoAssignRuleChainsIterator = + new PageDataIterableByTenant<>(ruleChainService::findAutoAssignToEdgeRuleChainsByTenantId, tenantId, DEFAULT_PAGE_SIZE); + for (RuleChain ruleChain : autoAssignRuleChainsIterator) { + result.add(ruleChain); } + return checkNotNull(result); } } 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 ec0b530117..c4415071f0 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -82,20 +82,16 @@ public class TbResourceController extends BaseController { public ResponseEntity downloadResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); - try { - TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); - TbResource tbResource = checkResourceId(resourceId, Operation.READ); + TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); + TbResource tbResource = checkResourceId(resourceId, Operation.READ); - ByteArrayResource resource = new ByteArrayResource(Base64.getDecoder().decode(tbResource.getData().getBytes())); - return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + tbResource.getFileName()) - .header("x-filename", tbResource.getFileName()) - .contentLength(resource.contentLength()) - .contentType(MediaType.APPLICATION_OCTET_STREAM) - .body(resource); - } catch (Exception e) { - throw handleException(e); - } + ByteArrayResource resource = new ByteArrayResource(Base64.getDecoder().decode(tbResource.getData().getBytes())); + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + tbResource.getFileName()) + .header("x-filename", tbResource.getFileName()) + .contentLength(resource.contentLength()) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(resource); } @ApiOperation(value = "Get Resource Info (getResourceInfoById)", @@ -108,12 +104,8 @@ public class TbResourceController extends BaseController { public TbResourceInfo getResourceInfoById(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); - try { - TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); - return checkResourceInfoId(resourceId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); + return checkResourceInfoId(resourceId, Operation.READ); } @ApiOperation(value = "Get Resource (getResourceById)", @@ -126,12 +118,8 @@ public class TbResourceController extends BaseController { public TbResource getResourceById(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); - try { - TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); - return checkResourceId(resourceId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); + return checkResourceId(resourceId, Operation.READ); } @ApiOperation(value = "Create Or Update Resource (saveResource)", @@ -171,15 +159,11 @@ public class TbResourceController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { - return checkNotNull(resourceService.findTenantResourcesByTenantId(getTenantId(), pageLink)); - } else { - return checkNotNull(resourceService.findAllTenantResourcesByTenantId(getTenantId(), pageLink)); - } - } catch (Exception e) { - throw handleException(e); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { + return checkNotNull(resourceService.findTenantResourcesByTenantId(getTenantId(), pageLink)); + } else { + return checkNotNull(resourceService.findAllTenantResourcesByTenantId(getTenantId(), pageLink)); } } @@ -200,12 +184,8 @@ public class TbResourceController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = new PageLink(pageSize, page, textSearch); - return checkNotNull(resourceService.findLwM2mObjectPage(getTenantId(), sortProperty, sortOrder, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = new PageLink(pageSize, page, textSearch); + return checkNotNull(resourceService.findLwM2mObjectPage(getTenantId(), sortProperty, sortOrder, pageLink)); } @ApiOperation(value = "Get LwM2M Objects (getLwm2mListObjects)", @@ -221,11 +201,7 @@ public class TbResourceController extends BaseController { @RequestParam String sortProperty, @ApiParam(value = "LwM2M Object ids.", required = true) @RequestParam(required = false) String[] objectIds) throws ThingsboardException { - try { - return checkNotNull(resourceService.findLwM2mObject(getTenantId(), sortOrder, sortProperty, objectIds)); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(resourceService.findLwM2mObject(getTenantId(), sortOrder, sortProperty, objectIds)); } @ApiOperation(value = "Delete Resource (deleteResource)", 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 b9c929f909..d7e2a13f82 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -185,11 +185,7 @@ public class TelemetryController extends BaseController { public DeferredResult getAttributeKeys( @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException { - try { - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback); - } catch (Exception e) { - throw handleException(e); - } + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback); } @ApiOperation(value = "Get all attribute keys by scope (getAttributeKeysByScope)", @@ -206,12 +202,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope) throws ThingsboardException { - try { - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); - } catch (Exception e) { - throw handleException(e); - } } @ApiOperation(value = "Get attributes (getAttributes)", @@ -229,13 +221,9 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { - try { SecurityUser user = getCurrentUser(); - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); - } catch (Exception e) { - throw handleException(e); - } } @@ -257,13 +245,9 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, + SecurityUser user = getCurrentUser(); + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); - } catch (Exception e) { - throw handleException(e); - } } @ApiOperation(value = "Get time-series keys (getTimeseriesKeys)", @@ -276,12 +260,8 @@ public class TelemetryController extends BaseController { public DeferredResult getTimeseriesKeys( @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException { - try { - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, (result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor())); - } catch (Exception e) { - throw handleException(e); - } } @ApiOperation(value = "Get latest time-series value (getLatestTimeseries)", @@ -306,13 +286,9 @@ public class TelemetryController extends BaseController { @ApiParam(value = TELEMETRY_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr, @ApiParam(value = STRICT_DATA_TYPES_DESCRIPTION) @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, - (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); - } catch (Exception e) { - throw handleException(e); - } + SecurityUser user = getCurrentUser(); + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, + (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); } @ApiOperation(value = "Get time-series data (getTimeseries)", @@ -349,19 +325,15 @@ public class TelemetryController extends BaseController { @RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy, @ApiParam(value = STRICT_DATA_TYPES_DESCRIPTION) @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { - try { - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, - (result, tenantId, entityId) -> { - // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted - Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); - List queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg, orderBy)) - .collect(Collectors.toList()); - - Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor()); - }); - } catch (Exception e) { - throw handleException(e); - } + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, + (result, tenantId, entityId) -> { + // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted + Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); + List queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg, orderBy)) + .collect(Collectors.toList()); + + Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor()); + }); } @ApiOperation(value = "Save device attributes (saveDeviceAttributes)", @@ -385,12 +357,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("deviceId") String deviceIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); - return saveAttributes(getTenantId(), entityId, scope, request); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); + return saveAttributes(getTenantId(), entityId, scope, request); } @ApiOperation(value = "Save entity attributes (saveEntityAttributesV1)", @@ -413,12 +381,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveAttributes(getTenantId(), entityId, scope, request); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveAttributes(getTenantId(), entityId, scope, request); } @ApiOperation(value = "Save entity attributes (saveEntityAttributesV2)", @@ -441,12 +405,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveAttributes(getTenantId(), entityId, scope, request); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveAttributes(getTenantId(), entityId, scope, request); } @@ -470,12 +430,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = TELEMETRY_SCOPE_DESCRIPTION, required = true, allowableValues = "ANY") @PathVariable("scope") String scope, @ApiParam(value = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody String requestBody) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveTelemetry(getTenantId(), entityId, requestBody, 0L); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveTelemetry(getTenantId(), entityId, requestBody, 0L); } @ApiOperation(value = "Save or update time-series data with TTL (saveEntityTelemetryWithTTL)", @@ -500,12 +456,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = TELEMETRY_SCOPE_DESCRIPTION, required = true, allowableValues = "ANY") @PathVariable("scope") String scope, @ApiParam(value = "A long value representing TTL (Time to Live) parameter.", required = true) @PathVariable("ttl") Long ttl, @ApiParam(value = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody String requestBody) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveTelemetry(getTenantId(), entityId, requestBody, ttl); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveTelemetry(getTenantId(), entityId, requestBody, ttl); } @ApiOperation(value = "Delete entity time-series data (deleteEntityTimeseries)", @@ -538,12 +490,8 @@ public class TelemetryController extends BaseController { @RequestParam(name = "endTs", required = false) Long endTs, @ApiParam(value = "If the parameter is set to true, the latest telemetry will be rewritten in case that current latest value was removed, otherwise, in case that parameter is set to false the new latest value will not set.") @RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); } private DeferredResult deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys, @@ -608,12 +556,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(DEVICE_ID) String deviceIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); - return deleteAttributes(entityId, scope, keysStr); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); + return deleteAttributes(entityId, scope, keysStr); } @ApiOperation(value = "Delete entity attributes (deleteEntityAttributes)", @@ -636,12 +580,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return deleteAttributes(entityId, scope, keysStr); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return deleteAttributes(entityId, scope, keysStr); } private DeferredResult deleteAttributes(EntityId entityIdSrc, String scope, String keysStr) throws ThingsboardException { 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 b44c5327cd..7971cd437a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantController.java @@ -79,16 +79,12 @@ public class TenantController extends BaseController { @ApiParam(value = TENANT_ID_PARAM_DESCRIPTION) @PathVariable(TENANT_ID) String strTenantId) throws ThingsboardException { checkParameter(TENANT_ID, strTenantId); - try { - TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); - Tenant tenant = checkTenantId(tenantId, Operation.READ); - if (!tenant.getAdditionalInfo().isNull()) { - processDashboardIdFromAdditionalInfo((ObjectNode) tenant.getAdditionalInfo(), HOME_DASHBOARD); - } - return tenant; - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); + Tenant tenant = checkTenantId(tenantId, Operation.READ); + if (!tenant.getAdditionalInfo().isNull()) { + processDashboardIdFromAdditionalInfo((ObjectNode) tenant.getAdditionalInfo(), HOME_DASHBOARD); } + return tenant; } @ApiOperation(value = "Get Tenant Info (getTenantInfoById)", @@ -101,12 +97,8 @@ public class TenantController extends BaseController { @ApiParam(value = TENANT_ID_PARAM_DESCRIPTION) @PathVariable(TENANT_ID) String strTenantId) throws ThingsboardException { checkParameter(TENANT_ID, strTenantId); - try { - TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); - return checkTenantInfoId(tenantId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); + return checkTenantInfoId(tenantId, Operation.READ); } @ApiOperation(value = "Create Or update Tenant (saveTenant)", @@ -154,12 +146,8 @@ public class TenantController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(tenantService.findTenants(pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(tenantService.findTenants(pageLink)); } @ApiOperation(value = "Get Tenants Info (getTenants)", notes = "Returns a page of tenant info objects registered in the platform. " @@ -179,12 +167,8 @@ public class TenantController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder ) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(tenantService.findTenantInfos(pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(tenantService.findTenantInfos(pageLink)); } } 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 e5a33da948..5b3d721e14 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java @@ -75,12 +75,8 @@ public class TenantProfileController extends BaseController { @ApiParam(value = TENANT_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { checkParameter("tenantProfileId", strTenantProfileId); - try { - TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); - return checkTenantProfileId(tenantProfileId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); + return checkTenantProfileId(tenantProfileId, Operation.READ); } @ApiOperation(value = "Get Tenant Profile Info (getTenantProfileInfoById)", @@ -92,12 +88,8 @@ public class TenantProfileController extends BaseController { @ApiParam(value = TENANT_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { checkParameter("tenantProfileId", strTenantProfileId); - try { - TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); - return checkNotNull(tenantProfileService.findTenantProfileInfoById(getTenantId(), tenantProfileId)); - } catch (Exception e) { - throw handleException(e); - } + TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); + return checkNotNull(tenantProfileService.findTenantProfileInfoById(getTenantId(), tenantProfileId)); } @ApiOperation(value = "Get default Tenant Profile Info (getDefaultTenantProfileInfo)", @@ -106,11 +98,7 @@ public class TenantProfileController extends BaseController { @RequestMapping(value = "/tenantProfileInfo/default", method = RequestMethod.GET) @ResponseBody public EntityInfo getDefaultTenantProfileInfo() throws ThingsboardException { - try { - return checkNotNull(tenantProfileService.findDefaultTenantProfileInfo(getTenantId())); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(tenantProfileService.findDefaultTenantProfileInfo(getTenantId())); } @ApiOperation(value = "Create Or update Tenant Profile (saveTenantProfile)", @@ -171,19 +159,15 @@ public class TenantProfileController extends BaseController { @ResponseBody public TenantProfile saveTenantProfile(@ApiParam(value = "A JSON value representing the tenant profile.") @RequestBody TenantProfile tenantProfile) throws ThingsboardException { - try { - TenantProfile oldProfile; - if (tenantProfile.getId() == null) { - accessControlService.checkPermission(getCurrentUser(), Resource.TENANT_PROFILE, Operation.CREATE); - oldProfile = null; - } else { - oldProfile = checkTenantProfileId(tenantProfile.getId(), Operation.WRITE); - } - - return tbTenantProfileService.save(getTenantId(), tenantProfile, oldProfile); - } catch (Exception e) { - throw handleException(e); + TenantProfile oldProfile; + if (tenantProfile.getId() == null) { + accessControlService.checkPermission(getCurrentUser(), Resource.TENANT_PROFILE, Operation.CREATE); + oldProfile = null; + } else { + oldProfile = checkTenantProfileId(tenantProfile.getId(), Operation.WRITE); } + + return tbTenantProfileService.save(getTenantId(), tenantProfile, oldProfile); } @ApiOperation(value = "Delete Tenant Profile (deleteTenantProfile)", @@ -193,14 +177,10 @@ public class TenantProfileController extends BaseController { @ResponseStatus(value = HttpStatus.OK) public void deleteTenantProfile(@ApiParam(value = TENANT_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { - try { - checkParameter("tenantProfileId", strTenantProfileId); - TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); - TenantProfile profile = checkTenantProfileId(tenantProfileId, Operation.DELETE); - tbTenantProfileService.delete(getTenantId(), profile); - } catch (Exception e) { - throw handleException(e); - } + checkParameter("tenantProfileId", strTenantProfileId); + TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); + TenantProfile profile = checkTenantProfileId(tenantProfileId, Operation.DELETE); + tbTenantProfileService.delete(getTenantId(), profile); } @ApiOperation(value = "Make tenant profile default (setDefaultTenantProfile)", @@ -212,14 +192,10 @@ public class TenantProfileController extends BaseController { @ApiParam(value = TENANT_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { checkParameter("tenantProfileId", strTenantProfileId); - try { - TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); - TenantProfile tenantProfile = checkTenantProfileId(tenantProfileId, Operation.WRITE); - tenantProfileService.setDefaultTenantProfile(getTenantId(), tenantProfileId); - return tenantProfile; - } catch (Exception e) { - throw handleException(e); - } + TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); + TenantProfile tenantProfile = checkTenantProfileId(tenantProfileId, Operation.WRITE); + tenantProfileService.setDefaultTenantProfile(getTenantId(), tenantProfileId); + return tenantProfile; } @ApiOperation(value = "Get Tenant Profiles (getTenantProfiles)", notes = "Returns a page of tenant profiles registered in the platform. " + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH) @@ -237,12 +213,8 @@ public class TenantProfileController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(tenantProfileService.findTenantProfiles(getTenantId(), pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(tenantProfileService.findTenantProfiles(getTenantId(), pageLink)); } @ApiOperation(value = "Get Tenant Profiles Info (getTenantProfileInfos)", notes = "Returns a page of tenant profile info objects registered in the platform. " @@ -261,11 +233,7 @@ public class TenantProfileController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(tenantProfileService.findTenantProfileInfos(getTenantId(), pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(tenantProfileService.findTenantProfileInfos(getTenantId(), pageLink)); } } 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 e96a73b65c..222851389c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -112,22 +112,18 @@ public class UserController extends BaseController { @ApiParam(value = USER_ID_PARAM_DESCRIPTION) @PathVariable(USER_ID) String strUserId) throws ThingsboardException { checkParameter(USER_ID, strUserId); - try { - UserId userId = new UserId(toUUID(strUserId)); - User user = checkUserId(userId, Operation.READ); - if (user.getAdditionalInfo().isObject()) { - ObjectNode additionalInfo = (ObjectNode) user.getAdditionalInfo(); - processDashboardIdFromAdditionalInfo(additionalInfo, DEFAULT_DASHBOARD); - processDashboardIdFromAdditionalInfo(additionalInfo, HOME_DASHBOARD); - UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); - if (userCredentials.isEnabled() && !additionalInfo.has("userCredentialsEnabled")) { - additionalInfo.put("userCredentialsEnabled", true); - } + UserId userId = new UserId(toUUID(strUserId)); + User user = checkUserId(userId, Operation.READ); + if (user.getAdditionalInfo().isObject()) { + ObjectNode additionalInfo = (ObjectNode) user.getAdditionalInfo(); + processDashboardIdFromAdditionalInfo(additionalInfo, DEFAULT_DASHBOARD); + processDashboardIdFromAdditionalInfo(additionalInfo, HOME_DASHBOARD); + UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); + if (userCredentials.isEnabled() && !additionalInfo.has("userCredentialsEnabled")) { + additionalInfo.put("userCredentialsEnabled", true); } - return user; - } catch (Exception e) { - throw handleException(e); } + return user; } @ApiOperation(value = "Check Token Access Enabled (isUserTokenAccessEnabled)", @@ -152,23 +148,19 @@ public class UserController extends BaseController { @ApiParam(value = USER_ID_PARAM_DESCRIPTION) @PathVariable(USER_ID) String strUserId) throws ThingsboardException { checkParameter(USER_ID, strUserId); - try { - if (!userTokenAccessEnabled) { - throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, - ThingsboardErrorCode.PERMISSION_DENIED); - } - UserId userId = new UserId(toUUID(strUserId)); - SecurityUser authUser = getCurrentUser(); - User user = checkUserId(userId, Operation.READ); - UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); - UserCredentials credentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), userId); - SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); - JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); - JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); - return new JwtTokenPair(accessToken.getToken(), refreshToken.getToken()); - } catch (Exception e) { - throw handleException(e); + if (!userTokenAccessEnabled) { + throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, + ThingsboardErrorCode.PERMISSION_DENIED); } + UserId userId = new UserId(toUUID(strUserId)); + SecurityUser authUser = getCurrentUser(); + User user = checkUserId(userId, Operation.READ); + UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); + UserCredentials credentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), userId); + SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); + JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); + JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); + return new JwtTokenPair(accessToken.getToken(), refreshToken.getToken()); } @ApiOperation(value = "Save Or update User (saveUser)", @@ -203,23 +195,19 @@ public class UserController extends BaseController { @ApiParam(value = "Email of the user", required = true) @RequestParam(value = "email") String email, HttpServletRequest request) throws ThingsboardException { - try { - User user = checkNotNull(userService.findUserByEmail(getCurrentUser().getTenantId(), email)); + User user = checkNotNull(userService.findUserByEmail(getCurrentUser().getTenantId(), email)); - accessControlService.checkPermission(getCurrentUser(), Resource.USER, Operation.READ, - user.getId(), user); + accessControlService.checkPermission(getCurrentUser(), Resource.USER, Operation.READ, + user.getId(), user); - UserCredentials userCredentials = userService.findUserCredentialsByUserId(getCurrentUser().getTenantId(), user.getId()); - if (!userCredentials.isEnabled() && userCredentials.getActivateToken() != null) { - String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); - String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, - userCredentials.getActivateToken()); - mailService.sendActivationEmail(activateUrl, email); - } else { - throw new ThingsboardException("User is already activated!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - } catch (Exception e) { - throw handleException(e); + UserCredentials userCredentials = userService.findUserCredentialsByUserId(getCurrentUser().getTenantId(), user.getId()); + if (!userCredentials.isEnabled() && userCredentials.getActivateToken() != null) { + String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); + String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, + userCredentials.getActivateToken()); + mailService.sendActivationEmail(activateUrl, email); + } else { + throw new ThingsboardException("User is already activated!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } } @@ -234,21 +222,17 @@ public class UserController extends BaseController { @PathVariable(USER_ID) String strUserId, HttpServletRequest request) throws ThingsboardException { checkParameter(USER_ID, strUserId); - try { - UserId userId = new UserId(toUUID(strUserId)); - User user = checkUserId(userId, Operation.READ); - SecurityUser authUser = getCurrentUser(); - UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), user.getId()); - if (!userCredentials.isEnabled() && userCredentials.getActivateToken() != null) { - String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); - String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, - userCredentials.getActivateToken()); - return activateUrl; - } else { - throw new ThingsboardException("User is already activated!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - } catch (Exception e) { - throw handleException(e); + UserId userId = new UserId(toUUID(strUserId)); + User user = checkUserId(userId, Operation.READ); + SecurityUser authUser = getCurrentUser(); + UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), user.getId()); + if (!userCredentials.isEnabled() && userCredentials.getActivateToken() != null) { + String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); + String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, + userCredentials.getActivateToken()); + return activateUrl; + } else { + throw new ThingsboardException("User is already activated!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } } @@ -287,16 +271,12 @@ public class UserController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - SecurityUser currentUser = getCurrentUser(); - if (Authority.TENANT_ADMIN.equals(currentUser.getAuthority())) { - return checkNotNull(userService.findUsersByTenantId(currentUser.getTenantId(), pageLink)); - } else { - return checkNotNull(userService.findCustomerUsers(currentUser.getTenantId(), currentUser.getCustomerId(), pageLink)); - } - } catch (Exception e) { - throw handleException(e); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + SecurityUser currentUser = getCurrentUser(); + if (Authority.TENANT_ADMIN.equals(currentUser.getAuthority())) { + return checkNotNull(userService.findUsersByTenantId(currentUser.getTenantId(), pageLink)); + } else { + return checkNotNull(userService.findCustomerUsers(currentUser.getTenantId(), currentUser.getCustomerId(), pageLink)); } } @@ -319,13 +299,9 @@ public class UserController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("tenantId", strTenantId); - try { - TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(userService.findTenantAdmins(tenantId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(userService.findTenantAdmins(tenantId, pageLink)); } @ApiOperation(value = "Get Customer Users (getCustomerUsers)", @@ -347,15 +323,11 @@ public class UserController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(userService.findCustomerUsers(tenantId, customerId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(userService.findCustomerUsers(tenantId, customerId, pageLink)); } @ApiOperation(value = "Enable/Disable User credentials (setUserCredentialsEnabled)", @@ -369,17 +341,13 @@ public class UserController extends BaseController { @ApiParam(value = "Disable (\"true\") or enable (\"false\") the credentials.", defaultValue = "true") @RequestParam(required = false, defaultValue = "true") boolean userCredentialsEnabled) throws ThingsboardException { checkParameter(USER_ID, strUserId); - try { - UserId userId = new UserId(toUUID(strUserId)); - User user = checkUserId(userId, Operation.WRITE); - TenantId tenantId = getCurrentUser().getTenantId(); - userService.setUserCredentialsEnabled(tenantId, userId, userCredentialsEnabled); + UserId userId = new UserId(toUUID(strUserId)); + User user = checkUserId(userId, Operation.WRITE); + TenantId tenantId = getCurrentUser().getTenantId(); + userService.setUserCredentialsEnabled(tenantId, userId, userCredentialsEnabled); - if (!userCredentialsEnabled) { - eventPublisher.publishEvent(new UserAuthDataChangedEvent(userId)); - } - } catch (Exception e) { - throw handleException(e); + if (!userCredentialsEnabled) { + eventPublisher.publishEvent(new UserAuthDataChangedEvent(userId)); } } 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 725d8ca2b4..1be0f9d6e5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java @@ -70,12 +70,8 @@ public class WidgetTypeController extends AutoCommitController { @ApiParam(value = WIDGET_TYPE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("widgetTypeId") String strWidgetTypeId) throws ThingsboardException { checkParameter("widgetTypeId", strWidgetTypeId); - try { - WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); - return checkWidgetTypeId(widgetTypeId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); + return checkWidgetTypeId(widgetTypeId, Operation.READ); } @ApiOperation(value = "Create Or Update Widget Type (saveWidgetType)", @@ -93,32 +89,28 @@ public class WidgetTypeController extends AutoCommitController { @ResponseBody public WidgetTypeDetails saveWidgetType( @ApiParam(value = "A JSON value representing the Widget Type Details.", required = true) - @RequestBody WidgetTypeDetails widgetTypeDetails) throws ThingsboardException { - try { - var currentUser = getCurrentUser(); - if (Authority.SYS_ADMIN.equals(currentUser.getAuthority())) { - widgetTypeDetails.setTenantId(TenantId.SYS_TENANT_ID); - } else { - widgetTypeDetails.setTenantId(currentUser.getTenantId()); - } + @RequestBody WidgetTypeDetails widgetTypeDetails) throws Exception { + var currentUser = getCurrentUser(); + if (Authority.SYS_ADMIN.equals(currentUser.getAuthority())) { + widgetTypeDetails.setTenantId(TenantId.SYS_TENANT_ID); + } else { + widgetTypeDetails.setTenantId(currentUser.getTenantId()); + } - checkEntity(widgetTypeDetails.getId(), widgetTypeDetails, Resource.WIDGET_TYPE); - WidgetTypeDetails savedWidgetTypeDetails = widgetTypeService.saveWidgetType(widgetTypeDetails); + checkEntity(widgetTypeDetails.getId(), widgetTypeDetails, Resource.WIDGET_TYPE); + WidgetTypeDetails savedWidgetTypeDetails = widgetTypeService.saveWidgetType(widgetTypeDetails); - if (!Authority.SYS_ADMIN.equals(currentUser.getAuthority())) { - WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(widgetTypeDetails.getTenantId(), widgetTypeDetails.getBundleAlias()); - if (widgetsBundle != null) { - autoCommit(currentUser, widgetsBundle.getId()); - } + if (!Authority.SYS_ADMIN.equals(currentUser.getAuthority())) { + WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(widgetTypeDetails.getTenantId(), widgetTypeDetails.getBundleAlias()); + if (widgetsBundle != null) { + autoCommit(currentUser, widgetsBundle.getId()); } + } - sendEntityNotificationMsg(getTenantId(), savedWidgetTypeDetails.getId(), - widgetTypeDetails.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); + sendEntityNotificationMsg(getTenantId(), savedWidgetTypeDetails.getId(), + widgetTypeDetails.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); - return checkNotNull(savedWidgetTypeDetails); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(savedWidgetTypeDetails); } @ApiOperation(value = "Delete widget type (deleteWidgetType)", @@ -128,26 +120,21 @@ public class WidgetTypeController extends AutoCommitController { @ResponseStatus(value = HttpStatus.OK) public void deleteWidgetType( @ApiParam(value = WIDGET_TYPE_ID_PARAM_DESCRIPTION, required = true) - @PathVariable("widgetTypeId") String strWidgetTypeId) throws ThingsboardException { + @PathVariable("widgetTypeId") String strWidgetTypeId) throws Exception { checkParameter("widgetTypeId", strWidgetTypeId); - try { - var currentUser = getCurrentUser(); - WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); - WidgetTypeDetails wtd = checkWidgetTypeId(widgetTypeId, Operation.DELETE); - widgetTypeService.deleteWidgetType(currentUser.getTenantId(), widgetTypeId); - - if (wtd != null && !Authority.SYS_ADMIN.equals(currentUser.getAuthority())) { - WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(wtd.getTenantId(), wtd.getBundleAlias()); - if (widgetsBundle != null) { - autoCommit(currentUser, widgetsBundle.getId()); - } + var currentUser = getCurrentUser(); + WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); + WidgetTypeDetails wtd = checkWidgetTypeId(widgetTypeId, Operation.DELETE); + widgetTypeService.deleteWidgetType(currentUser.getTenantId(), widgetTypeId); + + if (wtd != null && !Authority.SYS_ADMIN.equals(currentUser.getAuthority())) { + WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(wtd.getTenantId(), wtd.getBundleAlias()); + if (widgetsBundle != null) { + autoCommit(currentUser, widgetsBundle.getId()); } - - sendEntityNotificationMsg(getTenantId(), widgetTypeId, EdgeEventActionType.DELETED); - - } catch (Exception e) { - throw handleException(e); } + + sendEntityNotificationMsg(getTenantId(), widgetTypeId, EdgeEventActionType.DELETED); } @ApiOperation(value = "Get all Widget types for specified Bundle (getBundleWidgetTypes)", @@ -160,17 +147,13 @@ public class WidgetTypeController extends AutoCommitController { @RequestParam boolean isSystem, @ApiParam(value = "Widget Bundle alias", required = true) @RequestParam String bundleAlias) throws ThingsboardException { - try { - TenantId tenantId; - if (isSystem) { - tenantId = TenantId.SYS_TENANT_ID; - } else { - tenantId = getCurrentUser().getTenantId(); - } - return checkNotNull(widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(tenantId, bundleAlias)); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId; + if (isSystem) { + tenantId = TenantId.SYS_TENANT_ID; + } else { + tenantId = getCurrentUser().getTenantId(); } + return checkNotNull(widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(tenantId, bundleAlias)); } @ApiOperation(value = "Get all Widget types details for specified Bundle (getBundleWidgetTypes)", @@ -183,17 +166,13 @@ public class WidgetTypeController extends AutoCommitController { @RequestParam boolean isSystem, @ApiParam(value = "Widget Bundle alias", required = true) @RequestParam String bundleAlias) throws ThingsboardException { - try { - TenantId tenantId; - if (isSystem) { - tenantId = TenantId.SYS_TENANT_ID; - } else { - tenantId = getCurrentUser().getTenantId(); - } - return checkNotNull(widgetTypeService.findWidgetTypesDetailsByTenantIdAndBundleAlias(tenantId, bundleAlias)); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId; + if (isSystem) { + tenantId = TenantId.SYS_TENANT_ID; + } else { + tenantId = getCurrentUser().getTenantId(); } + return checkNotNull(widgetTypeService.findWidgetTypesDetailsByTenantIdAndBundleAlias(tenantId, bundleAlias)); } @ApiOperation(value = "Get Widget Type Info objects (getBundleWidgetTypesInfos)", @@ -206,17 +185,13 @@ public class WidgetTypeController extends AutoCommitController { @RequestParam boolean isSystem, @ApiParam(value = "Widget Bundle alias", required = true) @RequestParam String bundleAlias) throws ThingsboardException { - try { - TenantId tenantId; - if (isSystem) { - tenantId = TenantId.SYS_TENANT_ID; - } else { - tenantId = getCurrentUser().getTenantId(); - } - return checkNotNull(widgetTypeService.findWidgetTypesInfosByTenantIdAndBundleAlias(tenantId, bundleAlias)); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId; + if (isSystem) { + tenantId = TenantId.SYS_TENANT_ID; + } else { + tenantId = getCurrentUser().getTenantId(); } + return checkNotNull(widgetTypeService.findWidgetTypesInfosByTenantIdAndBundleAlias(tenantId, bundleAlias)); } @ApiOperation(value = "Get Widget Type (getWidgetType)", @@ -231,20 +206,16 @@ public class WidgetTypeController extends AutoCommitController { @RequestParam String bundleAlias, @ApiParam(value = "Widget Type alias", required = true) @RequestParam String alias) throws ThingsboardException { - try { - TenantId tenantId; - if (isSystem) { - tenantId = TenantId.fromUUID(ModelConstants.NULL_UUID); - } else { - tenantId = getCurrentUser().getTenantId(); - } - WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdBundleAliasAndAlias(tenantId, bundleAlias, alias); - checkNotNull(widgetType); - accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, Operation.READ, widgetType.getId(), widgetType); - return widgetType; - } catch (Exception e) { - throw handleException(e); + TenantId tenantId; + if (isSystem) { + tenantId = TenantId.fromUUID(ModelConstants.NULL_UUID); + } else { + tenantId = getCurrentUser().getTenantId(); } + WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdBundleAliasAndAlias(tenantId, bundleAlias, alias); + checkNotNull(widgetType); + accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, Operation.READ, widgetType.getId(), widgetType); + return widgetType; } } 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 23ec498d0e..e5d8708283 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java @@ -74,12 +74,8 @@ public class WidgetsBundleController extends BaseController { @ApiParam(value = WIDGET_BUNDLE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException { checkParameter("widgetsBundleId", strWidgetsBundleId); - try { - WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); - return checkWidgetsBundleId(widgetsBundleId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); + return checkWidgetsBundleId(widgetsBundleId, Operation.READ); } @ApiOperation(value = "Create Or Update Widget Bundle (saveWidgetsBundle)", @@ -141,16 +137,12 @@ public class WidgetsBundleController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { - return checkNotNull(widgetsBundleService.findSystemWidgetsBundlesByPageLink(getTenantId(), pageLink)); - } else { - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { + return checkNotNull(widgetsBundleService.findSystemWidgetsBundlesByPageLink(getTenantId(), pageLink)); + } else { + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, pageLink)); } } @@ -160,15 +152,11 @@ public class WidgetsBundleController extends BaseController { @RequestMapping(value = "/widgetsBundles", method = RequestMethod.GET) @ResponseBody public List getWidgetsBundles() throws ThingsboardException { - try { - if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { - return checkNotNull(widgetsBundleService.findSystemWidgetsBundles(getTenantId())); - } else { - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantId(tenantId)); - } - } catch (Exception e) { - throw handleException(e); + if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { + return checkNotNull(widgetsBundleService.findSystemWidgetsBundles(getTenantId())); + } else { + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantId(tenantId)); } } From fa934f647e71e2ac84f3c53ba9ffc2326b238dcc Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 20 Oct 2022 12:54:20 +0300 Subject: [PATCH 009/488] Initial notification system structures and dao layer --- .../NotificationTargetController.java | 56 +++++++++++ .../DefaultNotificationProcessingService.java | 98 +++++++++++++++++++ .../NotificationProcessingService.java | 25 +++++ .../service/security/permission/Resource.java | 3 +- .../AbstractSubscriptionService.java | 2 +- .../DefaultAlarmSubscriptionService.java | 2 +- .../dao/notification/NotificationService.java | 39 ++++++++ .../NotificationTargetService.java | 37 +++++++ .../server/common/data/id/NotificationId.java | 27 +++++ .../common/data/id/NotificationRequestId.java | 27 +++++ .../common/data/id/NotificationTargetId.java | 27 +++++ .../data/notification/Notification.java | 51 ++++++++++ .../notification/NotificationRequest.java | 53 ++++++++++ .../notification/NotificationSettings.java | 20 ++++ .../notification/NotificationSeverity.java | 22 +++++ .../data/notification/NotificationStatus.java | 21 ++++ .../NonConfirmedNotificationEscalation.java | 23 +++++ .../notification/rule/NotificationRule.java | 38 +++++++ .../targets/NotificationTarget.java | 39 ++++++++ .../targets/NotificationTargetConfig.java | 32 ++++++ .../targets/NotificationTargetConfigType.java | 23 +++++ .../SingleUserNotificationTargetConfig.java | 31 ++++++ .../server/dao/model/BaseSqlEntity.java | 19 ++++ .../server/dao/model/ModelConstants.java | 9 ++ .../dao/model/sql/NotificationEntity.java | 29 ++++++ .../model/sql/NotificationRequestEntity.java | 29 ++++++ .../model/sql/NotificationTargetEntity.java | 77 +++++++++++++++ .../DefaultNotificationService.java | 76 ++++++++++++++ .../DefaultNotificationTargetService.java | 75 ++++++++++++++ .../dao/notification/NotificationDao.java | 22 +++++ .../notification/NotificationRequestDao.java | 22 +++++ .../notification/NotificationTargetDao.java | 22 +++++ .../sql/notification/JpaNotificationDao.java | 46 +++++++++ .../JpaNotificationRequestDao.java | 46 +++++++++ .../JpaNotificationTargetDao.java | 46 +++++++++ .../notification/NotificationRepository.java | 26 +++++ .../NotificationRequestRepository.java | 26 +++++ .../NotificationTargetRepository.java | 26 +++++ 38 files changed, 1289 insertions(+), 3 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java create mode 100644 application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationId.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRequestId.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationTargetId.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationSettings.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationSeverity.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationStatus.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NonConfirmedNotificationEscalation.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/SingleUserNotificationTargetConfig.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java new file mode 100644 index 0000000000..220b4f2be2 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.dao.notification.NotificationTargetService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.model.SecurityUser; + +@RestController +@TbCoreComponent +@RequestMapping("/api/notification") +@RequiredArgsConstructor +@Slf4j +public class NotificationTargetController extends BaseController { + + private final NotificationTargetService notificationTargetService; + + @PostMapping("/target") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + public NotificationTarget saveNotificationTarget(NotificationTarget notificationTarget) throws Exception { + SecurityUser user = getCurrentUser(); + // fixme: permission check, log action + return notificationTargetService.saveNotificationTarget(user.getTenantId(), notificationTarget); + } + + @GetMapping("/targets") + public PageData getNotificationTargets(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException { +// notificationTargetService.findNotificationTargetsByTenantIdAndPageLink() + return null; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java new file mode 100644 index 0000000000..3aa74862a6 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java @@ -0,0 +1,98 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.notification; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.dao.notification.NotificationService; +import org.thingsboard.server.dao.notification.NotificationTargetService; +import org.thingsboard.server.dao.user.UserService; +import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.service.executors.DbCallbackExecutorService; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.permission.AccessControlService; +import org.thingsboard.server.service.telemetry.AbstractSubscriptionService; + +import java.util.List; + +@Service +@Slf4j +public class DefaultNotificationProcessingService extends AbstractSubscriptionService implements NotificationProcessingService { + + private final NotificationTargetService notificationTargetService; + private final NotificationService notificationService; + private final UserService userService; + private final AccessControlService accessControlService; + private final DbCallbackExecutorService dbCallbackExecutorService; + + public DefaultNotificationProcessingService(TbClusterService clusterService, PartitionService partitionService, + NotificationTargetService notificationTargetService, + NotificationService notificationService, UserService userService, + AccessControlService accessControlService, + DbCallbackExecutorService dbCallbackExecutorService) { + super(clusterService, partitionService); + this.notificationTargetService = notificationTargetService; + this.notificationService = notificationService; + this.userService = userService; + this.accessControlService = accessControlService; + this.dbCallbackExecutorService = dbCallbackExecutorService; + } + + @Override + public void processNotificationRequest(SecurityUser user, NotificationRequest notificationRequest) { + TenantId tenantId = user.getTenantId(); + notificationRequest = notificationService.createNotificationRequest(tenantId, notificationRequest); + + List recipients = notificationTargetService.findRecipientsForNotificationTarget(tenantId, notificationRequest.getTargetId()); + for (UserId recipientId : recipients) { + try { + // todo: check read permission for recipientId + Notification notification = Notification.builder() + .tenantId(tenantId) + .requestId(notificationRequest.getId()) + .status(null) + .recipientId(recipientId) + .text(formatNotificationText(notificationRequest.getTextTemplate(), null)) + .severity(notificationRequest.getSeverity()) + .senderId(notificationRequest.getSenderId()) + .build(); + notification = notificationService.createNotification(tenantId, notification); + onNewNotification(notification); + } catch (Exception e) { + // fixme: handle + } + } + + } + + private void onNewNotification(Notification notification) { + } + + private String formatNotificationText(String template, Object context) { + return template; + } + + @Override + protected String getExecutorPrefix() { + return "notification"; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java new file mode 100644 index 0000000000..d094c5e4b4 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.notification; + +import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.service.security.model.SecurityUser; + +public interface NotificationProcessingService { + + void processNotificationRequest(SecurityUser user, NotificationRequest notificationRequest); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index a9484a2bd3..7f8ea8e158 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -43,7 +43,8 @@ public enum Resource { EDGE(EntityType.EDGE), RPC(EntityType.RPC), QUEUE(EntityType.QUEUE), - VERSION_CONTROL; + VERSION_CONTROL, + NOTIFICATION; private final EntityType entityType; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java index 7645465b19..b207de6a9c 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java @@ -64,7 +64,7 @@ public abstract class AbstractSubscriptionService extends TbApplicationEventList this.subscriptionManagerService = subscriptionManagerService; } - abstract String getExecutorPrefix(); + protected abstract String getExecutorPrefix(); @PostConstruct public void initExecutor() { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java index 4dc0dc238b..25f412da20 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java @@ -81,7 +81,7 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService } @Override - String getExecutorPrefix() { + protected String getExecutorPrefix() { return "alarm"; } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java new file mode 100644 index 0000000000..3dfca4ae36 --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.notification; + +import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationStatus; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; + +public interface NotificationService { + + NotificationRequest createNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest); + + PageData findNotificationRequestsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + + Notification createNotification(TenantId tenantId, Notification notification); + + void updateNotificationStatus(TenantId tenantId, NotificationId notificationId, NotificationStatus status); + + PageData findNotificationsByUserIdAndPageLink(TenantId tenantId, UserId userId, PageLink pageLink); + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java new file mode 100644 index 0000000000..52b757191b --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.notification; + +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; + +import java.util.List; + +public interface NotificationTargetService { + + NotificationTarget saveNotificationTarget(TenantId tenantId, NotificationTarget notificationTarget); + + NotificationTarget findNotificationTargetById(TenantId tenantId, NotificationTargetId id); + + PageData findNotificationTargetsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + + List findRecipientsForNotificationTarget(TenantId tenantId, NotificationTargetId notificationTargetId); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationId.java new file mode 100644 index 0000000000..a52bfdd9d1 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationId.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.id; + +import com.fasterxml.jackson.annotation.JsonCreator; + +import java.util.UUID; + +public class NotificationId extends UUIDBased { + @JsonCreator + public NotificationId(UUID id) { + super(id); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRequestId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRequestId.java new file mode 100644 index 0000000000..cffea0f6f5 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRequestId.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.id; + +import com.fasterxml.jackson.annotation.JsonCreator; + +import java.util.UUID; + +public class NotificationRequestId extends UUIDBased { + @JsonCreator + public NotificationRequestId(UUID id) { + super(id); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationTargetId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationTargetId.java new file mode 100644 index 0000000000..3d8dddfe0e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationTargetId.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.id; + +import com.fasterxml.jackson.annotation.JsonCreator; + +import java.util.UUID; + +public class NotificationTargetId extends UUIDBased { + @JsonCreator + public NotificationTargetId(UUID id) { + super(id); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java new file mode 100644 index 0000000000..3e9c88d45e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java @@ -0,0 +1,51 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.SearchTextBased; +import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; + +import java.util.UUID; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@EqualsAndHashCode(callSuper = true) +public class Notification extends SearchTextBased { + + private NotificationRequestId requestId; + private TenantId tenantId; + private UserId recipientId; + private String text; + private NotificationSeverity severity; + private NotificationStatus status; + private UserId senderId; + + @Override + public String getSearchText() { + return text; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java new file mode 100644 index 0000000000..3b3b2c527e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java @@ -0,0 +1,53 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.SearchTextBased; +import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; + +import java.util.UUID; + +@Data +@EqualsAndHashCode(callSuper = true) +public class NotificationRequest extends SearchTextBased { + + private NotificationTargetId targetId; + private String textTemplate; // html with params? + private Object notificationType; // ALARM, ADMIN, + private Object notificationInfo; // for alarms: alarm details, link to dashboard etc. + private NotificationSeverity severity; + private TenantId tenantId; + private UserId senderId; + + @Override + public String getSearchText() { + return textTemplate; + } + + // todo: scheduling + +} + +/* +* NotificationService - manages NotificationRequest and Notification entities +* NotificationTargetService - manages NotificationTarget +* */ \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationSettings.java new file mode 100644 index 0000000000..2632d1b76e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationSettings.java @@ -0,0 +1,20 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification; + +public class NotificationSettings { + // location on the screen, shown notifications count, timings of displaying +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationSeverity.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationSeverity.java new file mode 100644 index 0000000000..17f28bee4b --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationSeverity.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification; + +public enum NotificationSeverity { + NORMAL, + CRITICAL, + URGENT // send pop-up error +} 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 new file mode 100644 index 0000000000..14008c34bb --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationStatus.java @@ -0,0 +1,21 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification; + +public enum NotificationStatus { + DELIVERED, + READ +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NonConfirmedNotificationEscalation.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NonConfirmedNotificationEscalation.java new file mode 100644 index 0000000000..befc666281 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NonConfirmedNotificationEscalation.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule; + +import java.util.UUID; + +public class NonConfirmedNotificationEscalation { + private long delayMs; // delay since initial notification request // if no one from previous escalation item has read the notification, send notifications after this time to other recipients + private UUID notificationTargetId; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java new file mode 100644 index 0000000000..340a49c54d --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule; + +import lombok.Data; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@Data +public class NotificationRule { + + // we may choose it in the alarm rule config, or maybe it's better to configure evrth in the triggers? + + private UUID id; // NotificationRuleId id; + private String name; + private Map triggers; // or maybe bad idea + // Map - concrete alarmRule or alarm rule search (e.g. alarm rule of device profiles of particular transport type with certain severity) + // triggerConfiguration (??) - alarm filter: severity, specific device profile, alarm rule + + private UUID initialNotificationTargetId; + private List escalations; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java new file mode 100644 index 0000000000..0048f24be5 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.targets; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.SearchTextBased; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +@EqualsAndHashCode(callSuper = true) +public class NotificationTarget extends SearchTextBased implements HasTenantId, HasName { + + private TenantId tenantId; + private String name; + private NotificationTargetConfig configuration; + + @Override + public String getSearchText() { + return name; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java new file mode 100644 index 0000000000..366f78ef6e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.targets; + +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; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @Type(value = SingleUserNotificationTargetConfig.class, name = "SINGLE_USER") +}) +public interface NotificationTargetConfig { + + NotificationTargetConfigType getType(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java new file mode 100644 index 0000000000..8bc6d04c56 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.targets; + +public enum NotificationTargetConfigType { + SINGLE_USER, + USER_GROUP, + USERS_WITH_ROLE, + QUERY // ? +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/SingleUserNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/SingleUserNotificationTargetConfig.java new file mode 100644 index 0000000000..cf0b4316d0 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/SingleUserNotificationTargetConfig.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.targets; + +import lombok.Data; +import org.thingsboard.server.common.data.id.UserId; + +@Data +public class SingleUserNotificationTargetConfig implements NotificationTargetConfig { + + private UserId userId; + + @Override + public NotificationTargetConfigType getType() { + return NotificationTargetConfigType.SINGLE_USER; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java index bb801766ab..6e8a4a7076 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java @@ -16,11 +16,13 @@ package org.thingsboard.server.dao.model; import lombok.Data; +import org.thingsboard.server.common.data.id.UUIDBased; import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import java.util.UUID; +import java.util.function.Function; /** * Created by ashvayka on 13.07.17. @@ -56,4 +58,21 @@ public abstract class BaseSqlEntity implements BaseEntity { this.createdTime = createdTime; } } + + protected static UUID getUuid(UUIDBased uuidBased) { + if (uuidBased != null) { + return uuidBased.getId(); + } else { + return null; + } + } + + protected static I createId(UUID uuid, Function creator) { + if (uuid != null) { + return creator.apply(uuid); + } else { + return null; + } + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index e24bde91fb..4ebcaaf873 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -43,6 +43,7 @@ public class ModelConstants { public static final String CUSTOMER_ID_PROPERTY = "customer_id"; public static final String DEVICE_ID_PROPERTY = "device_id"; public static final String TITLE_PROPERTY = "title"; + public static final String NAME_PROPERTY = "name"; public static final String ALIAS_PROPERTY = "alias"; public static final String SEARCH_TEXT_PROPERTY = "search_text"; public static final String ADDITIONAL_INFO_PROPERTY = "additional_info"; @@ -644,6 +645,14 @@ public class ModelConstants { public static final String QUEUE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; + /** + * Notification constants + * */ + + public static final String NOTIFICATION_TARGET_TABLE_NAME = "notification_target"; + public static final String NOTIFICATION_TARGET_CONFIGURATION_PROPERTY = "configuration"; + + protected static final String[] NONE_AGGREGATION_COLUMNS = new String[]{LONG_VALUE_COLUMN, DOUBLE_VALUE_COLUMN, BOOLEAN_VALUE_COLUMN, STRING_VALUE_COLUMN, JSON_VALUE_COLUMN, KEY_COLUMN, TS_COLUMN}; protected static final String[] COUNT_AGGREGATION_COLUMNS = new String[]{count(LONG_VALUE_COLUMN), count(DOUBLE_VALUE_COLUMN), count(BOOLEAN_VALUE_COLUMN), count(STRING_VALUE_COLUMN), count(JSON_VALUE_COLUMN), max(TS_COLUMN)}; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java new file mode 100644 index 0000000000..782600c742 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.model.sql; + +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.dao.model.BaseSqlEntity; + +import javax.persistence.Entity; + +@Entity +public class NotificationEntity extends BaseSqlEntity { + @Override + public Notification toData() { + return null; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java new file mode 100644 index 0000000000..6ba482dd5e --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.model.sql; + +import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.dao.model.BaseSqlEntity; + +import javax.persistence.Entity; + +@Entity +public class NotificationRequestEntity extends BaseSqlEntity { + @Override + public NotificationRequest toData() { + return null; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java new file mode 100644 index 0000000000..6cbb8c93c9 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java @@ -0,0 +1,77 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.model.sql; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.util.mapping.JsonStringType; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.UUID; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = ModelConstants.NOTIFICATION_TARGET_TABLE_NAME) +public class NotificationTargetEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.TENANT_ID_PROPERTY) + private UUID tenantId; + + @Column(name = ModelConstants.NAME_PROPERTY) + private String name; + + @Type(type = "json") + @Column(name = ModelConstants.NOTIFICATION_TARGET_CONFIGURATION_PROPERTY) + private JsonNode configuration; + + public NotificationTargetEntity() {} + + public NotificationTargetEntity(NotificationTarget notificationTarget) { + setId(notificationTarget.getUuidId()); + setCreatedTime(notificationTarget.getCreatedTime()); + setTenantId(getUuid(notificationTarget.getTenantId())); + setName(notificationTarget.getName()); + setConfiguration(JacksonUtil.valueToTree(notificationTarget.getConfiguration())); + } + + @Override + public NotificationTarget toData() { + NotificationTarget notificationTarget = new NotificationTarget(); + notificationTarget.setId(new NotificationTargetId(id)); + notificationTarget.setCreatedTime(createdTime); + notificationTarget.setTenantId(createId(tenantId, TenantId::fromUUID)); + notificationTarget.setName(name); + if (configuration != null) { + notificationTarget.setConfiguration(JacksonUtil.treeToValue(configuration, NotificationTargetConfig.class)); + } + return notificationTarget; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java new file mode 100644 index 0000000000..23e7149866 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java @@ -0,0 +1,76 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.notification; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationStatus; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.service.DataValidator; + +@Service +@Slf4j +@RequiredArgsConstructor +public class DefaultNotificationService implements NotificationService { + + private final NotificationRequestDao notificationRequestDao; + private final NotificationDao notificationDao; + private final NotificationTargetService notificationTargetService; + private final NotificationRequestValidator notificationRequestValidator = new NotificationRequestValidator(); + + @Override + public NotificationRequest createNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { + if (notificationRequest.getId() != null) { + throw new IllegalArgumentException(); + } + notificationRequestValidator.validate(notificationRequest, NotificationRequest::getTenantId); + return notificationRequestDao.save(tenantId, notificationRequest); + } + + @Override + public PageData findNotificationRequestsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { + return null; + } + + @Override + public Notification createNotification(TenantId tenantId, Notification notification) { + if (notification.getId() != null) { + throw new IllegalArgumentException(); + } + return notificationDao.save(tenantId, notification); + } + + @Override + public void updateNotificationStatus(TenantId tenantId, NotificationId notificationId, NotificationStatus status) { + + } + + @Override + public PageData findNotificationsByUserIdAndPageLink(TenantId tenantId, UserId userId, PageLink pageLink) { + return null; + } + + private static class NotificationRequestValidator extends DataValidator { + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java new file mode 100644 index 0000000000..6e8548d233 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java @@ -0,0 +1,75 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.notification; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.SingleUserNotificationTargetConfig; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.service.DataValidator; + +import java.util.ArrayList; +import java.util.List; + +@Service +@Slf4j +@RequiredArgsConstructor +public class DefaultNotificationTargetService implements NotificationTargetService { + + private final NotificationTargetDao notificationTargetDao; + private final NotificationTargetValidator validator = new NotificationTargetValidator(); + + @Override + public NotificationTarget saveNotificationTarget(TenantId tenantId, NotificationTarget notificationTarget) { + validator.validate(notificationTarget, NotificationTarget::getTenantId); + return notificationTargetDao.save(tenantId, notificationTarget); + } + + @Override + public NotificationTarget findNotificationTargetById(TenantId tenantId, NotificationTargetId id) { + return null; + } + + @Override + public PageData findNotificationTargetsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { + return null; + } + + @Override + public List findRecipientsForNotificationTarget(TenantId tenantId, NotificationTargetId notificationTargetId) { + NotificationTarget notificationTarget = findNotificationTargetById(tenantId, notificationTargetId); + NotificationTargetConfig configuration = notificationTarget.getConfiguration(); + List recipients = new ArrayList<>(); + switch (configuration.getType()) { + case SINGLE_USER: + SingleUserNotificationTargetConfig singleUserNotificationTargetConfig = (SingleUserNotificationTargetConfig) configuration; + recipients.add(singleUserNotificationTargetConfig.getUserId()); + break; + } + return recipients; + } + + private static class NotificationTargetValidator extends DataValidator { + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java new file mode 100644 index 0000000000..f7d8013f3e --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.notification; + +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.dao.Dao; + +public interface NotificationDao extends Dao { +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java new file mode 100644 index 0000000000..c93ca57beb --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.notification; + +import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.dao.Dao; + +public interface NotificationRequestDao extends Dao { +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java new file mode 100644 index 0000000000..07101f60ee --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.notification; + +import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.dao.Dao; + +public interface NotificationTargetDao extends Dao { +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java new file mode 100644 index 0000000000..909456e765 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.notification; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.dao.model.sql.NotificationEntity; +import org.thingsboard.server.dao.notification.NotificationDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.UUID; + +@Component +@SqlDao +@RequiredArgsConstructor +public class JpaNotificationDao extends JpaAbstractDao implements NotificationDao { + + private final NotificationRepository notificationRepository; + + @Override + protected Class getEntityClass() { + return NotificationEntity.class; + } + + @Override + protected JpaRepository getRepository() { + return notificationRepository; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java new file mode 100644 index 0000000000..6b6119b871 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.notification; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.dao.model.sql.NotificationRequestEntity; +import org.thingsboard.server.dao.notification.NotificationRequestDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.UUID; + +@Component +@SqlDao +@RequiredArgsConstructor +public class JpaNotificationRequestDao extends JpaAbstractDao implements NotificationRequestDao { + + private final NotificationRequestRepository notificationRequestRepository; + + @Override + protected Class getEntityClass() { + return NotificationRequestEntity.class; + } + + @Override + protected JpaRepository getRepository() { + return notificationRequestRepository; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java new file mode 100644 index 0000000000..e22f342272 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.notification; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.dao.model.sql.NotificationTargetEntity; +import org.thingsboard.server.dao.notification.NotificationTargetDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.UUID; + +@Component +@SqlDao +@RequiredArgsConstructor +public class JpaNotificationTargetDao extends JpaAbstractDao implements NotificationTargetDao { + + private final NotificationTargetRepository notificationTargetRepository; + + @Override + protected Class getEntityClass() { + return NotificationTargetEntity.class; + } + + @Override + protected JpaRepository getRepository() { + return notificationTargetRepository; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java new file mode 100644 index 0000000000..db8727f167 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.notification; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.thingsboard.server.dao.model.sql.NotificationEntity; + +import java.util.UUID; + +@Repository +public interface NotificationRepository extends JpaRepository { +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java new file mode 100644 index 0000000000..370e434c14 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.notification; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.thingsboard.server.dao.model.sql.NotificationRequestEntity; + +import java.util.UUID; + +@Repository +public interface NotificationRequestRepository extends JpaRepository { +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java new file mode 100644 index 0000000000..e3a4260102 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.notification; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.thingsboard.server.dao.model.sql.NotificationTargetEntity; + +import java.util.UUID; + +@Repository +public interface NotificationTargetRepository extends JpaRepository { +} From 515646da6756d69a9b187aa6805ee826cba43281 Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Mon, 24 Oct 2022 11:38:08 +0300 Subject: [PATCH 010/488] merge w/ master and refactoring --- .../system/widget_bundles/input_widgets.json | 3 +- .../lib/multiple-input-widget.component.html | 35 ++++++++++++++ .../lib/multiple-input-widget.component.ts | 48 +++++++++++++++++-- ...ple-attributes-key-settings.component.html | 3 ++ ...tiple-attributes-key-settings.component.ts | 6 ++- .../json-object-edit-dialog.component.html | 4 +- .../json-object-edit-dialog.component.ts | 6 +++ ui-ngx/src/app/shared/shared.module.ts | 1 + .../assets/locale/locale.constant-en_US.json | 2 + 9 files changed, 99 insertions(+), 9 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/input_widgets.json b/application/src/main/data/json/system/widget_bundles/input_widgets.json index 3c9a9fe718..f21c059119 100644 --- a/application/src/main/data/json/system/widget_bundles/input_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/input_widgets.json @@ -324,9 +324,8 @@ "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [$scope.validators.minLength(settings.minLength),\n $scope.validators.maxLength(settings.maxLength)];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n \n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n \n var value = $scope.attributeUpdateFormGroup.get('currentValue').value;\n \n if (!$scope.attributeUpdateFormGroup.get('currentValue').value.length) {\n value = null;\n }\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", - "settingsSchema": "", + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesTableSettings\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showLabel\":{\n \"title\":\"Show label\",\n \"type\":\"boolean\",\n \"default\":true\n },\n \"labelValue\": {\n \"title\": \"Label\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"requiredErrorMessage\": {\n \"title\": \"'Required' error message\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"maxLength\": {\n \"title\": \"Max length\",\n \"type\": \"number\",\n \"default\": \"\"\n },\n \"minLength\": {\n \"title\": \"Min length\",\n \"type\": \"number\",\n \"default\": \"\"\n },\n \"showResultMessage\":{\n \"title\":\"Show result message\",\n \"type\":\"boolean\",\n \"default\":true\n },\n \"isRequired\":{\n \"title\":\"Required\",\n \"type\":\"boolean\",\n \"default\":true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n \"showResultMessage\",\n \"showLabel\",\n \"isRequired\",\n \"labelValue\",\n \"requiredErrorMessage\",\n \"maxLength\",\n \"minLength\"\n ]\n}", "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-string-attribute-widget-settings", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showResultMessage\":true,\"showLabel\":true,\"isRequired\":true},\"title\":\"Update server string attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" } }, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.html index 550f7cde49..54de73fc66 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.html @@ -85,6 +85,41 @@ {{key.label}} +
+ + {{key.label}} + + + {{key.settings.icon}} + + icon + + + + + {{ getErrorMessageText(key.settings,'required') }} + + + {{ 'value.json-value-invalid' | translate }} + + +
(JsonObjectEditDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + jsonValue: formControl.value, + title: key.settings.dialogLabel, + saveLabel: key.settings.saveButtonLabel, + undoLabel: key.settings.saveButtonLabel + } + }).afterClosed().subscribe( + (res) => { + if (!isEqual(res, formControl.value)) { + formControl.patchValue(res); + formControl.markAsDirty(); + if(!this.settings.showActionButtons) { + this.inputChanged(source, key); + } + } + } + ); + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.html index 22823ba778..b3e205ac9e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.html @@ -48,6 +48,9 @@ {{ 'widgets.input-widgets.datakey-value-type-integer' | translate }} + + {{ 'widgets.input-widgets.datakey-value-type-json' | translate }} + {{ 'widgets.input-widgets.datakey-value-type-boolean-checkbox' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts index 4884166be5..f4a4f36a34 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts @@ -24,6 +24,7 @@ import { dataKeySelectOptionValidator } from '@home/components/widget/lib/settings/input/datakey-select-option.component'; import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { MultipleInputWidgetDataKeyValueType } from '@home/components/widget/lib/multiple-input-widget.component'; @Component({ selector: 'tb-update-multiple-attributes-key-settings', @@ -130,7 +131,8 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings protected updateValidators(emitEvent: boolean) { const dataKeyHidden: boolean = this.updateMultipleAttributesKeySettingsForm.get('dataKeyHidden').value; - const dataKeyValueType: string = this.updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value; + + const dataKeyValueType: MultipleInputWidgetDataKeyValueType = this.updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value; const required: boolean = this.updateMultipleAttributesKeySettingsForm.get('required').value; const isEditable: string = this.updateMultipleAttributesKeySettingsForm.get('isEditable').value; const useCustomIcon: boolean = this.updateMultipleAttributesKeySettingsForm.get('useCustomIcon').value; @@ -244,7 +246,7 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings const dataKeyHidden: boolean = this.updateMultipleAttributesKeySettingsForm.get('dataKeyHidden').value; const required: boolean = this.updateMultipleAttributesKeySettingsForm.get('required').value; const dataKeyValueType: string = this.updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value; - return !dataKeyHidden && (required || (['integer', 'double', 'dateTime', 'date', 'time'].includes(dataKeyValueType))); + return !dataKeyHidden && (required || (['integer', 'double', 'dateTime', 'date', 'time', 'JSON'].includes(dataKeyValueType))); } } diff --git a/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.html b/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.html index b7949d59b7..4f91113c79 100644 --- a/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.html +++ b/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.html @@ -45,12 +45,12 @@ type="button" [disabled]="(isLoading$ | async)" (click)="cancel()" cdkFocusInitial> - {{ 'action.cancel' | translate }} + {{ undoButtonLabel }}
diff --git a/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.ts b/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.ts index 3f4672ffb0..673bdddd78 100644 --- a/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.ts @@ -26,6 +26,8 @@ import { TranslateService } from '@ngx-translate/core'; export interface JsonObjectEditDialogData { jsonValue: object; title?: string; + saveLabel?: string; + undoLabel?: string; } @Component({ @@ -37,6 +39,8 @@ export class JsonObjectEditDialogComponent extends DialogComponent Date: Mon, 24 Oct 2022 12:18:09 +0300 Subject: [PATCH 011/488] minor corrections --- .../main/data/json/system/widget_bundles/input_widgets.json | 3 ++- .../components/widget/lib/multiple-input-widget.component.ts | 1 - .../input/update-multiple-attributes-key-settings.component.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/input_widgets.json b/application/src/main/data/json/system/widget_bundles/input_widgets.json index f21c059119..3c9a9fe718 100644 --- a/application/src/main/data/json/system/widget_bundles/input_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/input_widgets.json @@ -324,8 +324,9 @@ "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [$scope.validators.minLength(settings.minLength),\n $scope.validators.maxLength(settings.maxLength)];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n \n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n \n var value = $scope.attributeUpdateFormGroup.get('currentValue').value;\n \n if (!$scope.attributeUpdateFormGroup.get('currentValue').value.length) {\n value = null;\n }\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesTableSettings\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showLabel\":{\n \"title\":\"Show label\",\n \"type\":\"boolean\",\n \"default\":true\n },\n \"labelValue\": {\n \"title\": \"Label\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"requiredErrorMessage\": {\n \"title\": \"'Required' error message\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"maxLength\": {\n \"title\": \"Max length\",\n \"type\": \"number\",\n \"default\": \"\"\n },\n \"minLength\": {\n \"title\": \"Min length\",\n \"type\": \"number\",\n \"default\": \"\"\n },\n \"showResultMessage\":{\n \"title\":\"Show result message\",\n \"type\":\"boolean\",\n \"default\":true\n },\n \"isRequired\":{\n \"title\":\"Required\",\n \"type\":\"boolean\",\n \"default\":true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n \"showResultMessage\",\n \"showLabel\",\n \"isRequired\",\n \"labelValue\",\n \"requiredErrorMessage\",\n \"maxLength\",\n \"minLength\"\n ]\n}", + "settingsSchema": "", "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-string-attribute-widget-settings", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showResultMessage\":true,\"showLabel\":true,\"isRequired\":true},\"title\":\"Update server string attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" } }, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts index 1a1a85c599..b0d6aa58dd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts @@ -91,7 +91,6 @@ interface MultipleInputWidgetDataKeySettings { maxValue?: number; requiredErrorMessage?: string; invalidDateErrorMessage?: string; - invalidJSONErrorMessage?: string; minValueErrorMessage?: string; maxValueErrorMessage?: string; useCustomIcon: boolean; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts index f4a4f36a34..2d9ca8db35 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts @@ -131,7 +131,6 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings protected updateValidators(emitEvent: boolean) { const dataKeyHidden: boolean = this.updateMultipleAttributesKeySettingsForm.get('dataKeyHidden').value; - const dataKeyValueType: MultipleInputWidgetDataKeyValueType = this.updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value; const required: boolean = this.updateMultipleAttributesKeySettingsForm.get('required').value; const isEditable: string = this.updateMultipleAttributesKeySettingsForm.get('isEditable').value; From f5247386fa6bec142d50546ec435d43f5466810e Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 26 Oct 2022 17:50:33 +0300 Subject: [PATCH 012/488] Notification system improvements and refactoring --- .../controller/NotificationController.java | 104 ++++++++++++++++++ .../NotificationTargetController.java | 67 +++++++++-- .../DefaultNotificationProcessingService.java | 72 ++++++++---- .../NotificationProcessingService.java | 3 +- .../src/main/resources/thingsboard.yml | 2 + .../dao/notification/NotificationService.java | 5 +- .../NotificationTargetService.java | 2 + .../data/notification/Notification.java | 7 +- .../data/notification/NotificationInfo.java | 37 +++++++ .../notification/NotificationRequest.java | 46 ++++---- .../NotificationRequestConfig.java | 23 ++++ .../data/notification/NotificationStatus.java | 1 + .../targets/NotificationTarget.java | 9 +- .../targets/NotificationTargetConfig.java | 3 +- .../targets/NotificationTargetConfigType.java | 8 +- .../UserListNotificationTargetConfig.java | 33 ++++++ .../server/dao/model/ModelConstants.java | 15 +++ .../dao/model/sql/NotificationEntity.java | 54 ++++++++- .../model/sql/NotificationRequestEntity.java | 81 +++++++++++++- .../model/sql/NotificationTargetEntity.java | 1 + .../DefaultNotificationService.java | 46 ++++++-- .../DefaultNotificationTargetService.java | 15 ++- .../dao/notification/NotificationDao.java | 13 +++ .../notification/NotificationRequestDao.java | 6 + .../notification/NotificationTargetDao.java | 6 + .../sql/notification/JpaNotificationDao.java | 44 ++++++++ .../JpaNotificationRequestDao.java | 11 ++ .../JpaNotificationTargetDao.java | 11 ++ .../notification/NotificationRepository.java | 17 +++ .../NotificationRequestRepository.java | 11 ++ .../NotificationTargetRepository.java | 7 ++ 31 files changed, 681 insertions(+), 79 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/controller/NotificationController.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationInfo.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/UserListNotificationTargetConfig.java diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java new file mode 100644 index 0000000000..454a8422e5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -0,0 +1,104 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationStatus; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.notification.NotificationService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.notification.NotificationProcessingService; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.security.permission.Resource; + +import java.util.UUID; + +@RestController +@TbCoreComponent +@RequestMapping("/api") +@RequiredArgsConstructor +@Slf4j +public class NotificationController extends BaseController { + + private final NotificationService notificationService; + private final NotificationProcessingService notificationProcessingService; + + @GetMapping("/notifications") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + public PageData getNotifications(@RequestParam int pageSize, + @RequestParam int page, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String sortProperty, + @RequestParam(required = false) String sortOrder, + @RequestParam(defaultValue = "false") boolean unreadOnly, + @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return notificationService.findNotificationsByUserIdAndReadStatusAndPageLink(user.getTenantId(), user.getId(), unreadOnly, pageLink); + } + + @PutMapping("/notification/{id}/read") // or maybe to NotificationUpdateRequest for the future + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + public void markNotificationAsRead(@PathVariable UUID id, + @AuthenticationPrincipal SecurityUser user) { + NotificationId notificationId = new NotificationId(id); + notificationService.updateNotificationStatus(user.getTenantId(), notificationId, NotificationStatus.READ); + } + + + @PostMapping("/notification/request") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public NotificationRequest createNotificationRequest(@RequestBody NotificationRequest notificationRequest, + @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { + accessControlService.checkPermission(user, Resource.NOTIFICATION, Operation.CREATE); + return notificationProcessingService.processNotificationRequest(user, notificationRequest); + } + + @GetMapping("/notification/requests") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public PageData getNotificationRequests(@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 { + accessControlService.checkPermission(user, Resource.NOTIFICATION, Operation.CREATE); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return notificationService.findNotificationRequestsByTenantIdAndPageLink(user.getTenantId(), pageLink); + } + + // delete request and sent notifications + public void deleteNotificationRequest() { + + } + +} 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 220b4f2be2..471b72e015 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java @@ -19,16 +19,32 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; +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.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.notification.NotificationTargetService; +import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.permission.Operation; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; @RestController @TbCoreComponent @@ -36,21 +52,58 @@ import org.thingsboard.server.service.security.model.SecurityUser; @RequiredArgsConstructor @Slf4j public class NotificationTargetController extends BaseController { + // fixme: permission check, log action, events private final NotificationTargetService notificationTargetService; + private final UserService userService; @PostMapping("/target") - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - public NotificationTarget saveNotificationTarget(NotificationTarget notificationTarget) throws Exception { - SecurityUser user = getCurrentUser(); - // fixme: permission check, log action + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public NotificationTarget saveNotificationTarget(@RequestBody NotificationTarget notificationTarget, + @AuthenticationPrincipal SecurityUser user) { return notificationTargetService.saveNotificationTarget(user.getTenantId(), notificationTarget); } + @GetMapping("/target/{id}") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public NotificationTarget getNotificationTargetById(@PathVariable UUID id, + @AuthenticationPrincipal SecurityUser user) { + NotificationTargetId notificationTargetId = new NotificationTargetId(id); + return notificationTargetService.findNotificationTargetById(user.getTenantId(), notificationTargetId); + } + + @GetMapping("/target/{id}/recipients") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public List getRecipientsForNotificationTarget(@PathVariable UUID id, + @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { + NotificationTargetId notificationTargetId = new NotificationTargetId(id); + // fixme: to page data + // todo: check read permission for recipients + List recipients = new ArrayList<>(); + for (UserId userId : notificationTargetService.findRecipientsForNotificationTarget(user.getTenantId(), notificationTargetId)) { + recipients.add(checkUserId(userId, Operation.READ)); + } + return recipients; + } + @GetMapping("/targets") - public PageData getNotificationTargets(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException { -// notificationTargetService.findNotificationTargetsByTenantIdAndPageLink() - return null; + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public PageData getNotificationTargets(@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 { + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return notificationTargetService.findNotificationTargetsByTenantIdAndPageLink(user.getTenantId(), pageLink); + } + + @DeleteMapping("/target/{id}") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public void deleteNotificationTarget(@PathVariable UUID id, + @AuthenticationPrincipal SecurityUser user) { + NotificationTargetId notificationTargetId = new NotificationTargetId(id); + notificationTargetService.deleteNotificationTarget(user.getTenantId(), notificationTargetId); } } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java index 3aa74862a6..04bc45b9d4 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java @@ -17,11 +17,15 @@ package org.thingsboard.server.service.notification; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.user.UserService; @@ -29,9 +33,13 @@ import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.AccessControlService; +import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.telemetry.AbstractSubscriptionService; +import java.util.ArrayList; import java.util.List; +import java.util.Map; @Service @Slf4j @@ -57,37 +65,57 @@ public class DefaultNotificationProcessingService extends AbstractSubscriptionSe } @Override - public void processNotificationRequest(SecurityUser user, NotificationRequest notificationRequest) { + public NotificationRequest processNotificationRequest(SecurityUser user, NotificationRequest notificationRequest) throws ThingsboardException { TenantId tenantId = user.getTenantId(); - notificationRequest = notificationService.createNotificationRequest(tenantId, notificationRequest); - - List recipients = notificationTargetService.findRecipientsForNotificationTarget(tenantId, notificationRequest.getTargetId()); - for (UserId recipientId : recipients) { - try { - // todo: check read permission for recipientId - Notification notification = Notification.builder() - .tenantId(tenantId) - .requestId(notificationRequest.getId()) - .status(null) - .recipientId(recipientId) - .text(formatNotificationText(notificationRequest.getTextTemplate(), null)) - .severity(notificationRequest.getSeverity()) - .senderId(notificationRequest.getSenderId()) - .build(); - notification = notificationService.createNotification(tenantId, notification); + List recipientsIds = notificationTargetService.findRecipientsForNotificationTarget(tenantId, notificationRequest.getTargetId()); + List recipients = new ArrayList<>(); + for (UserId recipientId : recipientsIds) { + User recipient = userService.findUserById(tenantId, recipientId); // todo: add caching + accessControlService.checkPermission(user, Resource.USER, Operation.READ, recipientId, recipient); + recipients.add(recipient); + } + + notificationRequest.setTenantId(tenantId); + notificationRequest.setSenderId(user.getId()); + NotificationRequest savedNotificationRequest = notificationService.createNotificationRequest(tenantId, notificationRequest); + + // todo: delayed sending; check all delayed notification requests on start up, schedule send + + for (User recipient : recipients) { + dbCallbackExecutorService.submit(() -> { + Notification notification = createNotification(recipient, notificationRequest); onNewNotification(notification); - } catch (Exception e) { - // fixme: handle - } + }); } + return savedNotificationRequest; + } + + private Notification createNotification(User recipient, NotificationRequest notificationRequest) { + Notification notification = Notification.builder() + .requestId(notificationRequest.getId()) + .recipientId(recipient.getId()) + .text(formatNotificationText(notificationRequest.getTextTemplate(), recipient)) + .severity(notificationRequest.getNotificationSeverity()) + .status(NotificationStatus.SENT) + .build(); + notification = notificationService.createNotification(recipient.getTenantId(), notification); + return notification; } private void onNewNotification(Notification notification) { + wsCallBackExecutor.submit(() -> { + + }) } - private String formatNotificationText(String template, Object context) { - return template; + private String formatNotificationText(String template, User recipient) { + Map context = Map.of( + "recipientEmail", recipient.getEmail(), + "recipientFirstName", recipient.getFirstName(), + "recipientLastName", recipient.getLastName() + ); + return TbNodeUtils.processTemplate(template, context); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java index d094c5e4b4..1293ba999d 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java @@ -15,11 +15,12 @@ */ package org.thingsboard.server.service.notification; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.service.security.model.SecurityUser; public interface NotificationProcessingService { - void processNotificationRequest(SecurityUser user, NotificationRequest notificationRequest); + NotificationRequest processNotificationRequest(SecurityUser user, NotificationRequest notificationRequest) throws ThingsboardException; } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index f5ac40afd5..87e5487a7c 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -267,6 +267,8 @@ sql: stats_print_interval_ms: "${SQL_EDGE_EVENTS_BATCH_STATS_PRINT_MS:10000}" audit_logs: partition_size: "${SQL_AUDIT_LOGS_PARTITION_SIZE_HOURS:168}" # Default value - 1 week + notifications: + partition_size: "${SQL_NOTIFICATIONS_PARTITION_SIZE_HOURS:168}" # Specify whether to sort entities before batch update. Should be enabled for cluster mode to avoid deadlocks batch_sort: "${SQL_BATCH_SORT:false}" # Specify whether to remove null characters from strValue of attributes and timeseries before insert diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java index 3dfca4ae36..a582758639 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java @@ -30,10 +30,13 @@ public interface NotificationService { PageData findNotificationRequestsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + Notification createNotification(TenantId tenantId, Notification notification); void updateNotificationStatus(TenantId tenantId, NotificationId notificationId, NotificationStatus status); - PageData findNotificationsByUserIdAndPageLink(TenantId tenantId, UserId userId, PageLink pageLink); + PageData findNotificationsByUserIdAndReadStatusAndPageLink(TenantId tenantId, UserId userId, boolean unreadOnly, PageLink pageLink); + + PageData findLatestUnreadNotificationsByUserId(TenantId tenantId, UserId userId, int limit); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java index 52b757191b..f6cdf780e5 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java @@ -34,4 +34,6 @@ public interface NotificationTargetService { List findRecipientsForNotificationTarget(TenantId tenantId, NotificationTargetId notificationTargetId); + void deleteNotificationTarget(TenantId tenantId, NotificationTargetId notificationTargetId); + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java index 3e9c88d45e..02f0fb2aa4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java @@ -23,12 +23,8 @@ import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.SearchTextBased; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; -import org.thingsboard.server.common.data.id.NotificationTargetId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; -import java.util.UUID; - @Data @AllArgsConstructor @NoArgsConstructor @@ -37,12 +33,11 @@ import java.util.UUID; public class Notification extends SearchTextBased { private NotificationRequestId requestId; - private TenantId tenantId; private UserId recipientId; private String text; private NotificationSeverity severity; private NotificationStatus status; - private UserId senderId; +// private UserId senderId; @Override public String getSearchText() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationInfo.java new file mode 100644 index 0000000000..4831e056cd --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationInfo.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.Data; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.validation.NoXss; + + +@Data +//@JsonIgnoreProperties(ignoreUnknown = true) +//@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "notificationType", visible = true, defaultImpl = NotificationInfo.class) +//@JsonSubTypes({ +// @Type(name = "ALARM", value = DeviceExportData.class), +//}) +public class NotificationInfo { + @NoXss + private String description; + + private ObjectNode alarmDetails; // move to child class + private DashboardId dashboardId; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java index 3b3b2c527e..392063ad93 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java @@ -15,39 +15,45 @@ */ package org.thingsboard.server.common.data.notification; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.SearchTextBased; -import org.thingsboard.server.common.data.id.NotificationId; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.validation.NoXss; -import java.util.UUID; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; @Data @EqualsAndHashCode(callSuper = true) -public class NotificationRequest extends SearchTextBased { +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class NotificationRequest extends BaseData implements HasTenantId { - private NotificationTargetId targetId; - private String textTemplate; // html with params? - private Object notificationType; // ALARM, ADMIN, - private Object notificationInfo; // for alarms: alarm details, link to dashboard etc. - private NotificationSeverity severity; private TenantId tenantId; + @NotNull(message = "Target is not specified") + private NotificationTargetId targetId; + @NoXss + private String notificationReason; // "Alarm", "Scheduled event". "General" by default + // @NoXss + @NotBlank(message = "Notification text template is missing") + private String textTemplate; + @Valid + private NotificationInfo notificationInfo; + private NotificationSeverity notificationSeverity; + private NotificationRequestConfig additionalConfig; private UserId senderId; - @Override - public String getSearchText() { - return textTemplate; - } - - // todo: scheduling + public static final String GENERAL_NOTIFICATION_REASON = "General"; + public static final String ALARM_NOTIFICATION_REASON = "Alarm"; } - -/* -* NotificationService - manages NotificationRequest and Notification entities -* NotificationTargetService - manages NotificationTarget -* */ \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java new file mode 100644 index 0000000000..8f65427f21 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification; + +import lombok.Data; + +@Data +public class NotificationRequestConfig { + private Long sendingDelayMs; +} 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 14008c34bb..7e504cbe15 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 @@ -16,6 +16,7 @@ package org.thingsboard.server.common.data.notification; public enum NotificationStatus { + SENT, DELIVERED, READ } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java index 0048f24be5..957a6d359f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java @@ -17,23 +17,18 @@ package org.thingsboard.server.common.data.notification.targets; import lombok.Data; import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; -import org.thingsboard.server.common.data.SearchTextBased; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; @Data @EqualsAndHashCode(callSuper = true) -public class NotificationTarget extends SearchTextBased implements HasTenantId, HasName { +public class NotificationTarget extends BaseData implements HasTenantId, HasName { private TenantId tenantId; private String name; private NotificationTargetConfig configuration; - @Override - public String getSearchText() { - return name; - } - } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java index 366f78ef6e..0623bf07b9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java @@ -23,7 +23,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ - @Type(value = SingleUserNotificationTargetConfig.class, name = "SINGLE_USER") + @Type(value = SingleUserNotificationTargetConfig.class, name = "SINGLE_USER"), + @Type(value = UserListNotificationTargetConfig.class, name = "USER_LIST") }) public interface NotificationTargetConfig { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java index 8bc6d04c56..2413eb9326 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java @@ -17,7 +17,9 @@ package org.thingsboard.server.common.data.notification.targets; public enum NotificationTargetConfigType { SINGLE_USER, - USER_GROUP, - USERS_WITH_ROLE, - QUERY // ? + USER_LIST, + +// USER_GROUP, +// USERS_WITH_ROLE, +// QUERY // ? } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/UserListNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/UserListNotificationTargetConfig.java new file mode 100644 index 0000000000..22bc26703d --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/UserListNotificationTargetConfig.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.targets; + +import lombok.Data; +import org.thingsboard.server.common.data.id.UserId; + +import java.util.List; + +@Data +public class UserListNotificationTargetConfig implements NotificationTargetConfig { + + private List usersIds; + + @Override + public NotificationTargetConfigType getType() { + return NotificationTargetConfigType.USER_LIST; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 4ebcaaf873..5c938de98c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -652,6 +652,21 @@ public class ModelConstants { public static final String NOTIFICATION_TARGET_TABLE_NAME = "notification_target"; public static final String NOTIFICATION_TARGET_CONFIGURATION_PROPERTY = "configuration"; + public static final String NOTIFICATION_TABLE_NAME = "notification"; + public static final String NOTIFICATION_REQUEST_ID_PROPERTY = "request_id"; + public static final String NOTIFICATION_RECIPIENT_ID_PROPERTY = "recipient_id"; + public static final String NOTIFICATION_TEXT_PROPERTY = "text"; + public static final String NOTIFICATION_SEVERITY_PROPERTY = "severity"; + public static final String NOTIFICATION_STATUS_PROPERTY = "status"; + + public static final String NOTIFICATION_REQUEST_TABLE_NAME = "notification_request"; + public static final String NOTIFICATION_REQUEST_TARGET_ID_PROPERTY = "target_id"; + public static final String NOTIFICATION_REQUEST_TEXT_TEMPLATE_PROPERTY = "text_template"; + public static final String NOTIFICATION_REQUEST_NOTIFICATION_REASON_PROPERTY = "notification_reason"; + public static final String NOTIFICATION_REQUEST_NOTIFICATION_INFO_PROPERTY = "notification_info"; + public static final String NOTIFICATION_REQUEST_NOTIFICATION_SEVERITY_PROPERTY = "notification_severity"; + public static final String NOTIFICATION_REQUEST_ADDITIONAL_CONFIG_PROPERTY = "additional_config"; + public static final String NOTIFICATION_REQUEST_SENDER_ID_PROPERTY = "sender_id"; protected static final String[] NONE_AGGREGATION_COLUMNS = new String[]{LONG_VALUE_COLUMN, DOUBLE_VALUE_COLUMN, BOOLEAN_VALUE_COLUMN, STRING_VALUE_COLUMN, JSON_VALUE_COLUMN, KEY_COLUMN, TS_COLUMN}; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java index 782600c742..ad7c506d4b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java @@ -15,15 +15,67 @@ */ package org.thingsboard.server.dao.model.sql; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationSeverity; +import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; +import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; +import java.util.UUID; +@Data +@EqualsAndHashCode(callSuper = true) @Entity +@Table(name = ModelConstants.NOTIFICATION_TABLE_NAME) public class NotificationEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.NOTIFICATION_REQUEST_ID_PROPERTY) + private UUID requestId; + + @Column(name = ModelConstants.NOTIFICATION_RECIPIENT_ID_PROPERTY) + private UUID recipientId; + + @Column(name = ModelConstants.NOTIFICATION_TEXT_PROPERTY) + private String text; + + @Column(name = ModelConstants.NOTIFICATION_SEVERITY_PROPERTY) + private NotificationSeverity severity; + + @Enumerated(EnumType.STRING) + @Column(name = ModelConstants.NOTIFICATION_STATUS_PROPERTY) + private NotificationStatus status; + + public NotificationEntity() {} + + public NotificationEntity(Notification notification) { + setId(notification.getUuidId()); + setCreatedTime(notification.getCreatedTime()); + setRequestId(getUuid(notification.getRequestId())); + setRecipientId(getUuid(notification.getRecipientId())); + setText(notification.getText()); + setStatus(notification.getStatus()); + } + @Override public Notification toData() { - return null; + Notification notification = new Notification(); + notification.setId(new NotificationId(id)); + notification.setCreatedTime(createdTime); + notification.setRequestId(createId(requestId, NotificationRequestId::new)); + notification.setRecipientId(createId(recipientId, UserId::new)); + notification.setText(text); + notification.setStatus(status); + return notification; } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java index 6ba482dd5e..23a8ae1059 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java @@ -15,15 +15,94 @@ */ package org.thingsboard.server.dao.model.sql; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.NotificationInfo; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationRequestConfig; +import org.thingsboard.server.common.data.notification.NotificationSeverity; import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.util.mapping.JsonStringType; +import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; +import java.util.UUID; +@Data +@EqualsAndHashCode(callSuper = true) @Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = ModelConstants.NOTIFICATION_REQUEST_TABLE_NAME) public class NotificationRequestEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.TENANT_ID_PROPERTY) + private UUID tenantId; + + @Column(name = ModelConstants.NOTIFICATION_REQUEST_TARGET_ID_PROPERTY) + private UUID targetId; + + @Column(name = ModelConstants.NOTIFICATION_REQUEST_NOTIFICATION_REASON_PROPERTY) + private String notificationReason; + + @Column(name = ModelConstants.NOTIFICATION_REQUEST_TEXT_TEMPLATE_PROPERTY) + private String textTemplate; + + @Type(type = "json") + @Column(name = ModelConstants.NOTIFICATION_REQUEST_NOTIFICATION_INFO_PROPERTY) + private JsonNode notificationInfo; + + @Enumerated(EnumType.STRING) + @Column(name = ModelConstants.NOTIFICATION_REQUEST_NOTIFICATION_SEVERITY_PROPERTY) + private NotificationSeverity notificationSeverity; + + @Type(type = "json") + @Column(name = ModelConstants.NOTIFICATION_REQUEST_ADDITIONAL_CONFIG_PROPERTY) + private JsonNode additionalConfig; + + @Column(name = ModelConstants.NOTIFICATION_REQUEST_SENDER_ID_PROPERTY) + private UUID senderId; + + public NotificationRequestEntity() {} + + public NotificationRequestEntity(NotificationRequest notificationRequest) { + setId(notificationRequest.getUuidId()); + setCreatedTime(notificationRequest.getCreatedTime()); + setTenantId(getUuid(notificationRequest.getTenantId())); + setTargetId(getUuid(notificationRequest.getTargetId())); + setNotificationReason(notificationRequest.getNotificationReason()); + setTextTemplate(notificationRequest.getTextTemplate()); + setNotificationInfo(JacksonUtil.valueToTree(notificationRequest.getNotificationInfo())); + setNotificationSeverity(notificationRequest.getNotificationSeverity()); + setAdditionalConfig(JacksonUtil.valueToTree(notificationRequest.getAdditionalConfig())); + setSenderId(getUuid(notificationRequest.getSenderId())); + } + @Override public NotificationRequest toData() { - return null; + NotificationRequest notificationRequest = new NotificationRequest(); + notificationRequest.setId(new NotificationRequestId(id)); + notificationRequest.setCreatedTime(createdTime); + notificationRequest.setTenantId(createId(tenantId, TenantId::new)); + notificationRequest.setTargetId(createId(targetId, NotificationTargetId::new)); + notificationRequest.setNotificationReason(notificationReason); + notificationRequest.setTextTemplate(textTemplate); + notificationRequest.setNotificationInfo(JacksonUtil.treeToValue(notificationInfo, NotificationInfo.class)); + notificationRequest.setNotificationSeverity(notificationSeverity); + notificationRequest.setAdditionalConfig(JacksonUtil.treeToValue(additionalConfig, NotificationRequestConfig.class)); + notificationRequest.setSenderId(createId(senderId, UserId::new)); + return notificationRequest; } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java index 6cbb8c93c9..70d51833a4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.notification.targets.NotificationTarge import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.model.SearchTextEntity; import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java index 23e7149866..8b1b994b17 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java @@ -17,16 +17,21 @@ package org.thingsboard.server.dao.notification; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationSeverity; import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.SortOrder; +import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.sql.query.EntityKeyMapping; @Service @Slf4j @@ -35,13 +40,16 @@ public class DefaultNotificationService implements NotificationService { private final NotificationRequestDao notificationRequestDao; private final NotificationDao notificationDao; - private final NotificationTargetService notificationTargetService; + private final NotificationRequestValidator notificationRequestValidator = new NotificationRequestValidator(); @Override public NotificationRequest createNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { - if (notificationRequest.getId() != null) { - throw new IllegalArgumentException(); + if (StringUtils.isBlank(notificationRequest.getNotificationReason())) { + notificationRequest.setNotificationReason(NotificationRequest.GENERAL_NOTIFICATION_REASON); + } + if (notificationRequest.getNotificationSeverity() == null) { + notificationRequest.setNotificationSeverity(NotificationSeverity.NORMAL); } notificationRequestValidator.validate(notificationRequest, NotificationRequest::getTenantId); return notificationRequestDao.save(tenantId, notificationRequest); @@ -49,28 +57,52 @@ public class DefaultNotificationService implements NotificationService { @Override public PageData findNotificationRequestsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { - return null; + return notificationRequestDao.findByTenantIdAndPageLink(tenantId, pageLink); } @Override public Notification createNotification(TenantId tenantId, Notification notification) { if (notification.getId() != null) { - throw new IllegalArgumentException(); + throw new DataValidationException("Notification cannot be updated"); // tmp ? } return notificationDao.save(tenantId, notification); } @Override public void updateNotificationStatus(TenantId tenantId, NotificationId notificationId, NotificationStatus status) { + notificationDao.updateStatus(tenantId, notificationId, status); + } + @Override + public PageData findNotificationsByUserIdAndReadStatusAndPageLink(TenantId tenantId, UserId userId, boolean unreadOnly, PageLink pageLink) { + if (unreadOnly) { + return notificationDao.findUnreadByUserIdAndPageLink(tenantId, userId, pageLink); + } else { + return notificationDao.findByUserIdAndPageLink(tenantId, userId, pageLink); + } } @Override - public PageData findNotificationsByUserIdAndPageLink(TenantId tenantId, UserId userId, PageLink pageLink) { - return null; + public PageData findLatestUnreadNotificationsByUserId(TenantId tenantId, UserId userId, int limit) { + SortOrder sortOrder = new SortOrder(EntityKeyMapping.CREATED_TIME, SortOrder.Direction.DESC); + PageLink pageLink = new PageLink(limit, 0, null, sortOrder); + return findNotificationsByUserIdAndReadStatusAndPageLink(tenantId, userId, true, pageLink); } private static class NotificationRequestValidator extends DataValidator { + + @Override + protected void validateDataImpl(TenantId tenantId, NotificationRequest notificationRequest) { + if (notificationRequest.getId() != null) { + throw new DataValidationException("Notification request cannot be changed once created"); + } + if (notificationRequest.getSenderId() != null) { + if (notificationRequest.getNotificationReason().equalsIgnoreCase(NotificationRequest.ALARM_NOTIFICATION_REASON)) { + throw new DataValidationException("'Alarm' notification reason is for internal usage"); + } + } + } + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java index 6e8548d233..a7d5393d43 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.SingleUserNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.UserListNotificationTargetConfig; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.service.DataValidator; @@ -47,12 +48,12 @@ public class DefaultNotificationTargetService implements NotificationTargetServi @Override public NotificationTarget findNotificationTargetById(TenantId tenantId, NotificationTargetId id) { - return null; + return notificationTargetDao.findById(tenantId, id.getId()); } @Override public PageData findNotificationTargetsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { - return null; + return notificationTargetDao.findByTenantIdAndPageLink(tenantId, pageLink); } @Override @@ -65,10 +66,20 @@ public class DefaultNotificationTargetService implements NotificationTargetServi SingleUserNotificationTargetConfig singleUserNotificationTargetConfig = (SingleUserNotificationTargetConfig) configuration; recipients.add(singleUserNotificationTargetConfig.getUserId()); break; + case USER_LIST: + UserListNotificationTargetConfig userListNotificationTargetConfig = (UserListNotificationTargetConfig) configuration; + recipients.addAll(userListNotificationTargetConfig.getUsersIds()); + break; } return recipients; } + @Override + public void deleteNotificationTarget(TenantId tenantId, NotificationTargetId notificationTargetId) { + notificationTargetDao.removeById(tenantId, notificationTargetId.getId()); + // todo: delete related notification requests (?) + } + private static class NotificationTargetValidator extends DataValidator { } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java index f7d8013f3e..b088d75c35 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java @@ -15,8 +15,21 @@ */ package org.thingsboard.server.dao.notification; +import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationStatus; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; public interface NotificationDao extends Dao { + + PageData findUnreadByUserIdAndPageLink(TenantId tenantId, UserId userId, PageLink pageLink); + + PageData findByUserIdAndPageLink(TenantId tenantId, UserId userId, PageLink pageLink); + + void updateStatus(TenantId tenantId, NotificationId notificationId, NotificationStatus status); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java index c93ca57beb..d518a4e245 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java @@ -15,8 +15,14 @@ */ package org.thingsboard.server.dao.notification; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; public interface NotificationRequestDao extends Dao { + + PageData findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java index 07101f60ee..0bcae5c456 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java @@ -15,8 +15,14 @@ */ package org.thingsboard.server.dao.notification; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; public interface NotificationTargetDao extends Dao { + + PageData findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java index 909456e765..bb9c30a71b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java @@ -15,16 +15,28 @@ */ package org.thingsboard.server.dao.sql.notification; +import com.datastax.oss.driver.api.core.uuid.Uuids; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationStatus; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.sql.NotificationEntity; import org.thingsboard.server.dao.notification.NotificationDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.sqlts.insert.sql.SqlPartitioningRepository; import org.thingsboard.server.dao.util.SqlDao; import java.util.UUID; +import java.util.concurrent.TimeUnit; @Component @SqlDao @@ -32,6 +44,38 @@ import java.util.UUID; public class JpaNotificationDao extends JpaAbstractDao implements NotificationDao { private final NotificationRepository notificationRepository; + private final SqlPartitioningRepository partitioningRepository; + + @Value("${sql.notifications.partition_size:168}") + private int partitionSizeInHours; + + @Override + public Notification save(TenantId tenantId, Notification notification) { + if (notification.getId() == null) { + UUID uuid = Uuids.timeBased(); + notification.setId(new NotificationId(uuid)); + notification.setCreatedTime(Uuids.unixTimestamp(uuid)); + // todo: regarding ttl, it might be better to remove notifications on NotificationRequest level + partitioningRepository.createPartitionIfNotExists(ModelConstants.NOTIFICATION_TABLE_NAME, + notification.getCreatedTime(), TimeUnit.HOURS.toMillis(partitionSizeInHours)); + } + return super.save(tenantId, notification); + } + + @Override + public PageData findUnreadByUserIdAndPageLink(TenantId tenantId, UserId userId, PageLink pageLink) { + return DaoUtil.toPageData(notificationRepository.findByRecipientIdAndStatusNot(userId.getId(), NotificationStatus.READ, DaoUtil.toPageable(pageLink))); + } + + @Override + public PageData findByUserIdAndPageLink(TenantId tenantId, UserId userId, PageLink pageLink) { + return DaoUtil.toPageData(notificationRepository.findByRecipientId(userId.getId(), DaoUtil.toPageable(pageLink))); + } + + @Override + public void updateStatus(TenantId tenantId, NotificationId notificationId, NotificationStatus status) { + notificationRepository.updateStatus(notificationId.getId(), status); + } @Override protected Class getEntityClass() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java index 6b6119b871..099218abcc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java @@ -15,10 +15,15 @@ */ package org.thingsboard.server.dao.sql.notification; +import com.google.common.base.Strings; import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.NotificationRequestEntity; import org.thingsboard.server.dao.notification.NotificationRequestDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; @@ -33,6 +38,12 @@ public class JpaNotificationRequestDao extends JpaAbstractDao findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { + return DaoUtil.toPageData(notificationRequestRepository.findByTenantIdAndSearchText(tenantId.getId(), + Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); + } + @Override protected Class getEntityClass() { return NotificationRequestEntity.class; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java index e22f342272..26b9bfed76 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java @@ -15,10 +15,15 @@ */ package org.thingsboard.server.dao.sql.notification; +import com.google.common.base.Strings; import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.NotificationTargetEntity; import org.thingsboard.server.dao.notification.NotificationTargetDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; @@ -33,6 +38,12 @@ public class JpaNotificationTargetDao extends JpaAbstractDao findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { + return DaoUtil.toPageData(notificationTargetRepository.findByTenantIdAndNameContainingIgnoreCase(tenantId.getId(), + Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); + } + @Override protected Class getEntityClass() { return NotificationTargetEntity.class; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java index db8727f167..9ee5b07121 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java @@ -15,12 +15,29 @@ */ package org.thingsboard.server.dao.sql.notification; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.dao.model.sql.NotificationEntity; import java.util.UUID; @Repository public interface NotificationRepository extends JpaRepository { + + Page findByRecipientIdAndStatusNot(UUID recipientId, NotificationStatus status, Pageable pageable); + + Page findByRecipientId(UUID recipientId, Pageable pageable); + + @Modifying + @Transactional + @Query("UPDATE NotificationEntity n SET n.status = :status WHERE n.id = :id") + void updateStatus(@Param("id") UUID id, @Param("status") NotificationStatus status); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java index 370e434c14..1303c99034 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java @@ -15,7 +15,11 @@ */ package org.thingsboard.server.dao.sql.notification; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.thingsboard.server.dao.model.sql.NotificationRequestEntity; @@ -23,4 +27,11 @@ import java.util.UUID; @Repository public interface NotificationRequestRepository extends JpaRepository { + + @Query("SELECT r FROM NotificationRequestEntity r WHERE r.tenantId = :tenantId AND " + + "(lower(r.notificationReason) LIKE lower(concat('%', :searchText, '%')) OR " + + "lower(r.textTemplate) LIKE lower(concat('%', :searchText, '%')))") + Page findByTenantIdAndSearchText(@Param("tenantId") UUID tenantId, + @Param("searchText") String searchText, Pageable pageable); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java index e3a4260102..a558ad5163 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java @@ -15,7 +15,11 @@ */ package org.thingsboard.server.dao.sql.notification; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.thingsboard.server.dao.model.sql.NotificationTargetEntity; @@ -23,4 +27,7 @@ import java.util.UUID; @Repository public interface NotificationTargetRepository extends JpaRepository { + + Page findByTenantIdAndNameContainingIgnoreCase(UUID tenantId, String searchText, Pageable pageable); + } From 4c61d631499626c9e4bea088a9e11692b029e764 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 31 Oct 2022 14:04:29 +0200 Subject: [PATCH 013/488] Upgrade script, indexes --- .../main/data/upgrade/3.4.2/schema_update.sql | 54 +++++++++++++++++++ .../server/config/SwaggerConfiguration.java | 2 + .../dao/notification/NotificationService.java | 7 ++- .../server/common/data/EntityType.java | 4 +- .../common/data/id/EntityIdFactory.java | 4 ++ .../server/common/data/id/NotificationId.java | 3 +- .../common/data/id/NotificationRequestId.java | 13 ++++- .../common/data/id/NotificationTargetId.java | 13 ++++- .../data/notification/Notification.java | 2 + .../data/notification/NotificationStatus.java | 1 - .../targets/NotificationTarget.java | 8 +++ .../SingleUserNotificationTargetConfig.java | 3 ++ .../server/dao/model/BaseSqlEntity.java | 15 ++++++ .../server/dao/model/ModelConstants.java | 2 + .../dao/model/sql/NotificationEntity.java | 30 +++++++++-- .../model/sql/NotificationRequestEntity.java | 16 +++--- .../model/sql/NotificationTargetEntity.java | 6 +-- .../notification/NotificationRepository.java | 2 + .../resources/sql/schema-entities-idx.sql | 8 +++ .../main/resources/sql/schema-entities.sql | 35 ++++++++++++ 20 files changed, 206 insertions(+), 22 deletions(-) create mode 100644 application/src/main/data/upgrade/3.4.2/schema_update.sql diff --git a/application/src/main/data/upgrade/3.4.2/schema_update.sql b/application/src/main/data/upgrade/3.4.2/schema_update.sql new file mode 100644 index 0000000000..9b259cca3e --- /dev/null +++ b/application/src/main/data/upgrade/3.4.2/schema_update.sql @@ -0,0 +1,54 @@ +-- +-- Copyright © 2016-2022 The Thingsboard Authors +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + + +CREATE TABLE IF NOT EXISTS notification_target ( + id UUID NOT NULL CONSTRAINT notification_target_pkey PRIMARY KEY, + created_time BIGINT NOT NULL, + tenant_id UUID NOT NULL, + name VARCHAR(255) NOT NULL, + configuration varchar(1000) NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_notification_target_tenant_id_and_created_time ON notification_target(tenant_id, created_time DESC); + +CREATE TABLE IF NOT EXISTS notification_request ( + id UUID NOT NULL CONSTRAINT notification_request_pkey PRIMARY KEY, + created_time BIGINT NOT NULL, + tenant_id UUID NOT NULL, + target_id UUID NOT NULL CONSTRAINT fk_notification_request_target_id REFERENCES notification_target(id), + notification_reason VARCHAR NOT NULL, + text_template VARCHAR NOT NULL, + notification_info VARCHAR(1000), + notification_severity VARCHAR(32), + additional_config VARCHAR(1000), + sender_id UUID +); +CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id_and_created_time ON notification_request(tenant_id, created_time DESC); + +CREATE TABLE IF NOT EXISTS notification ( + id UUID NOT NULL, + created_time BIGINT NOT NULL, + request_id UUID NOT NULL CONSTRAINT fk_notification_request_id + REFERENCES notification_request(id) ON DELETE CASCADE, + recipient_id UUID NOT NULL, + reason VARCHAR NOT NULL, + text VARCHAR NOT NULL, + info VARCHAR(1000), + severity VARCHAR(32), + status VARCHAR(32) +) PARTITION BY RANGE (created_time); +CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_and_created_time ON notification(recipient_id, created_time DESC); +CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_and_status_and_created_time ON notification(recipient_id, status, created_time DESC); 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 c53cf1b504..49e041f1af 100644 --- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java @@ -25,6 +25,7 @@ import org.springframework.core.annotation.Order; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.security.Authority; @@ -138,6 +139,7 @@ public class SwaggerConfiguration { ) .securitySchemes(newArrayList(httpLogin())) .securityContexts(newArrayList(securityContext())) + .ignoredParameterTypes(AuthenticationPrincipal.class) .enableUrlTemplating(true); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java index a582758639..c8e1a7ea08 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.notification; import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; @@ -28,12 +29,16 @@ public interface NotificationService { NotificationRequest createNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest); + NotificationRequest findNotificationRequestById(TenantId tenantId, NotificationRequestId id); + PageData findNotificationRequestsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + void deleteNotificationRequest(TenantId tenantId, NotificationRequestId id); + Notification createNotification(TenantId tenantId, Notification notification); - void updateNotificationStatus(TenantId tenantId, NotificationId notificationId, NotificationStatus status); + Notification updateNotificationStatus(TenantId tenantId, NotificationId notificationId, NotificationStatus status); PageData findNotificationsByUserIdAndReadStatusAndPageLink(TenantId tenantId, UserId userId, boolean unreadOnly, PageLink pageLink); 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 0798647903..bdcff739f7 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 @@ -19,5 +19,7 @@ package org.thingsboard.server.common.data; * @author Andrew Shvayka */ public enum EntityType { - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, ASSET_PROFILE, API_USAGE_STATE, TB_RESOURCE, OTA_PACKAGE, EDGE, RPC, QUEUE; + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, + TENANT_PROFILE, DEVICE_PROFILE, ASSET_PROFILE, API_USAGE_STATE, TB_RESOURCE, OTA_PACKAGE, EDGE, RPC, QUEUE, + NOTIFICATION_TARGET, NOTIFICATION_REQUEST; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index da9a5c49eb..93080269b2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -81,6 +81,10 @@ public class EntityIdFactory { return new RpcId(uuid); case QUEUE: return new QueueId(uuid); + case NOTIFICATION_TARGET: + return new NotificationTargetId(uuid); + case NOTIFICATION_REQUEST: + return new NotificationRequestId(uuid); } throw new IllegalArgumentException("EntityType " + type + " is not supported!"); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationId.java index a52bfdd9d1..2a467d3a07 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationId.java @@ -16,12 +16,13 @@ package org.thingsboard.server.common.data.id; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import java.util.UUID; public class NotificationId extends UUIDBased { @JsonCreator - public NotificationId(UUID id) { + public NotificationId(@JsonProperty("id") UUID id) { super(id); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRequestId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRequestId.java index cffea0f6f5..3272d93d6f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRequestId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRequestId.java @@ -16,12 +16,21 @@ package org.thingsboard.server.common.data.id; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -public class NotificationRequestId extends UUIDBased { +public class NotificationRequestId extends UUIDBased implements EntityId { + @JsonCreator - public NotificationRequestId(UUID id) { + public NotificationRequestId(@JsonProperty("id") UUID id) { super(id); } + + @Override + public EntityType getEntityType() { + return EntityType.NOTIFICATION_REQUEST; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationTargetId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationTargetId.java index 3d8dddfe0e..44211d85fd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationTargetId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationTargetId.java @@ -16,12 +16,21 @@ package org.thingsboard.server.common.data.id; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -public class NotificationTargetId extends UUIDBased { +public class NotificationTargetId extends UUIDBased implements EntityId { + @JsonCreator - public NotificationTargetId(UUID id) { + public NotificationTargetId(@JsonProperty("id") UUID id) { super(id); } + + @Override + public EntityType getEntityType() { + return EntityType.NOTIFICATION_TARGET; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java index 02f0fb2aa4..a633ad6433 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java @@ -34,7 +34,9 @@ public class Notification extends SearchTextBased { private NotificationRequestId requestId; private UserId recipientId; + private String reason; private String text; + private NotificationInfo info; private NotificationSeverity severity; private NotificationStatus status; // private UserId senderId; 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 7e504cbe15..3484768860 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 @@ -17,6 +17,5 @@ package org.thingsboard.server.common.data.notification; public enum NotificationStatus { SENT, - DELIVERED, READ } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java index 957a6d359f..8b873b5ca9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java @@ -23,12 +23,20 @@ import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + @Data @EqualsAndHashCode(callSuper = true) public class NotificationTarget extends BaseData implements HasTenantId, HasName { + @NotNull private TenantId tenantId; + @NotBlank private String name; + @NotNull + @Valid private NotificationTargetConfig configuration; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/SingleUserNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/SingleUserNotificationTargetConfig.java index cf0b4316d0..9fbb6ca600 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/SingleUserNotificationTargetConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/SingleUserNotificationTargetConfig.java @@ -18,9 +18,12 @@ package org.thingsboard.server.common.data.notification.targets; import lombok.Data; import org.thingsboard.server.common.data.id.UserId; +import javax.validation.constraints.NotNull; + @Data public class SingleUserNotificationTargetConfig implements NotificationTargetConfig { + @NotNull private UserId userId; @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java index 6e8a4a7076..5256eee7db 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java @@ -15,7 +15,10 @@ */ package org.thingsboard.server.dao.model; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.UUIDBased; import javax.persistence.Column; @@ -75,4 +78,16 @@ public abstract class BaseSqlEntity implements BaseEntity { } } + protected JsonNode toJson(Object value) { + if (value != null) { + return JacksonUtil.valueToTree(value); + } else { + return null; + } + } + + protected T fromJson(JsonNode json) { + return JacksonUtil.convertValue(json, new TypeReference() {}); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 5c938de98c..13d149bcf5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -655,7 +655,9 @@ public class ModelConstants { public static final String NOTIFICATION_TABLE_NAME = "notification"; public static final String NOTIFICATION_REQUEST_ID_PROPERTY = "request_id"; public static final String NOTIFICATION_RECIPIENT_ID_PROPERTY = "recipient_id"; + public static final String NOTIFICATION_REASON_PROPERTY = "reason"; public static final String NOTIFICATION_TEXT_PROPERTY = "text"; + public static final String NOTIFICATION_INFO_PROPERTY = "info"; public static final String NOTIFICATION_SEVERITY_PROPERTY = "severity"; public static final String NOTIFICATION_STATUS_PROPERTY = "status"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java index ad7c506d4b..be3532e300 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java @@ -15,16 +15,22 @@ */ package org.thingsboard.server.dao.model.sql; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationInfo; import org.thingsboard.server.common.data.notification.NotificationSeverity; import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; import javax.persistence.Entity; @@ -36,18 +42,26 @@ import java.util.UUID; @Data @EqualsAndHashCode(callSuper = true) @Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) @Table(name = ModelConstants.NOTIFICATION_TABLE_NAME) public class NotificationEntity extends BaseSqlEntity { - @Column(name = ModelConstants.NOTIFICATION_REQUEST_ID_PROPERTY) + @Column(name = ModelConstants.NOTIFICATION_REQUEST_ID_PROPERTY, nullable = false) private UUID requestId; - @Column(name = ModelConstants.NOTIFICATION_RECIPIENT_ID_PROPERTY) + @Column(name = ModelConstants.NOTIFICATION_RECIPIENT_ID_PROPERTY, nullable = false) private UUID recipientId; - @Column(name = ModelConstants.NOTIFICATION_TEXT_PROPERTY) + @Column(name = ModelConstants.NOTIFICATION_REASON_PROPERTY, nullable = false) + private String reason; + + @Column(name = ModelConstants.NOTIFICATION_TEXT_PROPERTY, nullable = false) private String text; + @Type(type = "json") + @Column(name = ModelConstants.NOTIFICATION_INFO_PROPERTY) + private JsonNode info; + @Column(name = ModelConstants.NOTIFICATION_SEVERITY_PROPERTY) private NotificationSeverity severity; @@ -62,7 +76,12 @@ public class NotificationEntity extends BaseSqlEntity { setCreatedTime(notification.getCreatedTime()); setRequestId(getUuid(notification.getRequestId())); setRecipientId(getUuid(notification.getRecipientId())); + setReason(notification.getReason()); setText(notification.getText()); + if (notification.getInfo() != null) { + setInfo(JacksonUtil.valueToTree(notification.getInfo())); + } + setSeverity(notification.getSeverity()); setStatus(notification.getStatus()); } @@ -73,7 +92,12 @@ public class NotificationEntity extends BaseSqlEntity { notification.setCreatedTime(createdTime); notification.setRequestId(createId(requestId, NotificationRequestId::new)); notification.setRecipientId(createId(recipientId, UserId::new)); + notification.setReason(reason); notification.setText(text); + if (info != null) { + notification.setInfo(JacksonUtil.treeToValue(info, NotificationInfo.class)); + } + notification.setSeverity(severity); notification.setStatus(status); return notification; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java index 23a8ae1059..f469f4aed7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java @@ -47,16 +47,16 @@ import java.util.UUID; @Table(name = ModelConstants.NOTIFICATION_REQUEST_TABLE_NAME) public class NotificationRequestEntity extends BaseSqlEntity { - @Column(name = ModelConstants.TENANT_ID_PROPERTY) + @Column(name = ModelConstants.TENANT_ID_PROPERTY, nullable = false) private UUID tenantId; - @Column(name = ModelConstants.NOTIFICATION_REQUEST_TARGET_ID_PROPERTY) + @Column(name = ModelConstants.NOTIFICATION_REQUEST_TARGET_ID_PROPERTY, nullable = false) private UUID targetId; - @Column(name = ModelConstants.NOTIFICATION_REQUEST_NOTIFICATION_REASON_PROPERTY) + @Column(name = ModelConstants.NOTIFICATION_REQUEST_NOTIFICATION_REASON_PROPERTY, nullable = false) private String notificationReason; - @Column(name = ModelConstants.NOTIFICATION_REQUEST_TEXT_TEMPLATE_PROPERTY) + @Column(name = ModelConstants.NOTIFICATION_REQUEST_TEXT_TEMPLATE_PROPERTY, nullable = false) private String textTemplate; @Type(type = "json") @@ -83,9 +83,9 @@ public class NotificationRequestEntity extends BaseSqlEntity { - @Column(name = ModelConstants.TENANT_ID_PROPERTY) + @Column(name = ModelConstants.TENANT_ID_PROPERTY, nullable = false) private UUID tenantId; - @Column(name = ModelConstants.NAME_PROPERTY) + @Column(name = ModelConstants.NAME_PROPERTY, nullable = false) private String name; @Type(type = "json") - @Column(name = ModelConstants.NOTIFICATION_TARGET_CONFIGURATION_PROPERTY) + @Column(name = ModelConstants.NOTIFICATION_TARGET_CONFIGURATION_PROPERTY, nullable = false) private JsonNode configuration; public NotificationTargetEntity() {} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java index 9ee5b07121..07fc48e13f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java @@ -40,4 +40,6 @@ public interface NotificationRepository extends JpaRepository Date: Mon, 31 Oct 2022 14:05:08 +0200 Subject: [PATCH 014/488] WebSocket API for notifications; refactoring --- .../controller/AbstractRpcController.java | 2 +- .../controller/NotificationController.java | 20 +- .../server/controller/RpcV2Controller.java | 2 +- .../controller/TelemetryController.java | 4 +- .../controller/plugin/TbWebSocketHandler.java | 54 +++-- .../exception/AccessDeniedException.java | 2 +- .../exception/EntityNotFoundException.java | 2 +- .../exception/InternalErrorException.java | 2 +- .../exception/InvalidParametersException.java | 2 +- .../exception/ToErrorResponseEntity.java | 2 +- .../exception/UnauthorizedException.java | 2 +- .../exception/UncheckedApiException.java | 2 +- .../DefaultNotificationProcessingService.java | 58 +++-- .../NotificationProcessingService.java | 6 + .../queue/DefaultTbCoreConsumerService.java | 25 ++ .../service/security/AccessValidator.java | 2 +- .../service/security/ValidationCallback.java | 8 +- .../permission/TenantAdminPermissions.java | 1 + .../DefaultSubscriptionManagerService.java | 49 +++- ...efaultTbEntityDataSubscriptionService.java | 45 ++-- .../DefaultTbLocalSubscriptionService.java | 30 ++- .../subscription/ReadTsKvQueryInfo.java | 2 +- .../SubscriptionErrorCode.java | 2 +- .../SubscriptionManagerService.java | 6 + .../subscription/TbAbstractDataSubCtx.java | 14 +- .../subscription/TbAbstractSubCtx.java | 19 +- .../subscription/TbAlarmDataSubCtx.java | 16 +- .../subscription/TbAlarmsSubscription.java | 9 +- .../subscription/TbAttributeSubscription.java | 6 +- .../subscription/TbEntityCountSubCtx.java | 10 +- .../subscription/TbEntityDataSubCtx.java | 18 +- .../TbEntityDataSubscriptionService.java | 17 +- .../TbLocalSubscriptionService.java | 7 +- .../service/subscription/TbSubscription.java | 2 +- .../subscription/TbSubscriptionType.java | 2 +- .../subscription/TbSubscriptionUtils.java | 71 +++++- .../TbTimeseriesSubscription.java | 6 +- .../AbstractSubscriptionService.java | 22 +- .../DefaultAlarmSubscriptionService.java | 32 +-- .../DefaultTelemetrySubscriptionService.java | 91 +++---- ...vice.java => DefaultWebSocketService.java} | 225 +++++++++++------- .../telemetry/InternalTelemetryService.java | 2 - .../{telemetry => ws}/SessionEvent.java | 2 +- .../WebSocketMsgEndpoint.java} | 11 +- .../WebSocketSessionRef.java} | 30 +-- .../service/ws/WebSocketSessionType.java | 38 +++ .../{telemetry => ws}/WsSessionMetaData.java | 10 +- .../cmd/MarkNotificationAsReadCmd.java | 30 +++ .../cmd/NotificationCmdsWrapper.java | 28 +++ .../notification/cmd/NotificationsSubCmd.java | 28 +++ .../cmd/NotificationsUnsubCmd.java | 24 ++ .../cmd/UnreadNotificationsUpdate.java | 54 +++++ ...faultNotificationsSubscriptionService.java | 183 ++++++++++++++ .../sub/NotificationsSubscription.java | 64 +++++ .../sub/NotificationsSubscriptionService.java | 42 ++++ .../sub/NotificationsSubscriptionUpdate.java | 32 +++ .../{ => ws}/telemetry/TelemetryFeature.java | 2 +- .../telemetry/TelemetryWebSocketTextMsg.java | 5 +- .../telemetry/WebSocketService.java} | 15 +- .../cmd/TelemetryPluginCmdsWrapper.java | 20 +- .../cmd/v1/AttributesSubscriptionCmd.java | 4 +- .../telemetry/cmd/v1/GetHistoryCmd.java | 2 +- .../telemetry/cmd/v1/SubscriptionCmd.java | 4 +- .../telemetry/cmd/v1/TelemetryPluginCmd.java | 2 +- .../cmd/v1/TimeseriesSubscriptionCmd.java | 4 +- .../telemetry/cmd/v2/AggHistoryCmd.java | 2 +- .../{ => ws}/telemetry/cmd/v2/AggKey.java | 2 +- .../telemetry/cmd/v2/AggTimeSeriesCmd.java | 2 +- .../telemetry/cmd/v2/AlarmDataCmd.java | 2 +- .../cmd/v2/AlarmDataUnsubscribeCmd.java | 2 +- .../telemetry/cmd/v2/AlarmDataUpdate.java | 4 +- .../{ => ws}/telemetry/cmd/v2/CmdUpdate.java | 2 +- .../telemetry/cmd/v2/CmdUpdateType.java | 5 +- .../{ => ws}/telemetry/cmd/v2/DataCmd.java | 3 +- .../{ => ws}/telemetry/cmd/v2/DataUpdate.java | 4 +- .../telemetry/cmd/v2/EntityCountCmd.java | 3 +- .../cmd/v2/EntityCountUnsubscribeCmd.java | 2 +- .../telemetry/cmd/v2/EntityCountUpdate.java | 8 +- .../telemetry/cmd/v2/EntityDataCmd.java | 2 +- .../cmd/v2/EntityDataUnsubscribeCmd.java | 2 +- .../telemetry/cmd/v2/EntityDataUpdate.java | 4 +- .../telemetry/cmd/v2/EntityHistoryCmd.java | 2 +- .../{ => ws}/telemetry/cmd/v2/GetTsCmd.java | 2 +- .../telemetry/cmd/v2/LatestValueCmd.java | 2 +- .../telemetry/cmd/v2/TimeSeriesCmd.java | 2 +- .../telemetry/cmd/v2/UnsubscribeCmd.java | 2 +- .../sub/AlarmSubscriptionUpdate.java | 12 +- .../telemetry/sub/SubscriptionState.java | 4 +- .../sub/TelemetrySubscriptionUpdate.java | 3 +- .../controller/AbstractControllerTest.java | 21 +- .../controller/BaseWebsocketApiTest.java | 6 +- .../controller/TbTestWebSocketClient.java | 16 +- .../NotificationsWebSocketClient.java | 75 ++++++ .../notification/NotificationsWsApiTest.java | 147 ++++++++++++ .../lwm2m/AbstractLwM2MIntegrationTest.java | 8 +- ...AbstractMqttAttributesIntegrationTest.java | 3 +- common/cluster-api/src/main/proto/queue.proto | 30 +++ .../server/common/data/StringUtils.java | 9 + .../DefaultNotificationService.java | 17 +- .../DefaultNotificationTargetService.java | 6 + 100 files changed, 1481 insertions(+), 468 deletions(-) rename application/src/main/java/org/thingsboard/server/{service/telemetry => }/exception/AccessDeniedException.java (94%) rename application/src/main/java/org/thingsboard/server/{service/telemetry => }/exception/EntityNotFoundException.java (94%) rename application/src/main/java/org/thingsboard/server/{service/telemetry => }/exception/InternalErrorException.java (94%) rename application/src/main/java/org/thingsboard/server/{service/telemetry => }/exception/InvalidParametersException.java (94%) rename application/src/main/java/org/thingsboard/server/{service/telemetry => }/exception/ToErrorResponseEntity.java (93%) rename application/src/main/java/org/thingsboard/server/{service/telemetry => }/exception/UnauthorizedException.java (94%) rename application/src/main/java/org/thingsboard/server/{service/telemetry => }/exception/UncheckedApiException.java (95%) rename application/src/main/java/org/thingsboard/server/service/{telemetry/sub => subscription}/SubscriptionErrorCode.java (96%) rename application/src/main/java/org/thingsboard/server/service/telemetry/{DefaultTelemetryWebSocketService.java => DefaultWebSocketService.java} (82%) rename application/src/main/java/org/thingsboard/server/service/{telemetry => ws}/SessionEvent.java (96%) rename application/src/main/java/org/thingsboard/server/service/{telemetry/TelemetryWebSocketMsgEndpoint.java => ws/WebSocketMsgEndpoint.java} (63%) rename application/src/main/java/org/thingsboard/server/service/{telemetry/TelemetryWebSocketSessionRef.java => ws/WebSocketSessionRef.java} (70%) create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionType.java rename application/src/main/java/org/thingsboard/server/service/{telemetry => ws}/WsSessionMetaData.java (80%) create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationAsReadCmd.java create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsUpdate.java create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/notification/sub/DefaultNotificationsSubscriptionService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionUpdate.java rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/TelemetryFeature.java (94%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/TelemetryWebSocketTextMsg.java (82%) rename application/src/main/java/org/thingsboard/server/service/{telemetry/TelemetryWebSocketService.java => ws/telemetry/WebSocketService.java} (63%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/TelemetryPluginCmdsWrapper.java (62%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v1/AttributesSubscriptionCmd.java (87%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v1/GetHistoryCmd.java (94%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v1/SubscriptionCmd.java (90%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v1/TelemetryPluginCmd.java (92%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v1/TimeseriesSubscriptionCmd.java (89%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/AggHistoryCmd.java (92%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/AggKey.java (93%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/AggTimeSeriesCmd.java (92%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/AlarmDataCmd.java (94%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/AlarmDataUnsubscribeCmd.java (92%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/AlarmDataUpdate.java (94%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/CmdUpdate.java (94%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/CmdUpdateType.java (87%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/DataCmd.java (89%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/DataUpdate.java (91%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/EntityCountCmd.java (90%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/EntityCountUnsubscribeCmd.java (92%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/EntityCountUpdate.java (85%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/EntityDataCmd.java (97%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/EntityDataUnsubscribeCmd.java (92%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/EntityDataUpdate.java (93%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/EntityHistoryCmd.java (94%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/GetTsCmd.java (93%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/LatestValueCmd.java (92%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/TimeSeriesCmd.java (95%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/cmd/v2/UnsubscribeCmd.java (91%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/sub/AlarmSubscriptionUpdate.java (85%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/sub/SubscriptionState.java (95%) rename application/src/main/java/org/thingsboard/server/service/{ => ws}/telemetry/sub/TelemetrySubscriptionUpdate.java (96%) create mode 100644 application/src/test/java/org/thingsboard/server/service/notification/NotificationsWebSocketClient.java create mode 100644 application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java diff --git a/application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java b/application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java index e5a217893e..946a743370 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java @@ -43,7 +43,7 @@ import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; import org.thingsboard.server.service.security.AccessValidator; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; -import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity; +import org.thingsboard.server.exception.ToErrorResponseEntity; import javax.annotation.Nullable; import java.util.Optional; diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index 454a8422e5..f6f48aeb51 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -19,6 +19,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; +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; @@ -29,6 +30,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationStatus; @@ -71,7 +73,7 @@ public class NotificationController extends BaseController { public void markNotificationAsRead(@PathVariable UUID id, @AuthenticationPrincipal SecurityUser user) { NotificationId notificationId = new NotificationId(id); - notificationService.updateNotificationStatus(user.getTenantId(), notificationId, NotificationStatus.READ); + notificationProcessingService.markNotificationAsRead(user, notificationId); } @@ -83,6 +85,14 @@ public class NotificationController extends BaseController { return notificationProcessingService.processNotificationRequest(user, notificationRequest); } + @GetMapping("/notification/request/{id}") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public NotificationRequest getNotificationRequestById(@PathVariable UUID id, + @AuthenticationPrincipal SecurityUser user) { + NotificationRequestId notificationRequestId = new NotificationRequestId(id); + return notificationService.findNotificationRequestById(user.getTenantId(), notificationRequestId); + } + @GetMapping("/notification/requests") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public PageData getNotificationRequests(@RequestParam int pageSize, @@ -96,9 +106,11 @@ public class NotificationController extends BaseController { return notificationService.findNotificationRequestsByTenantIdAndPageLink(user.getTenantId(), pageLink); } - // delete request and sent notifications - public void deleteNotificationRequest() { - + @DeleteMapping("/notification/request/{id}") + public void deleteNotificationRequest(@PathVariable UUID id, + @AuthenticationPrincipal SecurityUser user) { + NotificationRequestId notificationRequestId = new NotificationRequestId(id); + notificationProcessingService.deleteNotificationRequest(user, notificationRequestId); } } diff --git a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java index 300d522dbe..ea795dabfb 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java @@ -47,7 +47,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.rpc.RemoveRpcActorMsg; import org.thingsboard.server.service.security.permission.Operation; -import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity; +import org.thingsboard.server.exception.ToErrorResponseEntity; import javax.annotation.Nullable; import java.util.UUID; 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 4498b51c6c..026ce24723 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -84,8 +84,8 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.telemetry.AttributeData; import org.thingsboard.server.service.telemetry.TsData; -import org.thingsboard.server.service.telemetry.exception.InvalidParametersException; -import org.thingsboard.server.service.telemetry.exception.UncheckedApiException; +import org.thingsboard.server.exception.InvalidParametersException; +import org.thingsboard.server.exception.UncheckedApiException; import javax.annotation.Nullable; import javax.annotation.PostConstruct; diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index 432b35fd80..187603afc5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -38,10 +38,11 @@ import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; -import org.thingsboard.server.service.telemetry.SessionEvent; -import org.thingsboard.server.service.telemetry.TelemetryWebSocketMsgEndpoint; -import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; -import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; +import org.thingsboard.server.service.ws.SessionEvent; +import org.thingsboard.server.service.ws.WebSocketMsgEndpoint; +import org.thingsboard.server.service.ws.WebSocketSessionType; +import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.WebSocketSessionRef; import javax.websocket.RemoteEndpoint; import javax.websocket.SendHandler; @@ -57,19 +58,19 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.LinkedBlockingQueue; -import static org.thingsboard.server.service.telemetry.DefaultTelemetryWebSocketService.NUMBER_OF_PING_ATTEMPTS; +import static org.thingsboard.server.service.telemetry.DefaultWebSocketService.NUMBER_OF_PING_ATTEMPTS; @Service @TbCoreComponent @Slf4j -public class TbWebSocketHandler extends TextWebSocketHandler implements TelemetryWebSocketMsgEndpoint { +public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocketMsgEndpoint { private static final ConcurrentMap internalSessionMap = new ConcurrentHashMap<>(); private static final ConcurrentMap externalSessionMap = new ConcurrentHashMap<>(); @Autowired - private TelemetryWebSocketService webSocketService; + private WebSocketService webSocketService; @Autowired private TbTenantProfileCache tenantProfileCache; @@ -79,7 +80,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr @Value("${server.ws.ping_timeout:30000}") private long pingTimeout; - private ConcurrentMap blacklistedSessions = new ConcurrentHashMap<>(); + private ConcurrentMap blacklistedSessions = new ConcurrentHashMap<>(); private ConcurrentMap perSessionUpdateLimits = new ConcurrentHashMap<>(); private ConcurrentMap> tenantSessionsMap = new ConcurrentHashMap<>(); @@ -130,7 +131,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } } String internalSessionId = session.getId(); - TelemetryWebSocketSessionRef sessionRef = toRef(session); + WebSocketSessionRef sessionRef = toRef(session); String externalSessionId = sessionRef.getSessionId(); if (!checkLimits(session, sessionRef)) { @@ -178,7 +179,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } } - private void processInWebSocketService(TelemetryWebSocketSessionRef sessionRef, SessionEvent event) { + private void processInWebSocketService(WebSocketSessionRef sessionRef, SessionEvent event) { try { webSocketService.handleWebSocketSessionEvent(sessionRef, event); } catch (BeanCreationNotAllowedException e) { @@ -186,7 +187,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } } - private TelemetryWebSocketSessionRef toRef(WebSocketSession session) throws IOException { + private WebSocketSessionRef toRef(WebSocketSession session) throws IOException { URI sessionUri = session.getUri(); String path = sessionUri.getPath(); path = path.substring(WebSocketConfiguration.WS_PLUGIN_PREFIX.length()); @@ -195,25 +196,30 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } String[] pathElements = path.split("/"); String serviceToken = pathElements[0]; - if (!"telemetry".equalsIgnoreCase(serviceToken)) { - throw new InvalidParameterException("Can't find plugin with specified token!"); - } else { - SecurityUser currentUser = (SecurityUser) ((Authentication) session.getPrincipal()).getPrincipal(); - return new TelemetryWebSocketSessionRef(UUID.randomUUID().toString(), currentUser, session.getLocalAddress(), session.getRemoteAddress()); - } + WebSocketSessionType sessionType = WebSocketSessionType.forName(serviceToken) + .orElseThrow(() -> new InvalidParameterException("Can't find plugin with specified token!")); + + SecurityUser currentUser = (SecurityUser) ((Authentication) session.getPrincipal()).getPrincipal(); + return WebSocketSessionRef.builder() + .sessionId(UUID.randomUUID().toString()) + .securityCtx(currentUser) + .localAddress(session.getLocalAddress()) + .remoteAddress(session.getRemoteAddress()) + .sessionType(sessionType) + .build(); } private class SessionMetaData implements SendHandler { private final WebSocketSession session; private final RemoteEndpoint.Async asyncRemote; - private final TelemetryWebSocketSessionRef sessionRef; + private final WebSocketSessionRef sessionRef; private volatile boolean isSending = false; private final Queue> msgQueue; private volatile long lastActivityTime; - SessionMetaData(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef, int maxMsgQueuePerSession) { + SessionMetaData(WebSocketSession session, WebSocketSessionRef sessionRef, int maxMsgQueuePerSession) { super(); this.session = session; Session nativeSession = ((NativeWebSocketSession) session).getNativeSession(Session.class); @@ -309,7 +315,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } @Override - public void send(TelemetryWebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException { + public void send(WebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException { String externalId = sessionRef.getSessionId(); log.debug("[{}] Processing {}", externalId, msg); String internalId = externalSessionMap.get(externalId); @@ -343,7 +349,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } @Override - public void sendPing(TelemetryWebSocketSessionRef sessionRef, long currentTime) throws IOException { + public void sendPing(WebSocketSessionRef sessionRef, long currentTime) throws IOException { String externalId = sessionRef.getSessionId(); String internalId = externalSessionMap.get(externalId); if (internalId != null) { @@ -359,7 +365,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } @Override - public void close(TelemetryWebSocketSessionRef sessionRef, CloseStatus reason) throws IOException { + public void close(WebSocketSessionRef sessionRef, CloseStatus reason) throws IOException { String externalId = sessionRef.getSessionId(); log.debug("[{}] Processing close request", externalId); String internalId = externalSessionMap.get(externalId); @@ -375,7 +381,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } } - private boolean checkLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) throws Exception { + private boolean checkLimits(WebSocketSession session, WebSocketSessionRef sessionRef) throws Exception { var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration(); if (tenantProfileConfiguration == null) { @@ -443,7 +449,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr return true; } - private void cleanupLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) { + private void cleanupLimits(WebSocketSession session, WebSocketSessionRef sessionRef) { var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration(); String sessionId = session.getId(); diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/exception/AccessDeniedException.java b/application/src/main/java/org/thingsboard/server/exception/AccessDeniedException.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/telemetry/exception/AccessDeniedException.java rename to application/src/main/java/org/thingsboard/server/exception/AccessDeniedException.java index 43a7f8c56f..d427c20dcc 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/exception/AccessDeniedException.java +++ b/application/src/main/java/org/thingsboard/server/exception/AccessDeniedException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.exception; +package org.thingsboard.server.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/exception/EntityNotFoundException.java b/application/src/main/java/org/thingsboard/server/exception/EntityNotFoundException.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/telemetry/exception/EntityNotFoundException.java rename to application/src/main/java/org/thingsboard/server/exception/EntityNotFoundException.java index 3ede4e7e70..574b40feab 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/exception/EntityNotFoundException.java +++ b/application/src/main/java/org/thingsboard/server/exception/EntityNotFoundException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.exception; +package org.thingsboard.server.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/exception/InternalErrorException.java b/application/src/main/java/org/thingsboard/server/exception/InternalErrorException.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/telemetry/exception/InternalErrorException.java rename to application/src/main/java/org/thingsboard/server/exception/InternalErrorException.java index ae68ffdc3d..05e9b3e937 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/exception/InternalErrorException.java +++ b/application/src/main/java/org/thingsboard/server/exception/InternalErrorException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.exception; +package org.thingsboard.server.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/exception/InvalidParametersException.java b/application/src/main/java/org/thingsboard/server/exception/InvalidParametersException.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/telemetry/exception/InvalidParametersException.java rename to application/src/main/java/org/thingsboard/server/exception/InvalidParametersException.java index 7452d25a56..467e8cf4e2 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/exception/InvalidParametersException.java +++ b/application/src/main/java/org/thingsboard/server/exception/InvalidParametersException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.exception; +package org.thingsboard.server.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/exception/ToErrorResponseEntity.java b/application/src/main/java/org/thingsboard/server/exception/ToErrorResponseEntity.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/telemetry/exception/ToErrorResponseEntity.java rename to application/src/main/java/org/thingsboard/server/exception/ToErrorResponseEntity.java index 769c5ffe5e..f44effc827 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/exception/ToErrorResponseEntity.java +++ b/application/src/main/java/org/thingsboard/server/exception/ToErrorResponseEntity.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.exception; +package org.thingsboard.server.exception; import org.springframework.http.ResponseEntity; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/exception/UnauthorizedException.java b/application/src/main/java/org/thingsboard/server/exception/UnauthorizedException.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/telemetry/exception/UnauthorizedException.java rename to application/src/main/java/org/thingsboard/server/exception/UnauthorizedException.java index b597a84e29..f7c046abe2 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/exception/UnauthorizedException.java +++ b/application/src/main/java/org/thingsboard/server/exception/UnauthorizedException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.exception; +package org.thingsboard.server.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/exception/UncheckedApiException.java b/application/src/main/java/org/thingsboard/server/exception/UncheckedApiException.java similarity index 95% rename from application/src/main/java/org/thingsboard/server/service/telemetry/exception/UncheckedApiException.java rename to application/src/main/java/org/thingsboard/server/exception/UncheckedApiException.java index 8ee4bfa753..1d7658fdfb 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/exception/UncheckedApiException.java +++ b/application/src/main/java/org/thingsboard/server/exception/UncheckedApiException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.exception; +package org.thingsboard.server.exception; import org.springframework.http.ResponseEntity; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java index 04bc45b9d4..862ce7389a 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java @@ -15,12 +15,15 @@ */ package org.thingsboard.server.service.notification; +import com.google.common.base.Strings; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.rule.engine.api.util.TbNodeUtils; -import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; @@ -29,13 +32,12 @@ import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.user.UserService; -import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.AccessControlService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; -import org.thingsboard.server.service.telemetry.AbstractSubscriptionService; +import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionService; import java.util.ArrayList; import java.util.List; @@ -43,26 +45,15 @@ import java.util.Map; @Service @Slf4j -public class DefaultNotificationProcessingService extends AbstractSubscriptionService implements NotificationProcessingService { +@RequiredArgsConstructor +public class DefaultNotificationProcessingService implements NotificationProcessingService { private final NotificationTargetService notificationTargetService; private final NotificationService notificationService; private final UserService userService; private final AccessControlService accessControlService; private final DbCallbackExecutorService dbCallbackExecutorService; - - public DefaultNotificationProcessingService(TbClusterService clusterService, PartitionService partitionService, - NotificationTargetService notificationTargetService, - NotificationService notificationService, UserService userService, - AccessControlService accessControlService, - DbCallbackExecutorService dbCallbackExecutorService) { - super(clusterService, partitionService); - this.notificationTargetService = notificationTargetService; - this.notificationService = notificationService; - this.userService = userService; - this.accessControlService = accessControlService; - this.dbCallbackExecutorService = dbCallbackExecutorService; - } + private final NotificationsSubscriptionService notificationsSubscriptionService; @Override public NotificationRequest processNotificationRequest(SecurityUser user, NotificationRequest notificationRequest) throws ThingsboardException { @@ -83,19 +74,33 @@ public class DefaultNotificationProcessingService extends AbstractSubscriptionSe for (User recipient : recipients) { dbCallbackExecutorService.submit(() -> { - Notification notification = createNotification(recipient, notificationRequest); - onNewNotification(notification); + Notification notification = createNotification(recipient, savedNotificationRequest); + notificationsSubscriptionService.onNewNotification(recipient.getTenantId(), recipient.getId(), notification); }); } return savedNotificationRequest; } + @Override + public void markNotificationAsRead(SecurityUser user, NotificationId notificationId) { + Notification notification = notificationService.updateNotificationStatus(user.getTenantId(), notificationId, NotificationStatus.READ); + notificationsSubscriptionService.onNotificationUpdated(user.getTenantId(), user.getId(), notification); + } + + @Override + public void deleteNotificationRequest(SecurityUser user, NotificationRequestId notificationRequestId) { + notificationService.deleteNotificationRequest(user.getTenantId(), notificationRequestId); + notificationsSubscriptionService.onNotificationRequestDeleted(user.getTenantId(), notificationRequestId); + } + private Notification createNotification(User recipient, NotificationRequest notificationRequest) { Notification notification = Notification.builder() .requestId(notificationRequest.getId()) .recipientId(recipient.getId()) + .reason(notificationRequest.getNotificationReason()) .text(formatNotificationText(notificationRequest.getTextTemplate(), recipient)) + .info(notificationRequest.getNotificationInfo()) .severity(notificationRequest.getNotificationSeverity()) .status(NotificationStatus.SENT) .build(); @@ -103,24 +108,15 @@ public class DefaultNotificationProcessingService extends AbstractSubscriptionSe return notification; } - private void onNewNotification(Notification notification) { - wsCallBackExecutor.submit(() -> { - - }) - } - private String formatNotificationText(String template, User recipient) { Map context = Map.of( "recipientEmail", recipient.getEmail(), - "recipientFirstName", recipient.getFirstName(), - "recipientLastName", recipient.getLastName() + "recipientFirstName", Strings.nullToEmpty(recipient.getFirstName()), + "recipientLastName", Strings.nullToEmpty(recipient.getLastName()) ); return TbNodeUtils.processTemplate(template, context); } - @Override - protected String getExecutorPrefix() { - return "notification"; - } + // handle markAsRead and deleteNotificationRequest - send UnreadNotificationsUpdate with updated list } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java index 1293ba999d..57aba583ed 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java @@ -16,6 +16,8 @@ package org.thingsboard.server.service.notification; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.service.security.model.SecurityUser; @@ -23,4 +25,8 @@ public interface NotificationProcessingService { NotificationRequest processNotificationRequest(SecurityUser user, NotificationRequest notificationRequest) throws ThingsboardException; + void markNotificationAsRead(SecurityUser user, NotificationId notificationId); + + void deleteNotificationRequest(SecurityUser user, NotificationRequestId notificationRequestId); + } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index ec58ac1aa9..0a7abdab51 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -27,7 +27,10 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.rpc.RpcError; import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.TbActorMsg; @@ -77,6 +80,7 @@ import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import org.thingsboard.server.service.sync.vc.GitVersionControlQueueService; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; +import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -456,6 +460,13 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService subscriptions = subscriptionsByEntityId.get(recipientId); + if (subscriptions != null) { + NotificationsSubscriptionUpdate subscriptionUpdate = NotificationsSubscriptionUpdate.builder() + .notification(notification) + .build(); + subscriptions.stream() + .filter(subscription -> subscription.getType() == TbSubscriptionType.NOTIFICATIONS) + .forEach(subscription -> { + if (serviceId.equals(subscription.getServiceId())) { + localSubscriptionService.onSubscriptionUpdate(subscription.getSessionId(), + subscription.getSubscriptionId(), subscriptionUpdate, TbCallback.EMPTY); + } else { + TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, subscription.getServiceId()); + ToCoreNotificationMsg updateProto = TbSubscriptionUtils.notificationsSubUpdateToProto(subscription, subscriptionUpdate); + TbProtoQueueMsg queueMsg = new TbProtoQueueMsg<>(subscription.getEntityId().getId(), updateProto); + toCoreNotificationsProducer.send(tpi, queueMsg, null); + } + }); + } + callback.onSuccess(); + } + + @Override + public void onNotificationRequestDeleted(TenantId tenantId, NotificationRequestId notificationRequestId, TbCallback callback) { + NotificationsSubscriptionUpdate subscriptionUpdate = NotificationsSubscriptionUpdate.builder() + .notificationRequestDeleted(true) + .notificationRequestId(notificationRequestId) + .build(); + subscriptionsByEntityId.entrySet().stream() + .filter(subEntry -> subEntry.getKey().getEntityType() == EntityType.USER) + .flatMap(subEntry -> subEntry.getValue().stream() + .filter(sub -> sub.getType() == TbSubscriptionType.NOTIFICATIONS) + .filter(sub -> sub.getServiceId().equals(serviceId))) + .forEach(subscription -> { + localSubscriptionService.onSubscriptionUpdate(subscription.getSessionId(), subscription.getSubscriptionId(), subscriptionUpdate, TbCallback.EMPTY); + }); + callback.onSuccess(); + } + @Override public void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List keys, TbCallback callback) { onLocalTelemetrySubUpdate(entityId, diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java index a5a45be5b2..eed12acf3e 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java @@ -48,22 +48,21 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.executors.DbCallbackExecutorService; -import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; -import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; -import org.thingsboard.server.service.telemetry.cmd.v2.AggHistoryCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.AggKey; -import org.thingsboard.server.service.telemetry.cmd.v2.AggTimeSeriesCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUpdate; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityHistoryCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.GetTsCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.TimeSeriesCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.UnsubscribeCmd; -import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; +import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.WebSocketSessionRef; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AggHistoryCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AggKey; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AggTimeSeriesCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataUpdate; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityHistoryCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.GetTsCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.LatestValueCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.TimeSeriesCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -95,7 +94,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc private final Map> subscriptionsBySessionId = new ConcurrentHashMap<>(); @Autowired - private TelemetryWebSocketService wsService; + private WebSocketService wsService; @Autowired private EntityService entityService; @@ -166,7 +165,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc } @Override - public void handleCmd(TelemetryWebSocketSessionRef session, EntityDataCmd cmd) { + public void handleCmd(WebSocketSessionRef session, EntityDataCmd cmd) { TbEntityDataSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId()); if (ctx != null) { log.debug("[{}][{}] Updating existing subscriptions using: {}", session.getSessionId(), cmd.getCmdId(), cmd); @@ -357,7 +356,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc } @Override - public void handleCmd(TelemetryWebSocketSessionRef session, EntityCountCmd cmd) { + public void handleCmd(WebSocketSessionRef session, EntityCountCmd cmd) { TbEntityCountSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId()); if (ctx == null) { ctx = createSubCtx(session, cmd); @@ -377,7 +376,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc } @Override - public void handleCmd(TelemetryWebSocketSessionRef session, AlarmDataCmd cmd) { + public void handleCmd(WebSocketSessionRef session, AlarmDataCmd cmd) { TbAlarmDataSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId()); if (ctx == null) { log.debug("[{}][{}] Creating new alarm subscription using: {}", session.getSessionId(), cmd.getCmdId(), cmd); @@ -468,7 +467,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc } } - private TbEntityDataSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, EntityDataCmd cmd) { + private TbEntityDataSubCtx createSubCtx(WebSocketSessionRef sessionRef, EntityDataCmd cmd) { Map sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmd.getCmdId(), maxEntitiesPerDataSubscription); @@ -479,7 +478,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc return ctx; } - private TbEntityCountSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, EntityCountCmd cmd) { + private TbEntityCountSubCtx createSubCtx(WebSocketSessionRef sessionRef, EntityCountCmd cmd) { Map sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); TbEntityCountSubCtx ctx = new TbEntityCountSubCtx(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmd.getCmdId()); @@ -491,7 +490,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc } - private TbAlarmDataSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) { + private TbAlarmDataSubCtx createSubCtx(WebSocketSessionRef sessionRef, AlarmDataCmd cmd) { Map sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); TbAlarmDataSubCtx ctx = new TbAlarmDataSubCtx(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, alarmService, sessionRef, cmd.getCmdId(), maxEntitiesPerAlarmSubscription, 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 968e15ea4b..e9001f3be3 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 @@ -21,18 +21,19 @@ import org.springframework.context.annotation.Lazy; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import org.thingsboard.common.util.ThingsBoardExecutors; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent; -import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; -import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbApplicationEventListener; +import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent; +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.cluster.TbClusterService; -import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; -import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; +import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; +import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate; +import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -152,7 +153,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer update.getLatestValues().forEach((key, value) -> attrSub.getKeyStates().put(key, value)); break; } - subscriptionUpdateExecutor.submit(() -> subscription.getUpdateConsumer().accept(sessionId, update)); + subscriptionUpdateExecutor.submit(() -> subscription.getUpdateProcessor().accept(subscription, update)); } callback.onSuccess(); } @@ -163,7 +164,16 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer TbSubscription subscription = subscriptionsBySessionId .getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId()); if (subscription != null && subscription.getType() == TbSubscriptionType.ALARMS) { - subscriptionUpdateExecutor.submit(() -> subscription.getUpdateConsumer().accept(sessionId, update)); + subscriptionUpdateExecutor.submit(() -> subscription.getUpdateProcessor().accept(subscription, update)); + } + callback.onSuccess(); + } + + @Override + public void onSubscriptionUpdate(String sessionId, int subscriptionId, NotificationsSubscriptionUpdate update, TbCallback callback) { + TbSubscription subscription = subscriptionsBySessionId.getOrDefault(sessionId, Collections.emptyMap()).get(subscriptionId); + if (subscription != null && subscription.getType() == TbSubscriptionType.NOTIFICATIONS) { + subscriptionUpdateExecutor.submit(() -> subscription.getUpdateProcessor().accept(subscription, update)); } callback.onSuccess(); } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/ReadTsKvQueryInfo.java b/application/src/main/java/org/thingsboard/server/service/subscription/ReadTsKvQueryInfo.java index 28ca2f6729..b80181ab84 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/ReadTsKvQueryInfo.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/ReadTsKvQueryInfo.java @@ -17,7 +17,7 @@ package org.thingsboard.server.service.subscription; import lombok.Data; import org.thingsboard.server.common.data.kv.ReadTsKvQuery; -import org.thingsboard.server.service.telemetry.cmd.v2.AggKey; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AggKey; @Data public class ReadTsKvQueryInfo { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionErrorCode.java b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionErrorCode.java similarity index 96% rename from application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionErrorCode.java rename to application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionErrorCode.java index e5fe16de01..b6d156aa10 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionErrorCode.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionErrorCode.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.sub; +package org.thingsboard.server.service.subscription; public enum SubscriptionErrorCode { diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java index 37c6181910..6724674143 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java @@ -18,9 +18,12 @@ package org.thingsboard.server.service.subscription; import org.springframework.context.ApplicationListener; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; @@ -46,5 +49,8 @@ public interface SubscriptionManagerService extends ApplicationListener data; - public TbAbstractDataSubCtx(String serviceId, TelemetryWebSocketService wsService, + public TbAbstractDataSubCtx(String serviceId, WebSocketService wsService, EntityService entityService, TbLocalSubscriptionService localSubscriptionService, AttributesService attributesService, SubscriptionServiceStatistics stats, - TelemetryWebSocketSessionRef sessionRef, int cmdId) { + WebSocketSessionRef sessionRef, int cmdId) { super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId); this.subToEntityIdMap = new ConcurrentHashMap<>(); } @@ -184,7 +184,7 @@ public abstract class TbAbstractDataSubCtx sendWsMsg(s, subscriptionUpdate, keysType)) + .updateProcessor((sub, subscriptionUpdate) -> sendWsMsg(sub.getSessionId(), subscriptionUpdate, keysType)) .allKeys(false) .keyStates(keyStates) .scope(scope) @@ -215,7 +215,7 @@ public abstract class TbAbstractDataSubCtx sendWsMsg(sessionId, subscriptionUpdate, EntityKeyType.TIME_SERIES, resultToLatestValues)) + .updateProcessor((sub, subscriptionUpdate) -> sendWsMsg(sub.getSessionId(), subscriptionUpdate, EntityKeyType.TIME_SERIES, resultToLatestValues)) .allKeys(false) .keyStates(keyStates) .latestValues(latestValues) diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java index 20e81f0662..d7fde23b31 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java @@ -31,7 +31,6 @@ import org.thingsboard.server.common.data.query.ComplexFilterPredicate; import org.thingsboard.server.common.data.query.DynamicValue; import org.thingsboard.server.common.data.query.DynamicValueSourceType; import org.thingsboard.server.common.data.query.EntityCountQuery; -import org.thingsboard.server.common.data.query.EntityKeyType; import org.thingsboard.server.common.data.query.FilterPredicateType; import org.thingsboard.server.common.data.query.KeyFilter; import org.thingsboard.server.common.data.query.KeyFilterPredicate; @@ -39,10 +38,10 @@ import org.thingsboard.server.common.data.query.SimpleKeyFilterPredicate; import org.thingsboard.server.common.data.query.TsValue; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.entity.EntityService; -import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; -import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; -import org.thingsboard.server.service.telemetry.cmd.v2.CmdUpdate; -import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; +import org.thingsboard.server.service.ws.WebSocketSessionRef; +import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate; +import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; import java.util.ArrayList; import java.util.HashMap; @@ -64,11 +63,11 @@ public abstract class TbAbstractSubCtx { protected final Lock wsLock = new ReentrantLock(true); protected final String serviceId; protected final SubscriptionServiceStatistics stats; - private final TelemetryWebSocketService wsService; + private final WebSocketService wsService; protected final EntityService entityService; protected final TbLocalSubscriptionService localSubscriptionService; protected final AttributesService attributesService; - protected final TelemetryWebSocketSessionRef sessionRef; + protected final WebSocketSessionRef sessionRef; protected final int cmdId; protected final Set subToDynamicValueKeySet; @Getter @@ -80,10 +79,10 @@ public abstract class TbAbstractSubCtx { protected volatile ScheduledFuture refreshTask; protected volatile boolean stopped; - public TbAbstractSubCtx(String serviceId, TelemetryWebSocketService wsService, + public TbAbstractSubCtx(String serviceId, WebSocketService wsService, EntityService entityService, TbLocalSubscriptionService localSubscriptionService, AttributesService attributesService, SubscriptionServiceStatistics stats, - TelemetryWebSocketSessionRef sessionRef, int cmdId) { + WebSocketSessionRef sessionRef, int cmdId) { this.serviceId = serviceId; this.wsService = wsService; this.entityService = entityService; @@ -142,7 +141,7 @@ public abstract class TbAbstractSubCtx { .subscriptionId(subIdx) .tenantId(sessionRef.getSecurityCtx().getTenantId()) .entityId(entityId) - .updateConsumer((s, subscriptionUpdate) -> dynamicValueSubUpdate(s, subscriptionUpdate, dynamicValueKeySubMap)) + .updateProcessor((subscription, subscriptionUpdate) -> dynamicValueSubUpdate(subscription.getSessionId(), subscriptionUpdate, dynamicValueKeySubMap)) .allKeys(false) .keyStates(keyStates) .scope(TbAttributeSubscriptionScope.SERVER_SCOPE) diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java index 5804002c77..c66cdc4828 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java @@ -38,11 +38,11 @@ import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; -import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; -import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUpdate; -import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; -import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; +import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.WebSocketSessionRef; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataUpdate; +import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate; +import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; import java.util.ArrayList; import java.util.Collection; @@ -81,10 +81,10 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx { private int alarmInvocationAttempts; - public TbAlarmDataSubCtx(String serviceId, TelemetryWebSocketService wsService, + public TbAlarmDataSubCtx(String serviceId, WebSocketService wsService, EntityService entityService, TbLocalSubscriptionService localSubscriptionService, AttributesService attributesService, SubscriptionServiceStatistics stats, AlarmService alarmService, - TelemetryWebSocketSessionRef sessionRef, int cmdId, + WebSocketSessionRef sessionRef, int cmdId, int maxEntitiesPerAlarmSubscription, int maxAlarmQueriesPerRefreshInterval) { super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId); this.maxEntitiesPerAlarmSubscription = maxEntitiesPerAlarmSubscription; @@ -175,7 +175,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx { .subscriptionId(subIdx) .tenantId(sessionRef.getSecurityCtx().getTenantId()) .entityId(entityData.getEntityId()) - .updateConsumer(this::sendWsMsg) + .updateProcessor((sub, update) -> sendWsMsg(sub.getSessionId(), update)) .ts(startTs) .build(); localSubscriptionService.addSubscription(subscription); diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmsSubscription.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmsSubscription.java index fba351f755..9d14a56ae4 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmsSubscription.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmsSubscription.java @@ -17,13 +17,10 @@ package org.thingsboard.server.service.subscription; import lombok.Builder; import lombok.Getter; -import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; -import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; +import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate; -import java.util.List; import java.util.function.BiConsumer; public class TbAlarmsSubscription extends TbSubscription { @@ -33,8 +30,8 @@ public class TbAlarmsSubscription extends TbSubscription updateConsumer, long ts) { - super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.ALARMS, updateConsumer); + BiConsumer, AlarmSubscriptionUpdate> updateProcessor, long ts) { + super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.ALARMS, updateProcessor); this.ts = ts; } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAttributeSubscription.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAttributeSubscription.java index ebd3fe548d..2d05f359d7 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAttributeSubscription.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAttributeSubscription.java @@ -19,7 +19,7 @@ import lombok.Builder; import lombok.Getter; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; +import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; import java.util.Map; import java.util.function.BiConsumer; @@ -32,9 +32,9 @@ public class TbAttributeSubscription extends TbSubscription updateConsumer, + BiConsumer, TelemetrySubscriptionUpdate> updateProcessor, boolean allKeys, Map keyStates, TbAttributeSubscriptionScope scope) { - super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.ATTRIBUTES, updateConsumer); + super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.ATTRIBUTES, updateProcessor); this.allKeys = allKeys; this.keyStates = keyStates; this.scope = scope; diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityCountSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityCountSubCtx.java index 7fc87eb17c..f0cc2260b6 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityCountSubCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityCountSubCtx.java @@ -19,18 +19,18 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.entity.EntityService; -import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; -import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUpdate; +import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.WebSocketSessionRef; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUpdate; @Slf4j public class TbEntityCountSubCtx extends TbAbstractSubCtx { private volatile int result; - public TbEntityCountSubCtx(String serviceId, TelemetryWebSocketService wsService, EntityService entityService, + public TbEntityCountSubCtx(String serviceId, WebSocketService wsService, EntityService entityService, TbLocalSubscriptionService localSubscriptionService, AttributesService attributesService, - SubscriptionServiceStatistics stats, TelemetryWebSocketSessionRef sessionRef, int cmdId) { + SubscriptionServiceStatistics stats, WebSocketSessionRef sessionRef, int cmdId) { super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId); } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java index 385661f177..975ecea1c2 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java @@ -27,13 +27,13 @@ import org.thingsboard.server.common.data.query.EntityKeyType; import org.thingsboard.server.common.data.query.TsValue; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.entity.EntityService; -import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; -import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; -import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.TimeSeriesCmd; -import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; +import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.WebSocketSessionRef; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.LatestValueCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.TimeSeriesCmd; +import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; import java.util.ArrayList; import java.util.Collections; @@ -58,9 +58,9 @@ public class TbEntityDataSubCtx extends TbAbstractDataSubCtx { private final int maxEntitiesPerDataSubscription; private Map> latestTsEntityData; - public TbEntityDataSubCtx(String serviceId, TelemetryWebSocketService wsService, EntityService entityService, + public TbEntityDataSubCtx(String serviceId, WebSocketService wsService, EntityService entityService, TbLocalSubscriptionService localSubscriptionService, AttributesService attributesService, - SubscriptionServiceStatistics stats, TelemetryWebSocketSessionRef sessionRef, int cmdId, int maxEntitiesPerDataSubscription) { + SubscriptionServiceStatistics stats, WebSocketSessionRef sessionRef, int cmdId, int maxEntitiesPerDataSubscription) { super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId); this.maxEntitiesPerDataSubscription = maxEntitiesPerDataSubscription; } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubscriptionService.java index 784fa7d842..1b62d3c03b 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubscriptionService.java @@ -15,20 +15,19 @@ */ package org.thingsboard.server.service.subscription; -import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; -import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.UnsubscribeCmd; +import org.thingsboard.server.service.ws.WebSocketSessionRef; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; public interface TbEntityDataSubscriptionService { - void handleCmd(TelemetryWebSocketSessionRef sessionId, EntityDataCmd cmd); + void handleCmd(WebSocketSessionRef sessionId, EntityDataCmd cmd); - void handleCmd(TelemetryWebSocketSessionRef sessionId, EntityCountCmd cmd); + void handleCmd(WebSocketSessionRef sessionId, EntityCountCmd cmd); - void handleCmd(TelemetryWebSocketSessionRef sessionId, AlarmDataCmd cmd); + void handleCmd(WebSocketSessionRef sessionId, AlarmDataCmd cmd); void cancelSubscription(String sessionId, UnsubscribeCmd subscriptionId); diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java index e398111f1f..bdc6d41877 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java @@ -18,8 +18,9 @@ package org.thingsboard.server.service.subscription; import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.common.msg.queue.TbCallback; -import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; -import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; +import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; +import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate; +import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; public interface TbLocalSubscriptionService { @@ -33,6 +34,8 @@ public interface TbLocalSubscriptionService { void onSubscriptionUpdate(String sessionId, AlarmSubscriptionUpdate update, TbCallback callback); + void onSubscriptionUpdate(String sessionId, int subscriptionId, NotificationsSubscriptionUpdate update, TbCallback callback); + void onApplicationEvent(PartitionChangeEvent event); void onApplicationEvent(ClusterTopologyChangeEvent event); diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscription.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscription.java index 95078d8504..a4d9125b65 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscription.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscription.java @@ -33,7 +33,7 @@ public abstract class TbSubscription { private final TenantId tenantId; private final EntityId entityId; private final TbSubscriptionType type; - private final BiConsumer updateConsumer; + private final BiConsumer, T> updateProcessor; @Override public boolean equals(Object o) { diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionType.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionType.java index 86a98ba682..380e74fa78 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionType.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionType.java @@ -16,5 +16,5 @@ package org.thingsboard.server.service.subscription; public enum TbSubscriptionType { - TIMESERIES, ATTRIBUTES, ALARMS + TIMESERIES, ATTRIBUTES, ALARMS, NOTIFICATIONS } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java index d8642f8f9a..603b1903f0 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java @@ -19,7 +19,9 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; @@ -31,6 +33,7 @@ import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; @@ -50,9 +53,10 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesSubscrip import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; -import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; -import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; -import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; +import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; +import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscription; +import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate; +import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; import java.util.ArrayList; import java.util.HashMap; @@ -105,6 +109,12 @@ public class TbSubscriptionUtils { .setTs(alarmSub.getTs()); msgBuilder.setAlarmSub(alarmSubProto.build()); break; + case NOTIFICATIONS: + NotificationsSubscription notificationsSub = (NotificationsSubscription) subscription; + msgBuilder.setNotificationsSub(TransportProtos.NotificationsSubscriptionProto.newBuilder() + .setSub(subscriptionProto) + .setLimit(notificationsSub.getLimit())); + break; } return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build(); } @@ -166,6 +176,18 @@ public class TbSubscriptionUtils { return builder.build(); } + public static NotificationsSubscription fromProto(TransportProtos.NotificationsSubscriptionProto notificationsSub) { + TbSubscriptionProto sub = notificationsSub.getSub(); + return NotificationsSubscription.builder() + .serviceId(sub.getServiceId()) + .sessionId(sub.getSessionId()) + .subscriptionId(sub.getSubscriptionId()) + .tenantId(TenantId.fromUUID(new UUID(sub.getTenantIdMSB(), sub.getTenantIdLSB()))) + .entityId(EntityIdFactory.getByTypeAndUuid(sub.getEntityType(), new UUID(sub.getEntityIdMSB(), sub.getEntityIdLSB()))) + .limit(notificationsSub.getLimit()) + .build(); + } + public static TelemetrySubscriptionUpdate fromProto(TbSubscriptionUpdateProto proto) { if (proto.getErrorCode() > 0) { return new TelemetrySubscriptionUpdate(proto.getSubscriptionId(), SubscriptionErrorCode.forCode(proto.getErrorCode()), proto.getErrorMsg()); @@ -340,4 +362,47 @@ public class TbSubscriptionUtils { msgBuilder.setAlarmDelete(builder); return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build(); } + + public static TransportProtos.ToCoreNotificationMsg notificationsSubUpdateToProto(TbSubscription subscription, NotificationsSubscriptionUpdate update) { + TransportProtos.NotificationsSubscriptionUpdateProto updateProto = TransportProtos.NotificationsSubscriptionUpdateProto.newBuilder() + .setSessionId(subscription.getSessionId()) + .setSubscriptionId(subscription.getSubscriptionId()) + .setNotification(JacksonUtil.toString(update.getNotification())) + .build(); + return TransportProtos.ToCoreNotificationMsg.newBuilder() + .setToLocalSubscriptionServiceMsg(TransportProtos.LocalSubscriptionServiceMsgProto.newBuilder() + .setNotificationsSubUpdate(updateProto) + .build()) + .build(); + } + + public static ToCoreMsg notificationUpdateToProto(TenantId tenantId, UserId recipientId, Notification notification) { + TransportProtos.NotificationUpdateProto updateProto = TransportProtos.NotificationUpdateProto.newBuilder() + .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) + .setRecipientIdMSB(recipientId.getId().getMostSignificantBits()) + .setRecipientIdLSB(recipientId.getId().getLeastSignificantBits()) + .setNotification(JacksonUtil.toString(notification)) + .build(); + return ToCoreMsg.newBuilder() + .setToSubscriptionMgrMsg(SubscriptionMgrMsgProto.newBuilder() + .setNotificationUpdate(updateProto) + .build()) + .build(); + } + + public static ToCoreMsg notificationRequestDeletedToProto(TenantId tenantId, NotificationRequestId notificationRequestId) { + TransportProtos.NotificationRequestDeleteProto deleteProto = TransportProtos.NotificationRequestDeleteProto.newBuilder() + .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) + .setNotificationRequestIdMSB(notificationRequestId.getId().getMostSignificantBits()) + .setNotificationRequestIdLSB(notificationRequestId.getId().getLeastSignificantBits()) + .build(); + return ToCoreMsg.newBuilder() + .setToSubscriptionMgrMsg(SubscriptionMgrMsgProto.newBuilder() + .setNotificationRequestDelete(deleteProto) + .build()) + .build(); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbTimeseriesSubscription.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbTimeseriesSubscription.java index e08f25a280..e343fb4643 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbTimeseriesSubscription.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbTimeseriesSubscription.java @@ -19,7 +19,7 @@ import lombok.Builder; import lombok.Getter; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; +import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; import java.util.Map; import java.util.function.BiConsumer; @@ -39,9 +39,9 @@ public class TbTimeseriesSubscription extends TbSubscription updateConsumer, + BiConsumer, TelemetrySubscriptionUpdate> updateProcessor, boolean allKeys, Map keyStates, long startTime, long endTime, boolean latestValues) { - super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.TIMESERIES, updateConsumer); + super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.TIMESERIES, updateProcessor); this.allKeys = allKeys; this.keyStates = keyStates; this.startTime = startTime; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java index b207de6a9c..c32a3e25dd 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java @@ -21,8 +21,11 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbApplicationEventListener; @@ -38,12 +41,13 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Consumer; +import java.util.function.Supplier; /** * Created by ashvayka on 27.03.18. */ @Slf4j -public abstract class AbstractSubscriptionService extends TbApplicationEventListener{ +public abstract class AbstractSubscriptionService extends TbApplicationEventListener { protected final Set currentPartitions = ConcurrentHashMap.newKeySet(); @@ -86,6 +90,22 @@ public abstract class AbstractSubscriptionService extends TbApplicationEventList } } + protected void forwardToSubscriptionManagerServiceOrSendToCore(TenantId tenantId, EntityId entityId, + Consumer toSubscriptionManagerService, + Supplier toCore) { + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); + if (currentPartitions.contains(tpi)) { + if (subscriptionManagerService.isPresent()) { + toSubscriptionManagerService.accept(subscriptionManagerService.get()); + } else { + log.warn("Possible misconfiguration because subscriptionManagerService is null!"); + } + } else { + TransportProtos.ToCoreMsg toCoreMsg = toCore.get(); + clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null); + } + } + protected void addWsCallback(ListenableFuture saveFuture, Consumer callback) { Futures.addCallback(saveFuture, new FutureCallback() { @Override diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java index 25f412da20..386904918f 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java @@ -164,17 +164,11 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService Alarm alarm = result.getAlarm(); TenantId tenantId = result.getAlarm().getTenantId(); for (EntityId entityId : result.getPropagatedEntitiesList()) { - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); - if (currentPartitions.contains(tpi)) { - if (subscriptionManagerService.isPresent()) { - subscriptionManagerService.get().onAlarmUpdate(tenantId, entityId, alarm, TbCallback.EMPTY); - } else { - log.warn("Possible misconfiguration because subscriptionManagerService is null!"); - } - } else { - TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toAlarmUpdateProto(tenantId, entityId, alarm); - clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null); - } + forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> { + subscriptionManagerService.onAlarmUpdate(tenantId, entityId, alarm, TbCallback.EMPTY); + }, () -> { + return TbSubscriptionUtils.toAlarmUpdateProto(tenantId, entityId, alarm); + }); } }); } @@ -184,17 +178,11 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService Alarm alarm = result.getAlarm(); TenantId tenantId = result.getAlarm().getTenantId(); for (EntityId entityId : result.getPropagatedEntitiesList()) { - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); - if (currentPartitions.contains(tpi)) { - if (subscriptionManagerService.isPresent()) { - subscriptionManagerService.get().onAlarmDeleted(tenantId, entityId, alarm, TbCallback.EMPTY); - } else { - log.warn("Possible misconfiguration because subscriptionManagerService is null!"); - } - } else { - TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toAlarmDeletedProto(tenantId, entityId, alarm); - clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null); - } + forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> { + subscriptionManagerService.onAlarmDeleted(tenantId, entityId, alarm, TbCallback.EMPTY); + }, () -> { + return TbSubscriptionUtils.toAlarmDeletedProto(tenantId, entityId, alarm); + }); } }); } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index a5f66968da..567f5cb9ff 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -40,12 +40,9 @@ import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvLatestRemovingResult; -import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.timeseries.TimeseriesService; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.usagestats.TbApiUsageClient; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; @@ -369,73 +366,49 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } private void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List attributes, boolean notifyDevice) { - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); - if (currentPartitions.contains(tpi)) { - if (subscriptionManagerService.isPresent()) { - subscriptionManagerService.get().onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice, TbCallback.EMPTY); - } else { - log.warn("Possible misconfiguration because subscriptionManagerService is null!"); - } - } else { - TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toAttributesUpdateProto(tenantId, entityId, scope, attributes); - clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null); - } + forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> { + subscriptionManagerService.onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice, TbCallback.EMPTY); + }, () -> { + return TbSubscriptionUtils.toAttributesUpdateProto(tenantId, entityId, scope, attributes); + }); } private void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List keys) { - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); - if (currentPartitions.contains(tpi)) { - if (subscriptionManagerService.isPresent()) { - subscriptionManagerService.get().onAttributesDelete(tenantId, entityId, scope, keys, TbCallback.EMPTY); - } else { - log.warn("Possible misconfiguration because subscriptionManagerService is null!"); - } - } else { - TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toAttributesDeleteProto(tenantId, entityId, scope, keys); - clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null); - } + forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> { + subscriptionManagerService.onAttributesDelete(tenantId, entityId, scope, keys, TbCallback.EMPTY); + }, () -> { + return TbSubscriptionUtils.toAttributesDeleteProto(tenantId, entityId, scope, keys); + }); } private void onTimeSeriesUpdate(TenantId tenantId, EntityId entityId, List ts) { - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); - if (currentPartitions.contains(tpi)) { - if (subscriptionManagerService.isPresent()) { - subscriptionManagerService.get().onTimeSeriesUpdate(tenantId, entityId, ts, TbCallback.EMPTY); - } else { - log.warn("Possible misconfiguration because subscriptionManagerService is null!"); - } - } else { - TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toTimeseriesUpdateProto(tenantId, entityId, ts); - clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null); - } + forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> { + subscriptionManagerService.onTimeSeriesUpdate(tenantId, entityId, ts, TbCallback.EMPTY); + }, () -> { + return TbSubscriptionUtils.toTimeseriesUpdateProto(tenantId, entityId, ts); + }); } private void onTimeSeriesDelete(TenantId tenantId, EntityId entityId, List keys, List ts) { - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); - if (currentPartitions.contains(tpi)) { - if (subscriptionManagerService.isPresent()) { - List updated = new ArrayList<>(); - List deleted = new ArrayList<>(); - - ts.stream().filter(Objects::nonNull).forEach(res -> { - if (res.isRemoved()) { - if (res.getData() != null) { - updated.add(res.getData()); - } else { - deleted.add(res.getKey()); - } + forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> { + List updated = new ArrayList<>(); + List deleted = new ArrayList<>(); + + ts.stream().filter(Objects::nonNull).forEach(res -> { + if (res.isRemoved()) { + if (res.getData() != null) { + updated.add(res.getData()); + } else { + deleted.add(res.getKey()); } - }); + } + }); - subscriptionManagerService.get().onTimeSeriesUpdate(tenantId, entityId, updated, TbCallback.EMPTY); - subscriptionManagerService.get().onTimeSeriesDelete(tenantId, entityId, deleted, TbCallback.EMPTY); - } else { - log.warn("Possible misconfiguration because subscriptionManagerService is null!"); - } - } else { - TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toTimeseriesDeleteProto(tenantId, entityId, keys); - clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null); - } + subscriptionManagerService.onTimeSeriesUpdate(tenantId, entityId, updated, TbCallback.EMPTY); + subscriptionManagerService.onTimeSeriesDelete(tenantId, entityId, deleted, TbCallback.EMPTY); + }, () -> { + return TbSubscriptionUtils.toTimeseriesDeleteProto(tenantId, entityId, keys); + }); } private void addVoidCallback(ListenableFuture saveFuture, final FutureCallback callback) { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java similarity index 82% rename from application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java index d9beb1ed33..86d41456da 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java @@ -47,6 +47,7 @@ import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.util.TenantRateLimitException; +import org.thingsboard.server.exception.UnauthorizedException; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.AccessValidator; @@ -55,26 +56,35 @@ import org.thingsboard.server.service.security.ValidationResult; import org.thingsboard.server.service.security.ValidationResultCode; import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.subscription.SubscriptionErrorCode; import org.thingsboard.server.service.subscription.TbAttributeSubscription; import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope; import org.thingsboard.server.service.subscription.TbEntityDataSubscriptionService; import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; import org.thingsboard.server.service.subscription.TbTimeseriesSubscription; -import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; -import org.thingsboard.server.service.telemetry.cmd.v1.AttributesSubscriptionCmd; -import org.thingsboard.server.service.telemetry.cmd.v1.GetHistoryCmd; -import org.thingsboard.server.service.telemetry.cmd.v1.SubscriptionCmd; -import org.thingsboard.server.service.telemetry.cmd.v1.TelemetryPluginCmd; -import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.CmdUpdate; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; -import org.thingsboard.server.service.telemetry.cmd.v2.UnsubscribeCmd; -import org.thingsboard.server.service.telemetry.exception.UnauthorizedException; -import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; -import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; +import org.thingsboard.server.service.ws.SessionEvent; +import org.thingsboard.server.service.ws.WebSocketMsgEndpoint; +import org.thingsboard.server.service.ws.WebSocketSessionRef; +import org.thingsboard.server.service.ws.WsSessionMetaData; +import org.thingsboard.server.service.ws.notification.cmd.NotificationCmdsWrapper; +import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd; +import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; +import org.thingsboard.server.service.ws.notification.cmd.NotificationsUnsubCmd; +import org.thingsboard.server.service.ws.notification.sub.DefaultNotificationsSubscriptionService; +import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.telemetry.cmd.TelemetryPluginCmdsWrapper; +import org.thingsboard.server.service.ws.telemetry.cmd.v1.AttributesSubscriptionCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v1.GetHistoryCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v1.SubscriptionCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v1.TelemetryPluginCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v1.TimeseriesSubscriptionCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; +import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; import javax.annotation.Nullable; import javax.annotation.PostConstruct; @@ -103,7 +113,7 @@ import java.util.stream.Collectors; @Service @TbCoreComponent @Slf4j -public class DefaultTelemetryWebSocketService implements TelemetryWebSocketService { +public class DefaultWebSocketService implements WebSocketService { public static final int NUMBER_OF_PING_ATTEMPTS = 3; @@ -126,7 +136,10 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi private TbEntityDataSubscriptionService entityDataSubService; @Autowired - private TelemetryWebSocketMsgEndpoint msgEndpoint; + private DefaultNotificationsSubscriptionService notificationsSubService; + + @Autowired + private WebSocketMsgEndpoint msgEndpoint; @Autowired private AccessValidator accessValidator; @@ -177,7 +190,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } @Override - public void handleWebSocketSessionEvent(TelemetryWebSocketSessionRef sessionRef, SessionEvent event) { + public void handleWebSocketSessionEvent(WebSocketSessionRef sessionRef, SessionEvent event) { String sessionId = sessionRef.getSessionId(); log.debug(PROCESSING_MSG, sessionId, event); switch (event.getEventType()) { @@ -197,49 +210,19 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } @Override - public void handleWebSocketMsg(TelemetryWebSocketSessionRef sessionRef, String msg) { + public void handleWebSocketMsg(WebSocketSessionRef sessionRef, String msg) { if (log.isTraceEnabled()) { log.trace("[{}] Processing: {}", sessionRef.getSessionId(), msg); } try { - TelemetryPluginCmdsWrapper cmdsWrapper = jsonMapper.readValue(msg, TelemetryPluginCmdsWrapper.class); - if (cmdsWrapper != null) { - if (cmdsWrapper.getAttrSubCmds() != null) { - cmdsWrapper.getAttrSubCmds().forEach(cmd -> { - if (processSubscription(sessionRef, cmd)) { - handleWsAttributesSubscriptionCmd(sessionRef, cmd); - } - }); - } - if (cmdsWrapper.getTsSubCmds() != null) { - cmdsWrapper.getTsSubCmds().forEach(cmd -> { - if (processSubscription(sessionRef, cmd)) { - handleWsTimeseriesSubscriptionCmd(sessionRef, cmd); - } - }); - } - if (cmdsWrapper.getHistoryCmds() != null) { - cmdsWrapper.getHistoryCmds().forEach(cmd -> handleWsHistoryCmd(sessionRef, cmd)); - } - if (cmdsWrapper.getEntityDataCmds() != null) { - cmdsWrapper.getEntityDataCmds().forEach(cmd -> handleWsEntityDataCmd(sessionRef, cmd)); - } - if (cmdsWrapper.getAlarmDataCmds() != null) { - cmdsWrapper.getAlarmDataCmds().forEach(cmd -> handleWsAlarmDataCmd(sessionRef, cmd)); - } - if (cmdsWrapper.getEntityCountCmds() != null) { - cmdsWrapper.getEntityCountCmds().forEach(cmd -> handleWsEntityCountCmd(sessionRef, cmd)); - } - if (cmdsWrapper.getEntityDataUnsubscribeCmds() != null) { - cmdsWrapper.getEntityDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); - } - if (cmdsWrapper.getAlarmDataUnsubscribeCmds() != null) { - cmdsWrapper.getAlarmDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); - } - if (cmdsWrapper.getEntityCountUnsubscribeCmds() != null) { - cmdsWrapper.getEntityCountUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); - } + switch (sessionRef.getSessionType()) { + case TELEMETRY: + processTelemetryCmds(sessionRef, msg); + break; + case NOTIFICATIONS: + processNotificationCmds(sessionRef, msg); + break; } } catch (IOException e) { log.warn("Failed to decode subscription cmd: {}", e.getMessage(), e); @@ -247,7 +230,81 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } } - private void handleWsEntityDataCmd(TelemetryWebSocketSessionRef sessionRef, EntityDataCmd cmd) { + private void processTelemetryCmds(WebSocketSessionRef sessionRef, String msg) throws JsonProcessingException { + TelemetryPluginCmdsWrapper cmdsWrapper = jsonMapper.readValue(msg, TelemetryPluginCmdsWrapper.class); + if (cmdsWrapper == null) { + return; + } + if (cmdsWrapper.getAttrSubCmds() != null) { + cmdsWrapper.getAttrSubCmds().forEach(cmd -> { + if (processSubscription(sessionRef, cmd)) { + handleWsAttributesSubscriptionCmd(sessionRef, cmd); + } + }); + } + if (cmdsWrapper.getTsSubCmds() != null) { + cmdsWrapper.getTsSubCmds().forEach(cmd -> { + if (processSubscription(sessionRef, cmd)) { + handleWsTimeseriesSubscriptionCmd(sessionRef, cmd); + } + }); + } + if (cmdsWrapper.getHistoryCmds() != null) { + cmdsWrapper.getHistoryCmds().forEach(cmd -> handleWsHistoryCmd(sessionRef, cmd)); + } + if (cmdsWrapper.getEntityDataCmds() != null) { + cmdsWrapper.getEntityDataCmds().forEach(cmd -> handleWsEntityDataCmd(sessionRef, cmd)); + } + if (cmdsWrapper.getAlarmDataCmds() != null) { + cmdsWrapper.getAlarmDataCmds().forEach(cmd -> handleWsAlarmDataCmd(sessionRef, cmd)); + } + if (cmdsWrapper.getEntityCountCmds() != null) { + cmdsWrapper.getEntityCountCmds().forEach(cmd -> handleWsEntityCountCmd(sessionRef, cmd)); + } + if (cmdsWrapper.getEntityDataUnsubscribeCmds() != null) { + cmdsWrapper.getEntityDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); + } + if (cmdsWrapper.getAlarmDataUnsubscribeCmds() != null) { + cmdsWrapper.getAlarmDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); + } + if (cmdsWrapper.getEntityCountUnsubscribeCmds() != null) { + cmdsWrapper.getEntityCountUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); + } + } + + private void processNotificationCmds(WebSocketSessionRef sessionRef, String msg) throws IOException { + NotificationCmdsWrapper cmdsWrapper = jsonMapper.readValue(msg, NotificationCmdsWrapper.class); + if (cmdsWrapper.getUnreadSubCmd() != null) { + handleUnreadNotificationsSubCmd(sessionRef, cmdsWrapper.getUnreadSubCmd()); + } else if (cmdsWrapper.getUnreadUnsubCmd() != null) { + handleUnreadNotificationsUnsubCmd(sessionRef, cmdsWrapper.getUnreadUnsubCmd()); + } else if (cmdsWrapper.getMarkAsReadCmd() != null) { + handleMarkNotificationAsReadCmd(sessionRef, cmdsWrapper.getMarkAsReadCmd()); + } + } + + private void handleUnreadNotificationsSubCmd(WebSocketSessionRef sessionRef, NotificationsSubCmd cmd) { + String sessionId = sessionRef.getSessionId(); + if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) { + notificationsSubService.handleUnreadNotificationsSubCmd(sessionRef, cmd); + } + } + + private void handleUnreadNotificationsUnsubCmd(WebSocketSessionRef sessionRef, NotificationsUnsubCmd cmd) { + String sessionId = sessionRef.getSessionId(); + if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) { + notificationsSubService.handleUnsubCmd(sessionRef, cmd); + } + } + + private void handleMarkNotificationAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationAsReadCmd cmd) { + String sessionId = sessionRef.getSessionId(); + if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) { + notificationsSubService.handleMarkAsReadCmd(sessionRef, cmd); + } + } + + private void handleWsEntityDataCmd(WebSocketSessionRef sessionRef, EntityDataCmd cmd) { String sessionId = sessionRef.getSessionId(); log.debug("[{}] Processing: {}", sessionId, cmd); @@ -257,7 +314,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } } - private void handleWsEntityCountCmd(TelemetryWebSocketSessionRef sessionRef, EntityCountCmd cmd) { + private void handleWsEntityCountCmd(WebSocketSessionRef sessionRef, EntityCountCmd cmd) { String sessionId = sessionRef.getSessionId(); log.debug("[{}] Processing: {}", sessionId, cmd); @@ -267,7 +324,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } } - private void handleWsAlarmDataCmd(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) { + private void handleWsAlarmDataCmd(WebSocketSessionRef sessionRef, AlarmDataCmd cmd) { String sessionId = sessionRef.getSessionId(); log.debug("[{}] Processing: {}", sessionId, cmd); @@ -277,7 +334,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } } - private void handleWsDataUnsubscribeCmd(TelemetryWebSocketSessionRef sessionRef, UnsubscribeCmd cmd) { + private void handleWsDataUnsubscribeCmd(WebSocketSessionRef sessionRef, UnsubscribeCmd cmd) { String sessionId = sessionRef.getSessionId(); log.debug("[{}] Processing: {}", sessionId, cmd); @@ -315,7 +372,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } } - private void processSessionClose(TelemetryWebSocketSessionRef sessionRef) { + private void processSessionClose(WebSocketSessionRef sessionRef) { var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration(); if (tenantProfileConfiguration != null) { String sessionId = "[" + sessionRef.getSessionId() + "]"; @@ -349,7 +406,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } } - private boolean processSubscription(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd) { + private boolean processSubscription(WebSocketSessionRef sessionRef, SubscriptionCmd cmd) { var tenantProfileConfiguration = (DefaultTenantProfileConfiguration) tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration(); String subId = "[" + sessionRef.getSessionId() + "]:[" + cmd.getCmdId() + "]"; @@ -420,7 +477,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi return true; } - private void handleWsAttributesSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, AttributesSubscriptionCmd cmd) { + private void handleWsAttributesSubscriptionCmd(WebSocketSessionRef sessionRef, AttributesSubscriptionCmd cmd) { String sessionId = sessionRef.getSessionId(); log.debug("[{}] Processing: {}", sessionId, cmd); @@ -441,7 +498,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } } - private void handleWsAttributesSubscriptionByKeys(TelemetryWebSocketSessionRef sessionRef, + private void handleWsAttributesSubscriptionByKeys(WebSocketSessionRef sessionRef, AttributesSubscriptionCmd cmd, String sessionId, EntityId entityId, List keys) { FutureCallback> callback = new FutureCallback<>() { @@ -465,7 +522,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi .allKeys(false) .keyStates(subState) .scope(scope) - .updateConsumer(DefaultTelemetryWebSocketService.this::sendWsMsg) + .updateProcessor((subscription, update) -> sendWsMsg(subscription.getSessionId(), update)) .build(); oldSubService.addSubscription(sub); } @@ -492,7 +549,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } } - private void handleWsHistoryCmd(TelemetryWebSocketSessionRef sessionRef, GetHistoryCmd cmd) { + private void handleWsHistoryCmd(WebSocketSessionRef sessionRef, GetHistoryCmd cmd) { String sessionId = sessionRef.getSessionId(); WsSessionMetaData sessionMD = wsSessionsMap.get(sessionId); if (sessionMD == null) { @@ -542,7 +599,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi on(r -> Futures.addCallback(tsService.findAll(sessionRef.getSecurityCtx().getTenantId(), entityId, queries), callback, executor), callback::onFailure)); } - private void handleWsAttributesSubscription(TelemetryWebSocketSessionRef sessionRef, + private void handleWsAttributesSubscription(WebSocketSessionRef sessionRef, AttributesSubscriptionCmd cmd, String sessionId, EntityId entityId) { FutureCallback> callback = new FutureCallback<>() { @Override @@ -563,7 +620,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi .entityId(entityId) .allKeys(true) .keyStates(subState) - .updateConsumer(DefaultTelemetryWebSocketService.this::sendWsMsg) + .updateProcessor((subscription, update) -> sendWsMsg(subscription.getSessionId(), update)) .scope(scope).build(); oldSubService.addSubscription(sub); } @@ -585,7 +642,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } } - private void handleWsTimeseriesSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, TimeseriesSubscriptionCmd cmd) { + private void handleWsTimeseriesSubscriptionCmd(WebSocketSessionRef sessionRef, TimeseriesSubscriptionCmd cmd) { String sessionId = sessionRef.getSessionId(); log.debug("[{}] Processing: {}", sessionId, cmd); @@ -605,7 +662,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } } - private void handleWsTimeseriesSubscriptionByKeys(TelemetryWebSocketSessionRef sessionRef, + private void handleWsTimeseriesSubscriptionByKeys(WebSocketSessionRef sessionRef, TimeseriesSubscriptionCmd cmd, String sessionId, EntityId entityId) { long startTs; if (cmd.getTimeWindow() > 0) { @@ -629,7 +686,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } } - private void handleWsTimeseriesSubscription(TelemetryWebSocketSessionRef sessionRef, + private void handleWsTimeseriesSubscription(WebSocketSessionRef sessionRef, TimeseriesSubscriptionCmd cmd, String sessionId, EntityId entityId) { FutureCallback> callback = new FutureCallback>() { @Override @@ -644,7 +701,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi .subscriptionId(cmd.getCmdId()) .tenantId(sessionRef.getSecurityCtx().getTenantId()) .entityId(entityId) - .updateConsumer(DefaultTelemetryWebSocketService.this::sendWsMsg) + .updateProcessor((subscription, update) -> sendWsMsg(subscription.getSessionId(), update)) .allKeys(true) .keyStates(subState).build(); oldSubService.addSubscription(sub); @@ -667,7 +724,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi on(r -> Futures.addCallback(tsService.findAllLatest(sessionRef.getSecurityCtx().getTenantId(), entityId), callback, executor), callback::onFailure)); } - private FutureCallback> getSubscriptionCallback(final TelemetryWebSocketSessionRef sessionRef, final TimeseriesSubscriptionCmd cmd, final String sessionId, final EntityId entityId, final long startTs, final List keys) { + private FutureCallback> getSubscriptionCallback(final WebSocketSessionRef sessionRef, final TimeseriesSubscriptionCmd cmd, final String sessionId, final EntityId entityId, final long startTs, final List keys) { return new FutureCallback<>() { @Override public void onSuccess(List data) { @@ -682,7 +739,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi .subscriptionId(cmd.getCmdId()) .tenantId(sessionRef.getSecurityCtx().getTenantId()) .entityId(entityId) - .updateConsumer(DefaultTelemetryWebSocketService.this::sendWsMsg) + .updateProcessor((subscription, update) -> sendWsMsg(subscription.getSessionId(), update)) .allKeys(false) .keyStates(subState).build(); oldSubService.addSubscription(sub); @@ -702,7 +759,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi }; } - private void unsubscribe(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd, String sessionId) { + private void unsubscribe(WebSocketSessionRef sessionRef, SubscriptionCmd cmd, String sessionId) { if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty()) { oldSubService.cancelAllSessionSubscriptions(sessionId); } else { @@ -710,7 +767,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } } - private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, EntityDataCmd cmd) { + private boolean validateSubscriptionCmd(WebSocketSessionRef sessionRef, EntityDataCmd cmd) { if (cmd.getCmdId() < 0) { TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, "Cmd id is negative value!"); @@ -725,7 +782,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi return true; } - private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, EntityCountCmd cmd) { + private boolean validateSubscriptionCmd(WebSocketSessionRef sessionRef, EntityCountCmd cmd) { if (cmd.getCmdId() < 0) { TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, "Cmd id is negative value!"); @@ -739,7 +796,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi return true; } - private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) { + private boolean validateSubscriptionCmd(WebSocketSessionRef sessionRef, AlarmDataCmd cmd) { if (cmd.getCmdId() < 0) { TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, "Cmd id is negative value!"); @@ -754,7 +811,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi return true; } - private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd) { + private boolean validateSubscriptionCmd(WebSocketSessionRef sessionRef, SubscriptionCmd cmd) { if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty()) { TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, "Device id is empty!"); @@ -764,11 +821,11 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi return true; } - private boolean validateSessionMetadata(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd, String sessionId) { + private boolean validateSessionMetadata(WebSocketSessionRef sessionRef, SubscriptionCmd cmd, String sessionId) { return validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId); } - private boolean validateSessionMetadata(TelemetryWebSocketSessionRef sessionRef, int cmdId, String sessionId) { + private boolean validateSessionMetadata(WebSocketSessionRef sessionRef, int cmdId, String sessionId) { WsSessionMetaData sessionMD = wsSessionsMap.get(sessionId); if (sessionMD == null) { log.warn("[{}] Session meta data not found. ", sessionId); @@ -781,15 +838,15 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } } - private void sendWsMsg(TelemetryWebSocketSessionRef sessionRef, EntityDataUpdate update) { + private void sendWsMsg(WebSocketSessionRef sessionRef, EntityDataUpdate update) { sendWsMsg(sessionRef, update.getCmdId(), update); } - private void sendWsMsg(TelemetryWebSocketSessionRef sessionRef, TelemetrySubscriptionUpdate update) { + private void sendWsMsg(WebSocketSessionRef sessionRef, TelemetrySubscriptionUpdate update) { sendWsMsg(sessionRef, update.getSubscriptionId(), update); } - private void sendWsMsg(TelemetryWebSocketSessionRef sessionRef, int cmdId, Object update) { + private void sendWsMsg(WebSocketSessionRef sessionRef, int cmdId, Object update) { try { String msg = jsonMapper.writeValueAsString(update); executor.submit(() -> { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java index 035e262a53..c35864ee92 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java @@ -41,6 +41,4 @@ public interface InternalTelemetryService extends RuleEngineTelemetryService { void deleteLatestInternal(TenantId tenantId, EntityId entityId, List keys, FutureCallback callback); - - } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/SessionEvent.java b/application/src/main/java/org/thingsboard/server/service/ws/SessionEvent.java similarity index 96% rename from application/src/main/java/org/thingsboard/server/service/telemetry/SessionEvent.java rename to application/src/main/java/org/thingsboard/server/service/ws/SessionEvent.java index 527f5dc667..6a1bc2bd7d 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/SessionEvent.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/SessionEvent.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry; +package org.thingsboard.server.service.ws; import lombok.Getter; import lombok.ToString; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketMsgEndpoint.java similarity index 63% rename from application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java rename to application/src/main/java/org/thingsboard/server/service/ws/WebSocketMsgEndpoint.java index d71777ae49..4e178f88d2 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketMsgEndpoint.java @@ -13,20 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry; +package org.thingsboard.server.service.ws; import org.springframework.web.socket.CloseStatus; +import org.thingsboard.server.service.ws.WebSocketSessionRef; import java.io.IOException; /** * Created by ashvayka on 27.03.18. */ -public interface TelemetryWebSocketMsgEndpoint { +public interface WebSocketMsgEndpoint { - void send(TelemetryWebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException; + void send(WebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException; - void sendPing(TelemetryWebSocketSessionRef sessionRef, long currentTime) throws IOException; + void sendPing(WebSocketSessionRef sessionRef, long currentTime) throws IOException; - void close(TelemetryWebSocketSessionRef sessionRef, CloseStatus withReason) throws IOException; + void close(WebSocketSessionRef sessionRef, CloseStatus withReason) throws IOException; } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketSessionRef.java b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionRef.java similarity index 70% rename from application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketSessionRef.java rename to application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionRef.java index da8a8962e6..cb4eea6101 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketSessionRef.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionRef.java @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry; +package org.thingsboard.server.service.ws; +import lombok.Builder; import lombok.Getter; +import lombok.RequiredArgsConstructor; import org.thingsboard.server.service.security.model.SecurityUser; import java.net.InetSocketAddress; @@ -25,34 +27,25 @@ import java.util.concurrent.atomic.AtomicInteger; /** * Created by ashvayka on 27.03.18. */ -public class TelemetryWebSocketSessionRef { +@RequiredArgsConstructor +@Builder +@Getter +public class WebSocketSessionRef { private static final long serialVersionUID = 1L; - @Getter private final String sessionId; - @Getter private final SecurityUser securityCtx; - @Getter private final InetSocketAddress localAddress; - @Getter private final InetSocketAddress remoteAddress; - @Getter - private final AtomicInteger sessionSubIdSeq; - - public TelemetryWebSocketSessionRef(String sessionId, SecurityUser securityCtx, InetSocketAddress localAddress, InetSocketAddress remoteAddress) { - this.sessionId = sessionId; - this.securityCtx = securityCtx; - this.localAddress = localAddress; - this.remoteAddress = remoteAddress; - this.sessionSubIdSeq = new AtomicInteger(); - } + private final WebSocketSessionType sessionType; + private final AtomicInteger sessionSubIdSeq = new AtomicInteger(); @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - TelemetryWebSocketSessionRef that = (TelemetryWebSocketSessionRef) o; + WebSocketSessionRef that = (WebSocketSessionRef) o; return Objects.equals(sessionId, that.sessionId); } @@ -63,10 +56,11 @@ public class TelemetryWebSocketSessionRef { @Override public String toString() { - return "TelemetryWebSocketSessionRef{" + + return "WebSocketSessionRef{" + "sessionId='" + sessionId + '\'' + ", localAddress=" + localAddress + ", remoteAddress=" + remoteAddress + + ", sessionType=" + sessionType + '}'; } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionType.java b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionType.java new file mode 100644 index 0000000000..3f55b921b4 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionType.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; +import java.util.Optional; + +@RequiredArgsConstructor +@Getter +public enum WebSocketSessionType { + TELEMETRY("telemetry"), + NOTIFICATIONS("notifications"); + + private final String name; + + public static Optional forName(String name) { + return Arrays.stream(values()) + .filter(sessionType -> sessionType.getName().equals(name)) + .findFirst(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/WsSessionMetaData.java b/application/src/main/java/org/thingsboard/server/service/ws/WsSessionMetaData.java similarity index 80% rename from application/src/main/java/org/thingsboard/server/service/telemetry/WsSessionMetaData.java rename to application/src/main/java/org/thingsboard/server/service/ws/WsSessionMetaData.java index ab353c0581..0567a067ed 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/WsSessionMetaData.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/WsSessionMetaData.java @@ -13,27 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry; +package org.thingsboard.server.service.ws; /** * Created by ashvayka on 27.03.18. */ public class WsSessionMetaData { - private TelemetryWebSocketSessionRef sessionRef; + private WebSocketSessionRef sessionRef; private long lastActivityTime; - public WsSessionMetaData(TelemetryWebSocketSessionRef sessionRef) { + public WsSessionMetaData(WebSocketSessionRef sessionRef) { super(); this.sessionRef = sessionRef; this.lastActivityTime = System.currentTimeMillis(); } - public TelemetryWebSocketSessionRef getSessionRef() { + public WebSocketSessionRef getSessionRef() { return sessionRef; } - public void setSessionRef(TelemetryWebSocketSessionRef sessionRef) { + public void setSessionRef(WebSocketSessionRef sessionRef) { this.sessionRef = sessionRef; } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationAsReadCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationAsReadCmd.java new file mode 100644 index 0000000000..50844623eb --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationAsReadCmd.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws.notification.cmd; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.UUID; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MarkNotificationAsReadCmd { + private int cmdId; + private UUID notificationId; +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java new file mode 100644 index 0000000000..367a6cc450 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws.notification.cmd; + +import lombok.Data; +import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd; +import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; + +@Data +public class NotificationCmdsWrapper { + private NotificationsSubCmd unreadSubCmd; + private NotificationsUnsubCmd unreadUnsubCmd; + + private MarkNotificationAsReadCmd markAsReadCmd; +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java new file mode 100644 index 0000000000..376b34b18e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws.notification.cmd; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class NotificationsSubCmd { + private int cmdId; + private int limit; +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java new file mode 100644 index 0000000000..1daeb570cc --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws.notification.cmd; + +import lombok.Data; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; + +@Data +public class NotificationsUnsubCmd implements UnsubscribeCmd { + private int cmdId; +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsUpdate.java new file mode 100644 index 0000000000..0597612569 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsUpdate.java @@ -0,0 +1,54 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws.notification.cmd; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Getter; +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdateType; + +import java.util.Collection; +import java.util.List; + +@Getter +public class UnreadNotificationsUpdate extends CmdUpdate { + + private final Collection notifications; + private final Notification update; + private final int totalUnreadCount; + + @Builder + @JsonCreator + public UnreadNotificationsUpdate(@JsonProperty("cmdId") int cmdId, @JsonProperty("errorCode") int errorCode, + @JsonProperty("errorMsg") String errorMsg, + @JsonProperty("notifications") Collection notifications, + @JsonProperty("update") Notification update, + @JsonProperty("totalUnreadCount") int totalUnreadCount) { + super(cmdId, errorCode, errorMsg); + this.notifications = notifications; + this.update = update; + this.totalUnreadCount = totalUnreadCount; + } + + @Override + public CmdUpdateType getCmdUpdateType() { + return CmdUpdateType.NOTIFICATIONS; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/DefaultNotificationsSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/DefaultNotificationsSubscriptionService.java new file mode 100644 index 0000000000..119c14dc23 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/DefaultNotificationsSubscriptionService.java @@ -0,0 +1,183 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws.notification.sub; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.id.IdBased; +import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationStatus; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.dao.notification.NotificationService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.notification.NotificationProcessingService; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; +import org.thingsboard.server.service.subscription.TbSubscriptionUtils; +import org.thingsboard.server.service.telemetry.AbstractSubscriptionService; +import org.thingsboard.server.service.ws.WebSocketSessionRef; +import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd; +import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; +import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate; +import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +@Service +@TbCoreComponent +@Slf4j +public class DefaultNotificationsSubscriptionService extends AbstractSubscriptionService implements NotificationsSubscriptionService { + + private final WebSocketService wsService; + private final TbLocalSubscriptionService localSubscriptionService; + private final NotificationService notificationService; + private final TbServiceInfoProvider serviceInfoProvider; + private final NotificationsTopicService notificationsTopicService; + private final NotificationProcessingService notificationProcessingService; + + public DefaultNotificationsSubscriptionService(TbClusterService clusterService, PartitionService partitionService, + @Lazy WebSocketService wsService, TbLocalSubscriptionService localSubscriptionService, + NotificationService notificationService, TbServiceInfoProvider serviceInfoProvider, + NotificationsTopicService notificationsTopicService, + @Lazy NotificationProcessingService notificationProcessingService) { + super(clusterService, partitionService); + this.wsService = wsService; + this.localSubscriptionService = localSubscriptionService; + this.notificationService = notificationService; + this.serviceInfoProvider = serviceInfoProvider; + this.notificationsTopicService = notificationsTopicService; + this.notificationProcessingService = notificationProcessingService; + } + + @Override + public void handleUnreadNotificationsSubCmd(WebSocketSessionRef sessionRef, NotificationsSubCmd cmd) { + SecurityUser user = sessionRef.getSecurityCtx(); + NotificationsSubscription subscription = NotificationsSubscription.builder() + .serviceId(serviceInfoProvider.getServiceId()) + .sessionId(sessionRef.getSessionId()) + .subscriptionId(cmd.getCmdId()) + .tenantId(user.getTenantId()) + .entityId(user.getId()) + .updateProcessor(this::handleSubscriptionUpdate) + .limit(cmd.getLimit()) + .build(); + localSubscriptionService.addSubscription(subscription); + + fetchUnreadNotifications(subscription); + sendUpdate(sessionRef.getSessionId(), subscription.createFullUpdate()); + } + + @Override + public void handleMarkAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationAsReadCmd cmd) { + NotificationId notificationId = new NotificationId(cmd.getNotificationId()); + notificationProcessingService.markNotificationAsRead(sessionRef.getSecurityCtx(), notificationId); + } + + @Override + public void handleUnsubCmd(WebSocketSessionRef sessionRef, UnsubscribeCmd cmd) { + localSubscriptionService.cancelSubscription(sessionRef.getSessionId(), cmd.getCmdId()); + } + + private void fetchUnreadNotifications(NotificationsSubscription subscription) { + PageData notifications = notificationService.findLatestUnreadNotificationsByUserId(subscription.getTenantId(), + (UserId) subscription.getEntityId(), subscription.getLimit()); + subscription.getUnreadNotifications().clear(); + subscription.getUnreadNotifications().putAll(notifications.getData().stream().collect(Collectors.toMap(IdBased::getUuidId, n -> n))); + subscription.getTotalUnreadCount().set((int) notifications.getTotalElements()); + } + + @Override + public void onNewNotification(TenantId tenantId, UserId recipientId, Notification notification) { + onNotificationUpdate(tenantId, recipientId, notification); + } + + @Override + public void onNotificationUpdated(TenantId tenantId, UserId recipientId, Notification notification) { + onNotificationUpdate(tenantId, recipientId, notification); + } + + private void onNotificationUpdate(TenantId tenantId, UserId recipientId, Notification notification) { + forwardToSubscriptionManagerServiceOrSendToCore(tenantId, recipientId, subscriptionManagerService -> { + subscriptionManagerService.onNotificationUpdate(tenantId, recipientId, notification, TbCallback.EMPTY); + }, () -> { + return TbSubscriptionUtils.notificationUpdateToProto(tenantId, recipientId, notification); + }); + } + + @Override + public void onNotificationRequestDeleted(TenantId tenantId, NotificationRequestId notificationRequestId) { + TransportProtos.ToCoreMsg notificationRequestDeletedProto = TbSubscriptionUtils.notificationRequestDeletedToProto(tenantId, notificationRequestId); + Set coreServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_CORE)); + for (String serviceId : coreServices) { + TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); + clusterService.pushMsgToCore(tpi, UUID.randomUUID(), notificationRequestDeletedProto, null); + } + } + + + private void handleSubscriptionUpdate(NotificationsSubscription subscription, NotificationsSubscriptionUpdate subscriptionUpdate) { + if (subscriptionUpdate.getNotification() != null) { + Notification notification = subscriptionUpdate.getNotification(); + if (notification.getStatus() == NotificationStatus.READ) { + fetchUnreadNotifications(subscription); + sendUpdate(subscription.getSessionId(), subscription.createFullUpdate()); + } else { + Notification previous = subscription.getUnreadNotifications().put(notification.getUuidId(), notification); + if (previous == null) { + subscription.getTotalUnreadCount().incrementAndGet(); + Set beyondLimit = subscription.getUnreadNotifications().keySet().stream() + .skip(subscription.getLimit()) + .collect(Collectors.toSet()); + beyondLimit.forEach(notificationId -> subscription.getUnreadNotifications().remove(notificationId)); + } + sendUpdate(subscription.getSessionId(), subscription.createPartialUpdate(notification)); + } + } else if (subscriptionUpdate.isNotificationRequestDeleted()) { + if (subscription.getUnreadNotifications().values().stream() + .anyMatch(notification -> notification.getRequestId().equals(subscriptionUpdate.getNotificationRequestId()))) { + fetchUnreadNotifications(subscription); + sendUpdate(subscription.getSessionId(), subscription.createFullUpdate()); + } + } + } + + private void sendUpdate(String sessionId, UnreadNotificationsUpdate update) { + wsService.sendWsMsg(sessionId, update);; + } + + @Override + protected String getExecutorPrefix() { + return "notification"; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java new file mode 100644 index 0000000000..872e4a9671 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java @@ -0,0 +1,64 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws.notification.sub; + +import lombok.Builder; +import lombok.Getter; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.service.subscription.TbSubscription; +import org.thingsboard.server.service.subscription.TbSubscriptionType; +import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiConsumer; + +@Getter +public class NotificationsSubscription extends TbSubscription { + + private final Map unreadNotifications = new LinkedHashMap<>(); + private final int limit; + private final AtomicInteger totalUnreadCount = new AtomicInteger(); + + @Builder + public NotificationsSubscription(String serviceId, String sessionId, int subscriptionId, TenantId tenantId, EntityId entityId, + BiConsumer updateProcessor, + int limit) { + super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.NOTIFICATIONS, updateProcessor); + this.limit = limit; + } + + public UnreadNotificationsUpdate createFullUpdate() { + return UnreadNotificationsUpdate.builder() + .cmdId(getSubscriptionId()) + .notifications(unreadNotifications.values()) + .totalUnreadCount(totalUnreadCount.get()) + .build(); + } + + public UnreadNotificationsUpdate createPartialUpdate(Notification notification) { + return UnreadNotificationsUpdate.builder() + .cmdId(getSubscriptionId()) + .update(notification) + .totalUnreadCount(totalUnreadCount.get()) + .build(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionService.java new file mode 100644 index 0000000000..0afd8dcd39 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionService.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws.notification.sub; + +import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.service.ws.WebSocketSessionRef; +import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd; +import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; + +public interface NotificationsSubscriptionService { + + void handleUnreadNotificationsSubCmd(WebSocketSessionRef sessionRef, NotificationsSubCmd cmd); + + void handleMarkAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationAsReadCmd cmd); + + void handleUnsubCmd(WebSocketSessionRef sessionRef, UnsubscribeCmd cmd); + + + void onNewNotification(TenantId tenantId, UserId recipientId, Notification notification); + + void onNotificationUpdated(TenantId tenantId, UserId recipientId, Notification notification); + + void onNotificationRequestDeleted(TenantId tenantId, NotificationRequestId notificationRequestId); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionUpdate.java new file mode 100644 index 0000000000..c674e149d9 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionUpdate.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws.notification.sub; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.notification.Notification; + +@Data +@Builder +public class NotificationsSubscriptionUpdate { + + private final Notification notification; + + private final boolean notificationRequestDeleted; + private final NotificationRequestId notificationRequestId; + +} diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryFeature.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/TelemetryFeature.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryFeature.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/TelemetryFeature.java index b0a63970a5..ea3b1d8752 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryFeature.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/TelemetryFeature.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry; +package org.thingsboard.server.service.ws.telemetry; /** * Created by ashvayka on 08.05.17. diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketTextMsg.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/TelemetryWebSocketTextMsg.java similarity index 82% rename from application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketTextMsg.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/TelemetryWebSocketTextMsg.java index 44f2ea0943..9c4087e0a0 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketTextMsg.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/TelemetryWebSocketTextMsg.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry; +package org.thingsboard.server.service.ws.telemetry; import lombok.Data; +import org.thingsboard.server.service.ws.WebSocketSessionRef; /** * Created by ashvayka on 27.03.18. @@ -23,7 +24,7 @@ import lombok.Data; @Data public class TelemetryWebSocketTextMsg { - private final TelemetryWebSocketSessionRef sessionRef; + private final WebSocketSessionRef sessionRef; private final String payload; } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/WebSocketService.java similarity index 63% rename from application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketService.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/WebSocketService.java index 510283b31d..d0d8dab59e 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/WebSocketService.java @@ -13,21 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry; +package org.thingsboard.server.service.ws.telemetry; import org.springframework.web.socket.CloseStatus; -import org.thingsboard.server.service.telemetry.cmd.v2.CmdUpdate; -import org.thingsboard.server.service.telemetry.cmd.v2.DataUpdate; -import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate; +import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; +import org.thingsboard.server.service.ws.SessionEvent; +import org.thingsboard.server.service.ws.WebSocketSessionRef; /** * Created by ashvayka on 27.03.18. */ -public interface TelemetryWebSocketService { +public interface WebSocketService { - void handleWebSocketSessionEvent(TelemetryWebSocketSessionRef sessionRef, SessionEvent sessionEvent); + void handleWebSocketSessionEvent(WebSocketSessionRef sessionRef, SessionEvent sessionEvent); - void handleWebSocketMsg(TelemetryWebSocketSessionRef sessionRef, String msg); + void handleWebSocketMsg(WebSocketSessionRef sessionRef, String msg); void sendWsMsg(String sessionId, TelemetrySubscriptionUpdate update); diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/TelemetryPluginCmdsWrapper.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/TelemetryPluginCmdsWrapper.java similarity index 62% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/TelemetryPluginCmdsWrapper.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/TelemetryPluginCmdsWrapper.java index 0c46f90610..99840e271a 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/TelemetryPluginCmdsWrapper.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/TelemetryPluginCmdsWrapper.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd; +package org.thingsboard.server.service.ws.telemetry.cmd; import lombok.Data; -import org.thingsboard.server.service.telemetry.cmd.v1.AttributesSubscriptionCmd; -import org.thingsboard.server.service.telemetry.cmd.v1.GetHistoryCmd; -import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUnsubscribeCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUnsubscribeCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v1.AttributesSubscriptionCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v1.GetHistoryCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v1.TimeseriesSubscriptionCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUnsubscribeCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataUnsubscribeCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUnsubscribeCmd; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/AttributesSubscriptionCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/AttributesSubscriptionCmd.java similarity index 87% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/AttributesSubscriptionCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/AttributesSubscriptionCmd.java index f5e2630af8..b6ef06f2c9 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/AttributesSubscriptionCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/AttributesSubscriptionCmd.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v1; +package org.thingsboard.server.service.ws.telemetry.cmd.v1; import lombok.NoArgsConstructor; -import org.thingsboard.server.service.telemetry.TelemetryFeature; +import org.thingsboard.server.service.ws.telemetry.TelemetryFeature; /** * @author Andrew Shvayka diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/GetHistoryCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/GetHistoryCmd.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/GetHistoryCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/GetHistoryCmd.java index b74f2f7ea7..4429498cd1 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/GetHistoryCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/GetHistoryCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v1; +package org.thingsboard.server.service.ws.telemetry.cmd.v1; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/SubscriptionCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/SubscriptionCmd.java similarity index 90% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/SubscriptionCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/SubscriptionCmd.java index 0c912fac30..f786c43ea3 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/SubscriptionCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/SubscriptionCmd.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v1; +package org.thingsboard.server.service.ws.telemetry.cmd.v1; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import org.thingsboard.server.service.telemetry.TelemetryFeature; +import org.thingsboard.server.service.ws.telemetry.TelemetryFeature; @NoArgsConstructor @AllArgsConstructor diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/TelemetryPluginCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TelemetryPluginCmd.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/TelemetryPluginCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TelemetryPluginCmd.java index 7423e9e4b0..71b1b7eb7b 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/TelemetryPluginCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TelemetryPluginCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v1; +package org.thingsboard.server.service.ws.telemetry.cmd.v1; /** * @author Andrew Shvayka diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/TimeseriesSubscriptionCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TimeseriesSubscriptionCmd.java similarity index 89% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/TimeseriesSubscriptionCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TimeseriesSubscriptionCmd.java index 309a9297d6..facd48f6c1 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v1/TimeseriesSubscriptionCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TimeseriesSubscriptionCmd.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v1; +package org.thingsboard.server.service.ws.telemetry.cmd.v1; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import org.thingsboard.server.service.telemetry.TelemetryFeature; +import org.thingsboard.server.service.ws.telemetry.TelemetryFeature; /** * @author Andrew Shvayka diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggHistoryCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AggHistoryCmd.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggHistoryCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AggHistoryCmd.java index 423c55bd0d..0ffb9afa6f 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggHistoryCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AggHistoryCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggKey.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AggKey.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggKey.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AggKey.java index d567149d01..6ffb303d73 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggKey.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AggKey.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import lombok.Data; import org.thingsboard.server.common.data.kv.Aggregation; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggTimeSeriesCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AggTimeSeriesCmd.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggTimeSeriesCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AggTimeSeriesCmd.java index 1f4d88d2c3..37f35258e6 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggTimeSeriesCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AggTimeSeriesCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AlarmDataCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataCmd.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AlarmDataCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataCmd.java index c2c01f8f77..1caf75faa5 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AlarmDataCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AlarmDataUnsubscribeCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataUnsubscribeCmd.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AlarmDataUnsubscribeCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataUnsubscribeCmd.java index 153935b7a7..9cee3f707d 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AlarmDataUnsubscribeCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataUnsubscribeCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AlarmDataUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataUpdate.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AlarmDataUpdate.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataUpdate.java index afd9d12b16..cdcdd41b54 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AlarmDataUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataUpdate.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; @@ -21,7 +21,7 @@ import lombok.Getter; import lombok.ToString; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.query.AlarmData; -import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; +import org.thingsboard.server.service.subscription.SubscriptionErrorCode; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/CmdUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/CmdUpdate.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/CmdUpdate.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/CmdUpdate.java index 075c80b92b..4ce684e8b2 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/CmdUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/CmdUpdate.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.AllArgsConstructor; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/CmdUpdateType.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/CmdUpdateType.java similarity index 87% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/CmdUpdateType.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/CmdUpdateType.java index cd78e760a6..76b63ad9b8 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/CmdUpdateType.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/CmdUpdateType.java @@ -13,10 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; public enum CmdUpdateType { ENTITY_DATA, ALARM_DATA, - COUNT_DATA + COUNT_DATA, + NOTIFICATIONS } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/DataCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/DataCmd.java similarity index 89% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/DataCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/DataCmd.java index b93af17d89..acbd5a8d1f 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/DataCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/DataCmd.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import lombok.Data; import lombok.Getter; -import lombok.NoArgsConstructor; @Data public class DataCmd { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/DataUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/DataUpdate.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/DataUpdate.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/DataUpdate.java index eda60b87b4..19855158d3 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/DataUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/DataUpdate.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import lombok.Getter; import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; +import org.thingsboard.server.service.subscription.SubscriptionErrorCode; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityCountCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountCmd.java similarity index 90% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityCountCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountCmd.java index c71884df38..d543d0fccc 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityCountCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountCmd.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import org.thingsboard.server.common.data.query.EntityCountQuery; -import org.thingsboard.server.common.data.query.EntityDataQuery; public class EntityCountCmd extends DataCmd { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityCountUnsubscribeCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountUnsubscribeCmd.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityCountUnsubscribeCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountUnsubscribeCmd.java index c8daf30833..3bbbee784a 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityCountUnsubscribeCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountUnsubscribeCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityCountUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountUpdate.java similarity index 85% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityCountUpdate.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountUpdate.java index de61d0e5fb..42f87610a3 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityCountUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountUpdate.java @@ -13,17 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import lombok.ToString; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.query.EntityData; -import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; - -import java.util.List; +import org.thingsboard.server.service.subscription.SubscriptionErrorCode; @ToString public class EntityCountUpdate extends CmdUpdate { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityDataCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataCmd.java similarity index 97% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityDataCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataCmd.java index 8a187afa7b..e6daf4d6be 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityDataCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityDataUnsubscribeCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataUnsubscribeCmd.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityDataUnsubscribeCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataUnsubscribeCmd.java index 45aa0c5216..1f0e1762fe 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityDataUnsubscribeCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataUnsubscribeCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityDataUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataUpdate.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityDataUpdate.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataUpdate.java index 8da696ce9c..03855a8aee 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityDataUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataUpdate.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; @@ -21,7 +21,7 @@ import lombok.Getter; import lombok.ToString; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.query.EntityData; -import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; +import org.thingsboard.server.service.subscription.SubscriptionErrorCode; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityHistoryCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityHistoryCmd.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityHistoryCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityHistoryCmd.java index 946775889f..147715a1ce 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityHistoryCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityHistoryCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import lombok.Data; import org.thingsboard.server.common.data.kv.Aggregation; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/GetTsCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/GetTsCmd.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/GetTsCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/GetTsCmd.java index 724c2d6fd2..a5ba6fea4c 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/GetTsCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/GetTsCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import org.thingsboard.server.common.data.kv.Aggregation; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/LatestValueCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/LatestValueCmd.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/LatestValueCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/LatestValueCmd.java index 184026ac54..5bce180204 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/LatestValueCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/LatestValueCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import lombok.Data; import org.thingsboard.server.common.data.query.EntityKey; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/TimeSeriesCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/TimeSeriesCmd.java similarity index 95% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/TimeSeriesCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/TimeSeriesCmd.java index daf9c32fbd..6ea18baf4d 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/TimeSeriesCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/TimeSeriesCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/UnsubscribeCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/UnsubscribeCmd.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/UnsubscribeCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/UnsubscribeCmd.java index eec564e7a8..790bfc4416 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/UnsubscribeCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/UnsubscribeCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.cmd.v2; +package org.thingsboard.server.service.ws.telemetry.cmd.v2; public interface UnsubscribeCmd { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/sub/AlarmSubscriptionUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/AlarmSubscriptionUpdate.java similarity index 85% rename from application/src/main/java/org/thingsboard/server/service/telemetry/sub/AlarmSubscriptionUpdate.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/AlarmSubscriptionUpdate.java index 3f4bc9ce1e..34528906ed 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/sub/AlarmSubscriptionUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/AlarmSubscriptionUpdate.java @@ -13,19 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.sub; +package org.thingsboard.server.service.ws.telemetry.sub; import lombok.Getter; import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.common.data.query.AlarmData; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import java.util.stream.Collectors; +import org.thingsboard.server.service.subscription.SubscriptionErrorCode; public class AlarmSubscriptionUpdate { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionState.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/SubscriptionState.java similarity index 95% rename from application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionState.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/SubscriptionState.java index 1343527444..7c11361bc7 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionState.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/SubscriptionState.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.sub; +package org.thingsboard.server.service.ws.telemetry.sub; import lombok.AllArgsConstructor; import lombok.Getter; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.service.telemetry.TelemetryFeature; +import org.thingsboard.server.service.ws.telemetry.TelemetryFeature; import java.util.Map; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/sub/TelemetrySubscriptionUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/TelemetrySubscriptionUpdate.java similarity index 96% rename from application/src/main/java/org/thingsboard/server/service/telemetry/sub/TelemetrySubscriptionUpdate.java rename to application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/TelemetrySubscriptionUpdate.java index cbbce43405..cddc6bbc13 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/sub/TelemetrySubscriptionUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/TelemetrySubscriptionUpdate.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry.sub; +package org.thingsboard.server.service.ws.telemetry.sub; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.service.subscription.SubscriptionErrorCode; import java.util.ArrayList; import java.util.Collections; diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java index ed8d8632da..96bde3f36f 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java @@ -53,6 +53,7 @@ public abstract class AbstractControllerTest extends AbstractNotifyEntityTest { protected int wsPort; private TbTestWebSocketClient wsClient; // lazy + private TbTestWebSocketClient anotherWsClient; // lazy public TbTestWebSocketClient getWsClient() { if (wsClient == null) { @@ -69,6 +70,21 @@ public abstract class AbstractControllerTest extends AbstractNotifyEntityTest { return wsClient; } + public TbTestWebSocketClient getAnotherWsClient() { + if (anotherWsClient == null) { + synchronized (this) { + try { + if (anotherWsClient == null) { + anotherWsClient = buildAndConnectWebSocketClient(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + return anotherWsClient; + } + @Before public void beforeWsTest() throws Exception { // placeholder @@ -79,9 +95,12 @@ public abstract class AbstractControllerTest extends AbstractNotifyEntityTest { if (wsClient != null) { wsClient.close(); } + if (anotherWsClient != null) { + anotherWsClient.close(); + } } - private TbTestWebSocketClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException { + protected TbTestWebSocketClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException { TbTestWebSocketClient wsClient = new TbTestWebSocketClient(new URI(WS_URL + wsPort + "/api/ws/plugins/telemetry?token=" + token)); assertThat(wsClient.connectBlocking(TIMEOUT, TimeUnit.SECONDS)).isTrue(); return wsClient; diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java index f7838d9689..ba229b68a5 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java @@ -44,9 +44,9 @@ import org.thingsboard.server.common.data.query.NumericFilterPredicate; import org.thingsboard.server.common.data.query.TsValue; import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUpdate; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUpdate; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate; import java.util.Arrays; import java.util.Collections; diff --git a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java index be7973d517..ac170a170d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java @@ -24,14 +24,14 @@ import org.thingsboard.server.common.data.query.EntityDataPageLink; import org.thingsboard.server.common.data.query.EntityDataQuery; import org.thingsboard.server.common.data.query.EntityFilter; import org.thingsboard.server.common.data.query.EntityKey; -import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUpdate; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityHistoryCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.TimeSeriesCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.TelemetryPluginCmdsWrapper; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUpdate; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityHistoryCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.LatestValueCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.TimeSeriesCmd; import java.net.URI; import java.nio.channels.NotYetConnectedException; diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWebSocketClient.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWebSocketClient.java new file mode 100644 index 0000000000..cba34a3f28 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWebSocketClient.java @@ -0,0 +1,75 @@ +package org.thingsboard.server.service.notification; + +import lombok.Getter; +import org.apache.commons.lang3.RandomUtils; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.IdBased; +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.controller.TbTestWebSocketClient; +import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd; +import org.thingsboard.server.service.ws.notification.cmd.NotificationCmdsWrapper; +import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; +import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate; +import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +public class NotificationsWebSocketClient extends TbTestWebSocketClient { + + private final Map currentNotifications = new LinkedHashMap<>(); + @Getter + private int totalUnreadCount; + @Getter + private UnreadNotificationsUpdate lastUpdate; + + public NotificationsWebSocketClient(String wsUrl, String token) throws URISyntaxException { + super(new URI(wsUrl + "/api/ws/plugins/notifications?token=" + token)); + } + + public void subscribeForUnreadNotifications(int limit) { + NotificationCmdsWrapper cmdsWrapper = new NotificationCmdsWrapper(); + cmdsWrapper.setUnreadSubCmd(new NotificationsSubCmd(newCmdId(), limit)); + sendCmd(cmdsWrapper); + } + + public void markNotificationAsRead(UUID notificationId) { + NotificationCmdsWrapper cmdsWrapper = new NotificationCmdsWrapper(); + cmdsWrapper.setMarkAsReadCmd(new MarkNotificationAsReadCmd(newCmdId(), notificationId)); + sendCmd(cmdsWrapper); + } + + + private void handleUpdate(UnreadNotificationsUpdate update) { + totalUnreadCount = update.getTotalUnreadCount(); + if (update.getNotifications() != null) { + currentNotifications.clear(); + currentNotifications.putAll(update.getNotifications().stream().collect(Collectors.toMap(IdBased::getUuidId, n -> n))); + } else if (update.getUpdate() != null) { + Notification notification = update.getUpdate(); + currentNotifications.put(notification.getUuidId(), notification); + } + } + + + public void sendCmd(NotificationCmdsWrapper cmdsWrapper) { + send(JacksonUtil.toString(cmdsWrapper)); + } + + @Override + public void onMessage(String s) { + UnreadNotificationsUpdate update = JacksonUtil.fromString(s, UnreadNotificationsUpdate.class); + lastUpdate = update; + handleUpdate(update); + super.onMessage(s); + } + + private static int newCmdId() { + return RandomUtils.nextInt(1, 1000); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java new file mode 100644 index 0000000000..12b2eb189f --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java @@ -0,0 +1,147 @@ +package org.thingsboard.server.service.notification; + +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.notification.targets.SingleUserNotificationTargetConfig; +import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.controller.TbTestWebSocketClient; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate; + +import java.net.URISyntaxException; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; + +@DaoSqlTest +public class NotificationsWsApiTest extends AbstractControllerTest { + + @Before + public void beforeEach() throws Exception { + loginTenantAdmin(); + } + + @Test + public void testSubscribingToUnreadNotifications_multipleSessions() throws Exception { + NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId); + String notificationText1 = "Notification 1"; + submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText1); + String notificationText2 = "Notification 2"; + submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText2); + + getWsClient().subscribeForUnreadNotifications(10); + getAnotherWsClient().subscribeForUnreadNotifications(10); + getWsClient().waitForReply(); + getAnotherWsClient().waitForReply(); + + checkFullNotificationsUpdate(getWsClient().getLastUpdate(), notificationText1, notificationText2); + checkFullNotificationsUpdate(getAnotherWsClient().getLastUpdate(), notificationText1, notificationText2); + } + + @Test + public void testReceivingNotificationUpdates_multipleSessions() { + getWsClient().subscribeForUnreadNotifications(10); + getAnotherWsClient().subscribeForUnreadNotifications(10); + getWsClient().waitForReply(); + getAnotherWsClient().waitForReply(); + UnreadNotificationsUpdate notificationsUpdate = getWsClient().getLastUpdate(); + assertThat(notificationsUpdate.getTotalUnreadCount()).isZero(); + + getWsClient().registerWaitForUpdate(); + getAnotherWsClient().registerWaitForUpdate(); + NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId); + String notificationText = "Notification 1"; + submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText); + getWsClient().waitForUpdate(); + getAnotherWsClient().waitForUpdate(); + + checkPartialNotificationsUpdate(getWsClient().getLastUpdate(), notificationText, 1); + checkPartialNotificationsUpdate(getAnotherWsClient().getLastUpdate(), notificationText, 1); + } + + @Test + public void testMarkingAsRead_multipleSessions() { + getWsClient().subscribeForUnreadNotifications(10); + getAnotherWsClient().subscribeForUnreadNotifications(10); + getWsClient().waitForReply(); + getAnotherWsClient().waitForReply(); + + NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId); + getWsClient().registerWaitForUpdate(); + String notificationText1 = "Notification 1"; + submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText1); + getWsClient().waitForUpdate(); + Notification notification1 = getWsClient().getLastUpdate().getUpdate(); + + getWsClient().registerWaitForUpdate(); + String notificationText2 = "Notification 2"; + submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText2); + getWsClient().waitForUpdate(); + assertThat(getWsClient().getLastUpdate().getTotalUnreadCount()).isEqualTo(2); + + getWsClient().registerWaitForUpdate(); + getAnotherWsClient().registerWaitForUpdate(); + getWsClient().markNotificationAsRead(notification1.getUuidId()); + getWsClient().waitForUpdate(); + getAnotherWsClient().waitForUpdate(); + + checkFullNotificationsUpdate(getWsClient().getLastUpdate(), notificationText2); + checkFullNotificationsUpdate(getAnotherWsClient().getLastUpdate(), notificationText2); + } + + public void testReceivingUpdatesWhenSubscriptionAtAnotherInstance() {} + + + private void checkFullNotificationsUpdate(UnreadNotificationsUpdate notificationsUpdate, String... expectedNotifications) { + assertThat(notificationsUpdate.getNotifications()).extracting(Notification::getText).containsOnly(expectedNotifications); + assertThat(notificationsUpdate.getTotalUnreadCount()).isEqualTo(expectedNotifications.length); + } + + private void checkPartialNotificationsUpdate(UnreadNotificationsUpdate notificationsUpdate, String expectedNotification, int expectedUnreadCount) { + assertThat(notificationsUpdate.getUpdate()).extracting(Notification::getText).isEqualTo(expectedNotification); + assertThat(notificationsUpdate.getTotalUnreadCount()).isEqualTo(expectedUnreadCount); + } + + private NotificationTarget createNotificationTarget(UserId userId) { + NotificationTarget notificationTarget = new NotificationTarget(); + notificationTarget.setTenantId(tenantId); + notificationTarget.setName("User " + userId); + SingleUserNotificationTargetConfig config = new SingleUserNotificationTargetConfig(); + config.setUserId(userId); + notificationTarget.setConfiguration(config); + return doPost("/api/notification/target", notificationTarget, NotificationTarget.class); + } + + private NotificationRequest submitNotificationRequest(NotificationTargetId targetId, String notificationReason, String text) { + NotificationRequest notificationRequest = NotificationRequest.builder() + .tenantId(tenantId) + .targetId(targetId) + .notificationReason(notificationReason) + .textTemplate(text) + .build(); + return doPost("/api/notification/request", notificationRequest, NotificationRequest.class); + } + + @Override + protected TbTestWebSocketClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException { + NotificationsWebSocketClient wsClient = new NotificationsWebSocketClient(WS_URL + wsPort, token); + assertThat(wsClient.connectBlocking(TIMEOUT, TimeUnit.SECONDS)).isTrue(); + return wsClient; + } + + @Override + public NotificationsWebSocketClient getWsClient() { + return (NotificationsWebSocketClient) super.getWsClient(); + } + + @Override + public NotificationsWebSocketClient getAnotherWsClient() { + return (NotificationsWebSocketClient) super.getAnotherWsClient(); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index ac272d0bb4..6ef55e3a20 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -64,10 +64,10 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; -import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.TelemetryPluginCmdsWrapper; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.LatestValueCmd; import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2mUplinkMsgHandler; diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/AbstractMqttAttributesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/AbstractMqttAttributesIntegrationTest.java index 6d030aca9f..6e03a15797 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/AbstractMqttAttributesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/AbstractMqttAttributesIntegrationTest.java @@ -22,7 +22,6 @@ import com.google.protobuf.InvalidProtocolBufferException; import com.squareup.wire.schema.internal.parser.ProtoFileElement; import io.netty.handler.codec.mqtt.MqttQoS; import lombok.extern.slf4j.Slf4j; -import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.TransportPayloadType; @@ -38,7 +37,7 @@ import org.thingsboard.server.common.data.query.EntityKeyType; import org.thingsboard.server.common.data.query.SingleEntityFilter; import org.thingsboard.server.gen.transport.TransportApiProtos; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate; import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest; import org.thingsboard.server.transport.mqtt.MqttTestCallback; import org.thingsboard.server.transport.mqtt.MqttTestClient; diff --git a/common/cluster-api/src/main/proto/queue.proto b/common/cluster-api/src/main/proto/queue.proto index cb3443a71f..7fb764579d 100644 --- a/common/cluster-api/src/main/proto/queue.proto +++ b/common/cluster-api/src/main/proto/queue.proto @@ -547,6 +547,11 @@ message TbAlarmSubscriptionProto { int64 ts = 2; } +message NotificationsSubscriptionProto { + TbSubscriptionProto sub = 1; + int32 limit = 2; +} + message TbSubscriptionUpdateProto { string sessionId = 1; int32 subscriptionId = 2; @@ -564,6 +569,27 @@ message TbAlarmSubscriptionUpdateProto { bool deleted = 6; } +message NotificationsSubscriptionUpdateProto { + string sessionId = 1; + int32 subscriptionId = 2; + string notification = 3; +} + +message NotificationUpdateProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 recipientIdMSB = 3; + int64 recipientIdLSB = 4; + string notification = 5; +} + +message NotificationRequestDeleteProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 notificationRequestIdMSB = 3; + int64 notificationRequestIdLSB = 4; +} + message TbAttributeUpdateProto { string entityType = 1; int64 entityIdMSB = 2; @@ -665,11 +691,15 @@ message SubscriptionMgrMsgProto { TbAlarmUpdateProto alarmUpdate = 8; TbAlarmDeleteProto alarmDelete = 9; TbTimeSeriesDeleteProto tsDelete = 10; + NotificationsSubscriptionProto notificationsSub = 11; + NotificationUpdateProto notificationUpdate = 12; + NotificationRequestDeleteProto notificationRequestDelete = 13; } message LocalSubscriptionServiceMsgProto { TbSubscriptionUpdateProto subUpdate = 1; TbAlarmSubscriptionUpdateProto alarmSubUpdate = 2; + NotificationsSubscriptionUpdateProto notificationsSubUpdate = 3; } message FromDeviceRPCResponseProto { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java index 9acbb15646..cb97f56bcf 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java @@ -142,6 +142,15 @@ public class StringUtils { return org.apache.commons.lang3.StringUtils.equals(str1, str2); } + public static boolean equalsAny(String string, String... otherStrings) { + for (String otherString : otherStrings) { + if (equals(string, otherString)) { + return true; + } + } + return false; + } + public static String substringAfterLast(String str, String sep) { return org.apache.commons.lang3.StringUtils.substringAfterLast(str, sep); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java index 8b1b994b17..054222a454 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java @@ -19,7 +19,9 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; @@ -55,11 +57,22 @@ public class DefaultNotificationService implements NotificationService { return notificationRequestDao.save(tenantId, notificationRequest); } + @Override + public NotificationRequest findNotificationRequestById(TenantId tenantId, NotificationRequestId id) { + return notificationRequestDao.findById(tenantId, id.getId()); + } + @Override public PageData findNotificationRequestsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { return notificationRequestDao.findByTenantIdAndPageLink(tenantId, pageLink); } + // ON DELETE CASCADE is used: notifications for request are deleted as well + @Override + public void deleteNotificationRequest(TenantId tenantId, NotificationRequestId id) { + notificationRequestDao.removeById(tenantId, id.getId()); + } + @Override public Notification createNotification(TenantId tenantId, Notification notification) { if (notification.getId() != null) { @@ -68,9 +81,11 @@ public class DefaultNotificationService implements NotificationService { return notificationDao.save(tenantId, notification); } + @Transactional @Override - public void updateNotificationStatus(TenantId tenantId, NotificationId notificationId, NotificationStatus status) { + public Notification updateNotificationStatus(TenantId tenantId, NotificationId notificationId, NotificationStatus status) { notificationDao.updateStatus(tenantId, notificationId, status); + return notificationDao.findById(tenantId, notificationId.getId()); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java index a7d5393d43..52f9f8b61b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java @@ -42,6 +42,7 @@ public class DefaultNotificationTargetService implements NotificationTargetServi @Override public NotificationTarget saveNotificationTarget(TenantId tenantId, NotificationTarget notificationTarget) { + notificationTarget.setTenantId(tenantId); validator.validate(notificationTarget, NotificationTarget::getTenantId); return notificationTargetDao.save(tenantId, notificationTarget); } @@ -81,6 +82,11 @@ public class DefaultNotificationTargetService implements NotificationTargetServi } private static class NotificationTargetValidator extends DataValidator { + + @Override + protected void validateDataImpl(TenantId tenantId, NotificationTarget notificationTarget) { + super.validateDataImpl(tenantId, notificationTarget); + } } } From 4d34c328d605237d8fde6bba437b6a54abee0069 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 31 Oct 2022 17:44:21 +0200 Subject: [PATCH 015/488] New TbNotificationNode; refactoring --- .../main/data/upgrade/3.4.2/schema_update.sql | 3 +- .../server/actors/ActorSystemContext.java | 5 ++ .../actors/ruleChain/DefaultTbContext.java | 6 ++ .../controller/NotificationController.java | 11 +-- .../NotificationTargetController.java | 1 + .../DefaultNotificationProcessingService.java | 89 +++++++++++++------ .../NotificationsSubscription.java | 5 +- .../subscription/TbSubscriptionUtils.java | 1 - .../telemetry/DefaultWebSocketService.java | 11 +-- ...> DefaultNotificationCommandsHandler.java} | 82 +++-------------- ....java => NotificationCommandsHandler.java} | 15 +--- .../NotificationsWebSocketClient.java | 16 +++- .../notification/NotificationsWsApiTest.java | 15 ++++ .../NotificationProcessingService.java | 12 +-- .../data/notification/Notification.java | 8 +- .../notification/NotificationRequest.java | 1 - .../server/dao/model/ModelConstants.java | 1 - .../dao/model/sql/NotificationEntity.java | 1 + .../model/sql/NotificationRequestEntity.java | 5 -- .../DefaultNotificationService.java | 5 -- .../main/resources/sql/schema-entities.sql | 3 +- .../rule/engine/api/TbContext.java | 3 + .../notification/TbNotificationNode.java | 83 +++++++++++++++++ .../TbNotificationNodeConfiguration.java | 40 +++++++++ 24 files changed, 269 insertions(+), 153 deletions(-) rename application/src/main/java/org/thingsboard/server/service/{ws/notification/sub => subscription}/NotificationsSubscription.java (92%) rename application/src/main/java/org/thingsboard/server/service/ws/notification/{sub/DefaultNotificationsSubscriptionService.java => DefaultNotificationCommandsHandler.java} (60%) rename application/src/main/java/org/thingsboard/server/service/ws/notification/{sub/NotificationsSubscriptionService.java => NotificationCommandsHandler.java} (64%) rename {application/src/main/java/org/thingsboard/server/service => common/dao-api/src/main/java/org/thingsboard/server/dao}/notification/NotificationProcessingService.java (65%) create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNodeConfiguration.java diff --git a/application/src/main/data/upgrade/3.4.2/schema_update.sql b/application/src/main/data/upgrade/3.4.2/schema_update.sql index 9b259cca3e..17c75773ad 100644 --- a/application/src/main/data/upgrade/3.4.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.4.2/schema_update.sql @@ -33,8 +33,7 @@ CREATE TABLE IF NOT EXISTS notification_request ( text_template VARCHAR NOT NULL, notification_info VARCHAR(1000), notification_severity VARCHAR(32), - additional_config VARCHAR(1000), - sender_id UUID + additional_config VARCHAR(1000) ); CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id_and_created_time ON notification_request(tenant_id, created_time DESC); diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index f4e7eb2463..2a574f5788 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -64,6 +64,7 @@ import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.nosql.CassandraBufferedRateReadExecutor; import org.thingsboard.server.dao.nosql.CassandraBufferedRateWriteExecutor; +import org.thingsboard.server.dao.notification.NotificationProcessingService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; @@ -304,6 +305,10 @@ public class ActorSystemContext { @Getter private SmsSenderFactory smsSenderFactory; + @Autowired + @Getter + private NotificationProcessingService notificationProcessingService; + @Lazy @Autowired(required = false) @Getter diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 62d4829085..c0197a0f89 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -79,6 +79,7 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.nosql.CassandraStatementTask; import org.thingsboard.server.dao.nosql.TbResultSetFuture; +import org.thingsboard.server.dao.notification.NotificationProcessingService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; @@ -630,6 +631,11 @@ class DefaultTbContext implements TbContext { return mainCtx.getSmsSenderFactory(); } + @Override + public NotificationProcessingService getNotificationProcessingService() { + return mainCtx.getNotificationProcessingService(); + } + @Override public RuleEngineRpcService getRpcService() { return mainCtx.getTbRuleEngineDeviceRpcService(); diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index f6f48aeb51..c3cfdc24e8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -33,12 +33,11 @@ import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationRequest; -import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.notification.NotificationProcessingService; +import org.thingsboard.server.dao.notification.NotificationProcessingService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; @@ -73,16 +72,18 @@ public class NotificationController extends BaseController { public void markNotificationAsRead(@PathVariable UUID id, @AuthenticationPrincipal SecurityUser user) { NotificationId notificationId = new NotificationId(id); - notificationProcessingService.markNotificationAsRead(user, notificationId); + notificationProcessingService.markNotificationAsRead(user.getTenantId(), user.getId(), notificationId); } + // delete notification? @PostMapping("/notification/request") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public NotificationRequest createNotificationRequest(@RequestBody NotificationRequest notificationRequest, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { accessControlService.checkPermission(user, Resource.NOTIFICATION, Operation.CREATE); - return notificationProcessingService.processNotificationRequest(user, notificationRequest); + // read permission for target's users + return notificationProcessingService.processNotificationRequest(user.getTenantId(), notificationRequest); } @GetMapping("/notification/request/{id}") @@ -110,7 +111,7 @@ public class NotificationController extends BaseController { public void deleteNotificationRequest(@PathVariable UUID id, @AuthenticationPrincipal SecurityUser user) { NotificationRequestId notificationRequestId = new NotificationRequestId(id); - notificationProcessingService.deleteNotificationRequest(user, notificationRequestId); + notificationProcessingService.deleteNotificationRequest(user.getTenantId(), notificationRequestId); } } 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 471b72e015..3eec3b6ffc 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java @@ -61,6 +61,7 @@ public class NotificationTargetController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public NotificationTarget saveNotificationTarget(@RequestBody NotificationTarget notificationTarget, @AuthenticationPrincipal SecurityUser user) { + // fixme: read permission check for users in target return notificationTargetService.saveNotificationTarget(user.getTenantId(), notificationTarget); } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java index 862ce7389a..57b295bddd 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java @@ -16,66 +16,84 @@ package org.thingsboard.server.service.notification; import com.google.common.base.Strings; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationRequestConfig; import org.thingsboard.server.common.data.notification.NotificationStatus; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.dao.notification.NotificationProcessingService; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.user.UserService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; -import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.security.permission.AccessControlService; -import org.thingsboard.server.service.security.permission.Operation; -import org.thingsboard.server.service.security.permission.Resource; -import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionService; +import org.thingsboard.server.service.subscription.TbSubscriptionUtils; +import org.thingsboard.server.service.telemetry.AbstractSubscriptionService; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.UUID; @Service @Slf4j -@RequiredArgsConstructor -public class DefaultNotificationProcessingService implements NotificationProcessingService { +public class DefaultNotificationProcessingService extends AbstractSubscriptionService implements NotificationProcessingService { private final NotificationTargetService notificationTargetService; private final NotificationService notificationService; private final UserService userService; - private final AccessControlService accessControlService; private final DbCallbackExecutorService dbCallbackExecutorService; - private final NotificationsSubscriptionService notificationsSubscriptionService; + private final NotificationsTopicService notificationsTopicService; + + public DefaultNotificationProcessingService(TbClusterService clusterService, PartitionService partitionService, + NotificationTargetService notificationTargetService, + NotificationService notificationService, UserService userService, + DbCallbackExecutorService dbCallbackExecutorService, + NotificationsTopicService notificationsTopicService) { + super(clusterService, partitionService); + this.notificationTargetService = notificationTargetService; + this.notificationService = notificationService; + this.userService = userService; + this.dbCallbackExecutorService = dbCallbackExecutorService; + this.notificationsTopicService = notificationsTopicService; + } @Override - public NotificationRequest processNotificationRequest(SecurityUser user, NotificationRequest notificationRequest) throws ThingsboardException { - TenantId tenantId = user.getTenantId(); + public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { List recipientsIds = notificationTargetService.findRecipientsForNotificationTarget(tenantId, notificationRequest.getTargetId()); List recipients = new ArrayList<>(); for (UserId recipientId : recipientsIds) { User recipient = userService.findUserById(tenantId, recipientId); // todo: add caching - accessControlService.checkPermission(user, Resource.USER, Operation.READ, recipientId, recipient); recipients.add(recipient); } notificationRequest.setTenantId(tenantId); - notificationRequest.setSenderId(user.getId()); NotificationRequest savedNotificationRequest = notificationService.createNotificationRequest(tenantId, notificationRequest); - // todo: delayed sending; check all delayed notification requests on start up, schedule send + if (notificationRequest.getAdditionalConfig() != null) { + NotificationRequestConfig config = notificationRequest.getAdditionalConfig(); + // todo: delayed sending; check all delayed notification requests on start up, schedule send + } for (User recipient : recipients) { dbCallbackExecutorService.submit(() -> { Notification notification = createNotification(recipient, savedNotificationRequest); - notificationsSubscriptionService.onNewNotification(recipient.getTenantId(), recipient.getId(), notification); + onNotificationUpdate(recipient.getTenantId(), recipient.getId(), notification); }); } @@ -83,15 +101,15 @@ public class DefaultNotificationProcessingService implements NotificationProcess } @Override - public void markNotificationAsRead(SecurityUser user, NotificationId notificationId) { - Notification notification = notificationService.updateNotificationStatus(user.getTenantId(), notificationId, NotificationStatus.READ); - notificationsSubscriptionService.onNotificationUpdated(user.getTenantId(), user.getId(), notification); + public void markNotificationAsRead(TenantId tenantId, UserId recipientId, NotificationId notificationId) { + Notification notification = notificationService.updateNotificationStatus(tenantId, notificationId, NotificationStatus.READ); + onNotificationUpdate(tenantId, recipientId, notification); } @Override - public void deleteNotificationRequest(SecurityUser user, NotificationRequestId notificationRequestId) { - notificationService.deleteNotificationRequest(user.getTenantId(), notificationRequestId); - notificationsSubscriptionService.onNotificationRequestDeleted(user.getTenantId(), notificationRequestId); + public void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId) { + notificationService.deleteNotificationRequest(tenantId, notificationRequestId); + onNotificationRequestDeleted(tenantId, notificationRequestId); } private Notification createNotification(User recipient, NotificationRequest notificationRequest) { @@ -104,8 +122,7 @@ public class DefaultNotificationProcessingService implements NotificationProcess .severity(notificationRequest.getNotificationSeverity()) .status(NotificationStatus.SENT) .build(); - notification = notificationService.createNotification(recipient.getTenantId(), notification); - return notification; + return notificationService.createNotification(recipient.getTenantId(), notification); } private String formatNotificationText(String template, User recipient) { @@ -117,6 +134,26 @@ public class DefaultNotificationProcessingService implements NotificationProcess return TbNodeUtils.processTemplate(template, context); } - // handle markAsRead and deleteNotificationRequest - send UnreadNotificationsUpdate with updated list + private void onNotificationUpdate(TenantId tenantId, UserId recipientId, Notification notification) { + forwardToSubscriptionManagerServiceOrSendToCore(tenantId, recipientId, subscriptionManagerService -> { + subscriptionManagerService.onNotificationUpdate(tenantId, recipientId, notification, TbCallback.EMPTY); + }, () -> { + return TbSubscriptionUtils.notificationUpdateToProto(tenantId, recipientId, notification); + }); + } + + public void onNotificationRequestDeleted(TenantId tenantId, NotificationRequestId notificationRequestId) { + TransportProtos.ToCoreMsg notificationRequestDeletedProto = TbSubscriptionUtils.notificationRequestDeletedToProto(tenantId, notificationRequestId); + Set coreServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_CORE)); + for (String serviceId : coreServices) { + TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); + clusterService.pushMsgToCore(tpi, UUID.randomUUID(), notificationRequestDeletedProto, null); + } + } + + @Override + protected String getExecutorPrefix() { + return "notification"; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java b/application/src/main/java/org/thingsboard/server/service/subscription/NotificationsSubscription.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java rename to application/src/main/java/org/thingsboard/server/service/subscription/NotificationsSubscription.java index 872e4a9671..00d23770e1 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/NotificationsSubscription.java @@ -13,16 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.ws.notification.sub; +package org.thingsboard.server.service.subscription; import lombok.Builder; import lombok.Getter; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.Notification; -import org.thingsboard.server.service.subscription.TbSubscription; -import org.thingsboard.server.service.subscription.TbSubscriptionType; import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate; +import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; import java.util.LinkedHashMap; import java.util.Map; diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java index 603b1903f0..5fd0aafae4 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java @@ -54,7 +54,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdatePr import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; -import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscription; import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java index 86d41456da..f6df94b46f 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java @@ -66,11 +66,12 @@ import org.thingsboard.server.service.ws.SessionEvent; import org.thingsboard.server.service.ws.WebSocketMsgEndpoint; import org.thingsboard.server.service.ws.WebSocketSessionRef; import org.thingsboard.server.service.ws.WsSessionMetaData; +import org.thingsboard.server.service.ws.notification.DefaultNotificationCommandsHandler; +import org.thingsboard.server.service.ws.notification.NotificationCommandsHandler; import org.thingsboard.server.service.ws.notification.cmd.NotificationCmdsWrapper; import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd; import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; import org.thingsboard.server.service.ws.notification.cmd.NotificationsUnsubCmd; -import org.thingsboard.server.service.ws.notification.sub.DefaultNotificationsSubscriptionService; import org.thingsboard.server.service.ws.telemetry.WebSocketService; import org.thingsboard.server.service.ws.telemetry.cmd.TelemetryPluginCmdsWrapper; import org.thingsboard.server.service.ws.telemetry.cmd.v1.AttributesSubscriptionCmd; @@ -136,7 +137,7 @@ public class DefaultWebSocketService implements WebSocketService { private TbEntityDataSubscriptionService entityDataSubService; @Autowired - private DefaultNotificationsSubscriptionService notificationsSubService; + private NotificationCommandsHandler notificationCmdsHandler; @Autowired private WebSocketMsgEndpoint msgEndpoint; @@ -286,21 +287,21 @@ public class DefaultWebSocketService implements WebSocketService { private void handleUnreadNotificationsSubCmd(WebSocketSessionRef sessionRef, NotificationsSubCmd cmd) { String sessionId = sessionRef.getSessionId(); if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) { - notificationsSubService.handleUnreadNotificationsSubCmd(sessionRef, cmd); + notificationCmdsHandler.handleUnreadNotificationsSubCmd(sessionRef, cmd); } } private void handleUnreadNotificationsUnsubCmd(WebSocketSessionRef sessionRef, NotificationsUnsubCmd cmd) { String sessionId = sessionRef.getSessionId(); if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) { - notificationsSubService.handleUnsubCmd(sessionRef, cmd); + notificationCmdsHandler.handleUnsubCmd(sessionRef, cmd); } } private void handleMarkNotificationAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationAsReadCmd cmd) { String sessionId = sessionRef.getSessionId(); if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) { - notificationsSubService.handleMarkAsReadCmd(sessionRef, cmd); + notificationCmdsHandler.handleMarkAsReadCmd(sessionRef, cmd); } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/DefaultNotificationsSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java similarity index 60% rename from application/src/main/java/org/thingsboard/server/service/ws/notification/sub/DefaultNotificationsSubscriptionService.java rename to application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java index 119c14dc23..ad3f9ca4b0 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/DefaultNotificationsSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java @@ -13,71 +13,45 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.ws.notification.sub; +package org.thingsboard.server.service.ws.notification; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.annotation.Lazy; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.id.IdBased; import org.thingsboard.server.common.data.id.NotificationId; -import org.thingsboard.server.common.data.id.NotificationRequestId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.common.msg.queue.TbCallback; -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.notification.NotificationService; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; -import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.notification.NotificationProcessingService; +import org.thingsboard.server.dao.notification.NotificationProcessingService; import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.subscription.NotificationsSubscription; import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; -import org.thingsboard.server.service.subscription.TbSubscriptionUtils; -import org.thingsboard.server.service.telemetry.AbstractSubscriptionService; import org.thingsboard.server.service.ws.WebSocketSessionRef; import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd; import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate; +import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.WebSocketService; import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; -import java.util.HashSet; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @Service @TbCoreComponent -@Slf4j -public class DefaultNotificationsSubscriptionService extends AbstractSubscriptionService implements NotificationsSubscriptionService { +@RequiredArgsConstructor +public class DefaultNotificationCommandsHandler implements NotificationCommandsHandler { + private final NotificationService notificationService; private final WebSocketService wsService; private final TbLocalSubscriptionService localSubscriptionService; - private final NotificationService notificationService; - private final TbServiceInfoProvider serviceInfoProvider; - private final NotificationsTopicService notificationsTopicService; private final NotificationProcessingService notificationProcessingService; - - public DefaultNotificationsSubscriptionService(TbClusterService clusterService, PartitionService partitionService, - @Lazy WebSocketService wsService, TbLocalSubscriptionService localSubscriptionService, - NotificationService notificationService, TbServiceInfoProvider serviceInfoProvider, - NotificationsTopicService notificationsTopicService, - @Lazy NotificationProcessingService notificationProcessingService) { - super(clusterService, partitionService); - this.wsService = wsService; - this.localSubscriptionService = localSubscriptionService; - this.notificationService = notificationService; - this.serviceInfoProvider = serviceInfoProvider; - this.notificationsTopicService = notificationsTopicService; - this.notificationProcessingService = notificationProcessingService; - } + private final TbServiceInfoProvider serviceInfoProvider; @Override public void handleUnreadNotificationsSubCmd(WebSocketSessionRef sessionRef, NotificationsSubCmd cmd) { @@ -100,7 +74,7 @@ public class DefaultNotificationsSubscriptionService extends AbstractSubscriptio @Override public void handleMarkAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationAsReadCmd cmd) { NotificationId notificationId = new NotificationId(cmd.getNotificationId()); - notificationProcessingService.markNotificationAsRead(sessionRef.getSecurityCtx(), notificationId); + notificationProcessingService.markNotificationAsRead(sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), notificationId); } @Override @@ -116,35 +90,6 @@ public class DefaultNotificationsSubscriptionService extends AbstractSubscriptio subscription.getTotalUnreadCount().set((int) notifications.getTotalElements()); } - @Override - public void onNewNotification(TenantId tenantId, UserId recipientId, Notification notification) { - onNotificationUpdate(tenantId, recipientId, notification); - } - - @Override - public void onNotificationUpdated(TenantId tenantId, UserId recipientId, Notification notification) { - onNotificationUpdate(tenantId, recipientId, notification); - } - - private void onNotificationUpdate(TenantId tenantId, UserId recipientId, Notification notification) { - forwardToSubscriptionManagerServiceOrSendToCore(tenantId, recipientId, subscriptionManagerService -> { - subscriptionManagerService.onNotificationUpdate(tenantId, recipientId, notification, TbCallback.EMPTY); - }, () -> { - return TbSubscriptionUtils.notificationUpdateToProto(tenantId, recipientId, notification); - }); - } - - @Override - public void onNotificationRequestDeleted(TenantId tenantId, NotificationRequestId notificationRequestId) { - TransportProtos.ToCoreMsg notificationRequestDeletedProto = TbSubscriptionUtils.notificationRequestDeletedToProto(tenantId, notificationRequestId); - Set coreServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_CORE)); - for (String serviceId : coreServices) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); - clusterService.pushMsgToCore(tpi, UUID.randomUUID(), notificationRequestDeletedProto, null); - } - } - - private void handleSubscriptionUpdate(NotificationsSubscription subscription, NotificationsSubscriptionUpdate subscriptionUpdate) { if (subscriptionUpdate.getNotification() != null) { Notification notification = subscriptionUpdate.getNotification(); @@ -172,12 +117,7 @@ public class DefaultNotificationsSubscriptionService extends AbstractSubscriptio } private void sendUpdate(String sessionId, UnreadNotificationsUpdate update) { - wsService.sendWsMsg(sessionId, update);; - } - - @Override - protected String getExecutorPrefix() { - return "notification"; + wsService.sendWsMsg(sessionId, update); } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/NotificationCommandsHandler.java similarity index 64% rename from application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionService.java rename to application/src/main/java/org/thingsboard/server/service/ws/notification/NotificationCommandsHandler.java index 0afd8dcd39..b45e512045 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/NotificationCommandsHandler.java @@ -13,18 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.ws.notification.sub; +package org.thingsboard.server.service.ws.notification; -import org.thingsboard.server.common.data.id.NotificationRequestId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.id.UserId; -import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.service.ws.WebSocketSessionRef; import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd; import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; -public interface NotificationsSubscriptionService { +public interface NotificationCommandsHandler { void handleUnreadNotificationsSubCmd(WebSocketSessionRef sessionRef, NotificationsSubCmd cmd); @@ -32,11 +28,4 @@ public interface NotificationsSubscriptionService { void handleUnsubCmd(WebSocketSessionRef sessionRef, UnsubscribeCmd cmd); - - void onNewNotification(TenantId tenantId, UserId recipientId, Notification notification); - - void onNotificationUpdated(TenantId tenantId, UserId recipientId, Notification notification); - - void onNotificationRequestDeleted(TenantId tenantId, NotificationRequestId notificationRequestId); - } diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWebSocketClient.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWebSocketClient.java index cba34a3f28..70fd9ad51d 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWebSocketClient.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWebSocketClient.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.thingsboard.server.service.notification; import lombok.Getter; @@ -10,7 +25,6 @@ import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsRead import org.thingsboard.server.service.ws.notification.cmd.NotificationCmdsWrapper; import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate; -import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; import java.net.URI; import java.net.URISyntaxException; diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java index 12b2eb189f..8ff88b7e3c 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.thingsboard.server.service.notification; import org.junit.Before; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationProcessingService.java similarity index 65% rename from application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java rename to common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationProcessingService.java index 57aba583ed..b25d4943b7 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationProcessingService.java @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.notification; +package org.thingsboard.server.dao.notification; -import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.NotificationRequest; -import org.thingsboard.server.service.security.model.SecurityUser; public interface NotificationProcessingService { - NotificationRequest processNotificationRequest(SecurityUser user, NotificationRequest notificationRequest) throws ThingsboardException; + NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest); - void markNotificationAsRead(SecurityUser user, NotificationId notificationId); + void markNotificationAsRead(TenantId tenantId, UserId recipientId, NotificationId notificationId); - void deleteNotificationRequest(SecurityUser user, NotificationRequestId notificationRequestId); + void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java index a633ad6433..827bddc980 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java @@ -20,7 +20,7 @@ import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.SearchTextBased; +import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.UserId; @@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.id.UserId; @NoArgsConstructor @Builder @EqualsAndHashCode(callSuper = true) -public class Notification extends SearchTextBased { +public class Notification extends BaseData { private NotificationRequestId requestId; private UserId recipientId; @@ -41,8 +41,4 @@ public class Notification extends SearchTextBased { private NotificationStatus status; // private UserId senderId; - @Override - public String getSearchText() { - return text; - } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java index 392063ad93..42cff008d0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java @@ -51,7 +51,6 @@ public class NotificationRequest extends BaseData impleme private NotificationInfo notificationInfo; private NotificationSeverity notificationSeverity; private NotificationRequestConfig additionalConfig; - private UserId senderId; public static final String GENERAL_NOTIFICATION_REASON = "General"; public static final String ALARM_NOTIFICATION_REASON = "Alarm"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 13d149bcf5..2ae524f1e2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -668,7 +668,6 @@ public class ModelConstants { public static final String NOTIFICATION_REQUEST_NOTIFICATION_INFO_PROPERTY = "notification_info"; public static final String NOTIFICATION_REQUEST_NOTIFICATION_SEVERITY_PROPERTY = "notification_severity"; public static final String NOTIFICATION_REQUEST_ADDITIONAL_CONFIG_PROPERTY = "additional_config"; - public static final String NOTIFICATION_REQUEST_SENDER_ID_PROPERTY = "sender_id"; protected static final String[] NONE_AGGREGATION_COLUMNS = new String[]{LONG_VALUE_COLUMN, DOUBLE_VALUE_COLUMN, BOOLEAN_VALUE_COLUMN, STRING_VALUE_COLUMN, JSON_VALUE_COLUMN, KEY_COLUMN, TS_COLUMN}; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java index be3532e300..c79ba6f30b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java @@ -62,6 +62,7 @@ public class NotificationEntity extends BaseSqlEntity { @Column(name = ModelConstants.NOTIFICATION_INFO_PROPERTY) private JsonNode info; + @Enumerated(EnumType.STRING) @Column(name = ModelConstants.NOTIFICATION_SEVERITY_PROPERTY) private NotificationSeverity severity; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java index f469f4aed7..65e1fe8214 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java @@ -71,9 +71,6 @@ public class NotificationRequestEntity extends BaseSqlEntity { + return ctx.getNotificationProcessingService().processNotificationRequest(ctx.getTenantId(), notificationRequest); + }), + r -> { + TbMsgMetaData msgMetaData = msg.getMetaData().copy(); + msgMetaData.putValue("notificationRequestId", r.getUuidId().toString()); + msgMetaData.putValue("notificationTextTemplate", r.getTextTemplate()); + ctx.tellSuccess(TbMsg.transformMsg(msg, msgMetaData)); + }, + e -> ctx.tellFailure(msg, e)); + } + + private void validateConfig(TbNotificationNodeConfiguration config) throws TbNodeException { + if (config.getTargetId() == null) { + throw new TbNodeException("Notification target is not specified"); + } + if (StringUtils.isBlank(config.getNotificationTextTemplate())) { + throw new TbNodeException("Notification text template is missing"); + } + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNodeConfiguration.java new file mode 100644 index 0000000000..557c5e19b0 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNodeConfiguration.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.notification; + +import lombok.Data; +import org.thingsboard.rule.engine.api.NodeConfiguration; +import org.thingsboard.server.common.data.notification.NotificationSeverity; + +import java.util.UUID; + +@Data +public class TbNotificationNodeConfiguration implements NodeConfiguration { + + private UUID targetId; + private String notificationReason; + private String notificationTextTemplate; + private NotificationSeverity notificationSeverity; + + @Override + public TbNotificationNodeConfiguration defaultConfiguration() { + TbNotificationNodeConfiguration configuration = new TbNotificationNodeConfiguration(); + configuration.setNotificationReason("General"); + configuration.setNotificationSeverity(NotificationSeverity.NORMAL); + return configuration; + } + +} From 8d69a4983551593d433041baf8b0812b6650a452 Mon Sep 17 00:00:00 2001 From: AndriiD Date: Tue, 1 Nov 2022 20:05:51 +0200 Subject: [PATCH 016/488] removed redundant handleException usages --- .../server/controller/AdminController.java | 158 ++++------- .../server/controller/AlarmController.java | 46 +-- .../server/controller/AssetController.java | 229 ++++++--------- .../controller/AssetProfileController.java | 38 +-- .../server/controller/AuditLogController.java | 56 ++-- .../server/controller/AuthController.java | 234 +++++++--------- .../ComponentDescriptorController.java | 24 +- .../server/controller/CustomerController.java | 60 ++-- .../controller/DashboardController.java | 240 +++++++--------- .../server/controller/DeviceController.java | 225 ++++++--------- .../controller/DeviceProfileController.java | 50 +--- .../server/controller/EdgeController.java | 237 ++++++---------- .../controller/EdgeEventController.java | 14 +- .../controller/EntityQueryController.java | 30 +- .../controller/EntityRelationController.java | 83 ++---- .../controller/EntityViewController.java | 185 +++++------- .../server/controller/EventController.java | 10 +- .../server/controller/Lwm2mController.java | 6 +- .../OAuth2ConfigTemplateController.java | 26 +- .../server/controller/OAuth2Controller.java | 56 ++-- .../controller/OtaPackageController.java | 62 ++-- .../server/controller/RpcV2Controller.java | 100 +++---- .../controller/RuleChainController.java | 265 ++++++++---------- .../controller/TbResourceController.java | 66 ++--- .../controller/TelemetryController.java | 138 +++------ .../server/controller/TenantController.java | 38 +-- .../controller/TenantProfileController.java | 82 ++---- .../server/controller/UserController.java | 160 +++++------ .../controller/WidgetTypeController.java | 144 ++++------ .../controller/WidgetsBundleController.java | 38 +-- 30 files changed, 1162 insertions(+), 1938 deletions(-) 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 772c034f62..9f10024eaa 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -82,16 +82,12 @@ public class AdminController extends BaseController { public AdminSettings getAdminSettings( @ApiParam(value = "A string value of the key (e.g. 'general' or 'mail').") @PathVariable("key") String key) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); - AdminSettings adminSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key), "No Administration settings found for key: " + key); - if (adminSettings.getKey().equals("mail")) { - ((ObjectNode) adminSettings.getJsonValue()).remove("password"); - } - return adminSettings; - } catch (Exception e) { - throw handleException(e); + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); + AdminSettings adminSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key), "No Administration settings found for key: " + key); + if (adminSettings.getKey().equals("mail")) { + ((ObjectNode) adminSettings.getJsonValue()).remove("password"); } + return adminSettings; } @@ -105,20 +101,16 @@ public class AdminController extends BaseController { public AdminSettings saveAdminSettings( @ApiParam(value = "A JSON value representing the Administration Settings.") @RequestBody AdminSettings adminSettings) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE); - adminSettings.setTenantId(getTenantId()); - adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings)); - if (adminSettings.getKey().equals("mail")) { - mailService.updateMailConfiguration(); - ((ObjectNode) adminSettings.getJsonValue()).remove("password"); - } else if (adminSettings.getKey().equals("sms")) { - smsService.updateSmsConfiguration(); - } - return adminSettings; - } catch (Exception e) { - throw handleException(e); + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE); + adminSettings.setTenantId(getTenantId()); + adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings)); + if (adminSettings.getKey().equals("mail")) { + mailService.updateMailConfiguration(); + ((ObjectNode) adminSettings.getJsonValue()).remove("password"); + } else if (adminSettings.getKey().equals("sms")) { + smsService.updateSmsConfiguration(); } + return adminSettings; } @ApiOperation(value = "Get the Security Settings object", @@ -127,12 +119,8 @@ public class AdminController extends BaseController { @RequestMapping(value = "/securitySettings", method = RequestMethod.GET) @ResponseBody public SecuritySettings getSecuritySettings() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); - return checkNotNull(systemSecurityService.getSecuritySettings(TenantId.SYS_TENANT_ID)); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); + return checkNotNull(systemSecurityService.getSecuritySettings(TenantId.SYS_TENANT_ID)); } @ApiOperation(value = "Update Security Settings (saveSecuritySettings)", @@ -143,13 +131,9 @@ public class AdminController extends BaseController { public SecuritySettings saveSecuritySettings( @ApiParam(value = "A JSON value representing the Security Settings.") @RequestBody SecuritySettings securitySettings) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE); - securitySettings = checkNotNull(systemSecurityService.saveSecuritySettings(TenantId.SYS_TENANT_ID, securitySettings)); - return securitySettings; - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE); + securitySettings = checkNotNull(systemSecurityService.saveSecuritySettings(TenantId.SYS_TENANT_ID, securitySettings)); + return securitySettings; } @ApiOperation(value = "Send test email (sendTestMail)", @@ -160,19 +144,15 @@ public class AdminController extends BaseController { public void sendTestMail( @ApiParam(value = "A JSON value representing the Mail Settings.") @RequestBody AdminSettings adminSettings) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); - adminSettings = checkNotNull(adminSettings); - if (adminSettings.getKey().equals("mail")) { - if (!adminSettings.getJsonValue().has("password")) { - AdminSettings mailSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail")); - ((ObjectNode) adminSettings.getJsonValue()).put("password", mailSettings.getJsonValue().get("password").asText()); - } - String email = getCurrentUser().getEmail(); - mailService.sendTestMail(adminSettings.getJsonValue(), email); + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); + adminSettings = checkNotNull(adminSettings); + if (adminSettings.getKey().equals("mail")) { + if (!adminSettings.getJsonValue().has("password")) { + AdminSettings mailSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail")); + ((ObjectNode) adminSettings.getJsonValue()).put("password", mailSettings.getJsonValue().get("password").asText()); } - } catch (Exception e) { - throw handleException(e); + String email = getCurrentUser().getEmail(); + mailService.sendTestMail(adminSettings.getJsonValue(), email); } } @@ -184,12 +164,8 @@ public class AdminController extends BaseController { public void sendTestSms( @ApiParam(value = "A JSON value representing the Test SMS request.") @RequestBody TestSmsRequest testSmsRequest) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); - smsService.sendTestSms(testSmsRequest); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); + smsService.sendTestSms(testSmsRequest); } @ApiOperation(value = "Get repository settings (getRepositorySettings)", @@ -197,16 +173,12 @@ public class AdminController extends BaseController { @PreAuthorize("hasAuthority('TENANT_ADMIN')") @GetMapping("/repositorySettings") public RepositorySettings getRepositorySettings() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); - RepositorySettings versionControlSettings = checkNotNull(versionControlService.getVersionControlSettings(getTenantId())); - versionControlSettings.setPassword(null); - versionControlSettings.setPrivateKey(null); - versionControlSettings.setPrivateKeyPassword(null); - return versionControlSettings; - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); + RepositorySettings versionControlSettings = checkNotNull(versionControlService.getVersionControlSettings(getTenantId())); + versionControlSettings.setPassword(null); + versionControlSettings.setPrivateKey(null); + versionControlSettings.setPrivateKeyPassword(null); + return versionControlSettings; } @ApiOperation(value = "Check repository settings exists (repositorySettingsExists)", @@ -214,12 +186,8 @@ public class AdminController extends BaseController { @PreAuthorize("hasAuthority('TENANT_ADMIN')") @GetMapping("/repositorySettings/exists") public Boolean repositorySettingsExists() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); - return versionControlService.getVersionControlSettings(getTenantId()) != null; - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); + return versionControlService.getVersionControlSettings(getTenantId()) != null; } @PreAuthorize("hasAuthority('TENANT_ADMIN')") @@ -260,13 +228,9 @@ public class AdminController extends BaseController { @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/repositorySettings", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.OK) - public DeferredResult deleteRepositorySettings() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.DELETE); - return wrapFuture(versionControlService.deleteVersionControlSettings(getTenantId())); - } catch (Exception e) { - throw handleException(e); - } + public DeferredResult deleteRepositorySettings() throws Exception { + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.DELETE); + return wrapFuture(versionControlService.deleteVersionControlSettings(getTenantId())); } @@ -276,14 +240,10 @@ public class AdminController extends BaseController { @RequestMapping(value = "/repositorySettings/checkAccess", method = RequestMethod.POST) public DeferredResult checkRepositoryAccess( @ApiParam(value = "A JSON value representing the Repository Settings.") - @RequestBody RepositorySettings settings) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); - settings = checkNotNull(settings); - return wrapFuture(versionControlService.checkVersionControlAccess(getTenantId(), settings)); - } catch (Exception e) { - throw handleException(e); - } + @RequestBody RepositorySettings settings) throws Exception { + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); + settings = checkNotNull(settings); + return wrapFuture(versionControlService.checkVersionControlAccess(getTenantId(), settings)); } @ApiOperation(value = "Get auto commit settings (getAutoCommitSettings)", @@ -291,12 +251,8 @@ public class AdminController extends BaseController { @PreAuthorize("hasAuthority('TENANT_ADMIN')") @GetMapping("/autoCommitSettings") public AutoCommitSettings getAutoCommitSettings() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); - return checkNotNull(autoCommitSettingsService.get(getTenantId())); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); + return checkNotNull(autoCommitSettingsService.get(getTenantId())); } @ApiOperation(value = "Check auto commit settings exists (autoCommitSettingsExists)", @@ -304,12 +260,8 @@ public class AdminController extends BaseController { @PreAuthorize("hasAuthority('TENANT_ADMIN')") @GetMapping("/autoCommitSettings/exists") public Boolean autoCommitSettingsExists() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); - return autoCommitSettingsService.get(getTenantId()) != null; - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); + return autoCommitSettingsService.get(getTenantId()) != null; } @ApiOperation(value = "Creates or Updates the auto commit settings (saveAutoCommitSettings)", @@ -328,12 +280,8 @@ public class AdminController extends BaseController { @RequestMapping(value = "/autoCommitSettings", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.OK) public void deleteAutoCommitSettings() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.DELETE); - autoCommitSettingsService.delete(getTenantId()); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.DELETE); + autoCommitSettingsService.delete(getTenantId()); } @ApiOperation(value = "Check for new Platform Releases (checkUpdates)", @@ -343,11 +291,7 @@ public class AdminController extends BaseController { @RequestMapping(value = "/updates", method = RequestMethod.GET) @ResponseBody public UpdateMessage checkUpdates() throws ThingsboardException { - try { - return updateService.checkUpdates(); - } catch (Exception e) { - throw handleException(e); - } + return updateService.checkUpdates(); } } diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index 858a7ee3c4..55e6fc51d9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -48,6 +48,8 @@ import org.thingsboard.server.service.entitiy.alarm.TbAlarmService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; +import java.util.concurrent.ExecutionException; + import static org.thingsboard.server.controller.ControllerConstants.ALARM_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.ALARM_INFO_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.ALARM_SORT_PROPERTY_ALLOWABLE_VALUES; @@ -93,12 +95,8 @@ public class AlarmController extends BaseController { public Alarm getAlarmById(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); - try { - AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); - return checkAlarmId(alarmId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); + return checkAlarmId(alarmId, Operation.READ); } @ApiOperation(value = "Get Alarm Info (getAlarmInfoById)", @@ -110,12 +108,8 @@ public class AlarmController extends BaseController { public AlarmInfo getAlarmInfoById(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); - try { - AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); - return checkAlarmInfoId(alarmId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); + return checkAlarmInfoId(alarmId, Operation.READ); } @ApiOperation(value = "Create or update Alarm (saveAlarm)", @@ -210,7 +204,7 @@ public class AlarmController extends BaseController { @RequestParam(required = false) Long endTime, @ApiParam(value = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION) @RequestParam(required = false) Boolean fetchOriginator - ) throws ThingsboardException { + ) throws ThingsboardException, ExecutionException, InterruptedException { checkParameter("EntityId", strEntityId); checkParameter("EntityType", strEntityType); EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); @@ -223,11 +217,7 @@ public class AlarmController extends BaseController { checkEntityId(entityId, Operation.READ); TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - try { - return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); } @ApiOperation(value = "Get All Alarms (getAllAlarms)", @@ -260,7 +250,7 @@ public class AlarmController extends BaseController { @RequestParam(required = false) Long endTime, @ApiParam(value = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION) @RequestParam(required = false) Boolean fetchOriginator - ) throws ThingsboardException { + ) throws ThingsboardException, ExecutionException, InterruptedException { AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus); AlarmStatus alarmStatus = StringUtils.isEmpty(status) ? null : AlarmStatus.valueOf(status); if (alarmSearchStatus != null && alarmStatus != null) { @@ -269,14 +259,10 @@ public class AlarmController extends BaseController { } TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - try { - if (getCurrentUser().isCustomerUser()) { - return checkNotNull(alarmService.findCustomerAlarms(getCurrentUser().getTenantId(), getCurrentUser().getCustomerId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); - } else { - return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); - } - } catch (Exception e) { - throw handleException(e); + if (getCurrentUser().isCustomerUser()) { + return checkNotNull(alarmService.findCustomerAlarms(getCurrentUser().getTenantId(), getCurrentUser().getCustomerId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); + } else { + return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); } } @@ -307,11 +293,7 @@ public class AlarmController extends BaseController { "and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } checkEntityId(entityId, Operation.READ); - try { - return alarmService.findHighestAlarmSeverity(getCurrentUser().getTenantId(), entityId, alarmSearchStatus, alarmStatus); - } catch (Exception e) { - throw handleException(e); - } + return alarmService.findHighestAlarmSeverity(getCurrentUser().getTenantId(), entityId, alarmSearchStatus, alarmStatus); } } 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 8450c67ea3..11115fe2c8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -61,6 +61,7 @@ import org.thingsboard.server.service.security.permission.Resource; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.thingsboard.server.controller.ControllerConstants.ASSET_ID_PARAM_DESCRIPTION; @@ -110,12 +111,8 @@ public class AssetController extends BaseController { public Asset getAssetById(@ApiParam(value = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException { checkParameter(ASSET_ID, strAssetId); - try { - AssetId assetId = new AssetId(toUUID(strAssetId)); - return checkAssetId(assetId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + AssetId assetId = new AssetId(toUUID(strAssetId)); + return checkAssetId(assetId, Operation.READ); } @ApiOperation(value = "Get Asset Info (getAssetInfoById)", @@ -129,12 +126,8 @@ public class AssetController extends BaseController { public AssetInfo getAssetInfoById(@ApiParam(value = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException { checkParameter(ASSET_ID, strAssetId); - try { - AssetId assetId = new AssetId(toUUID(strAssetId)); - return checkAssetInfoId(assetId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + AssetId assetId = new AssetId(toUUID(strAssetId)); + return checkAssetInfoId(assetId, Operation.READ); } @ApiOperation(value = "Create Or Update Asset (saveAsset)", @@ -233,16 +226,12 @@ public class AssetController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(assetService.findAssetsByTenantIdAndType(tenantId, type, pageLink)); - } else { - return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(assetService.findAssetsByTenantIdAndType(tenantId, type, pageLink)); + } else { + return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink)); } } @@ -267,19 +256,15 @@ public class AssetController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(assetService.findAssetInfosByTenantIdAndType(tenantId, type, pageLink)); - } else if (assetProfileId != null && assetProfileId.length() > 0) { - AssetProfileId profileId = new AssetProfileId(toUUID(assetProfileId)); - return checkNotNull(assetService.findAssetInfosByTenantIdAndAssetProfileId(tenantId, profileId, pageLink)); - } else { - return checkNotNull(assetService.findAssetInfosByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(assetService.findAssetInfosByTenantIdAndType(tenantId, type, pageLink)); + } else if (assetProfileId != null && assetProfileId.length() > 0) { + AssetProfileId profileId = new AssetProfileId(toUUID(assetProfileId)); + return checkNotNull(assetService.findAssetInfosByTenantIdAndAssetProfileId(tenantId, profileId, pageLink)); + } else { + return checkNotNull(assetService.findAssetInfosByTenantId(tenantId, pageLink)); } } @@ -292,12 +277,8 @@ public class AssetController extends BaseController { public Asset getTenantAsset( @ApiParam(value = ASSET_NAME_DESCRIPTION) @RequestParam String assetName) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(assetService.findAssetByTenantIdAndName(tenantId, assetName)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(assetService.findAssetByTenantIdAndName(tenantId, assetName)); } @ApiOperation(value = "Get Customer Assets (getCustomerAssets)", @@ -322,18 +303,14 @@ public class AssetController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); - } else { - return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); + } else { + return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); } } @@ -361,21 +338,17 @@ public class AssetController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(assetService.findAssetInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); - } else if (assetProfileId != null && assetProfileId.length() > 0) { - AssetProfileId profileId = new AssetProfileId(toUUID(assetProfileId)); - return checkNotNull(assetService.findAssetInfosByTenantIdAndCustomerIdAndAssetProfileId(tenantId, customerId, profileId, pageLink)); - } else { - return checkNotNull(assetService.findAssetInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(assetService.findAssetInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); + } else if (assetProfileId != null && assetProfileId.length() > 0) { + AssetProfileId profileId = new AssetProfileId(toUUID(assetProfileId)); + return checkNotNull(assetService.findAssetInfosByTenantIdAndCustomerIdAndAssetProfileId(tenantId, customerId, profileId, pageLink)); + } else { + return checkNotNull(assetService.findAssetInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink)); } } @@ -386,26 +359,22 @@ public class AssetController extends BaseController { @ResponseBody public List getAssetsByIds( @ApiParam(value = "A list of assets ids, separated by comma ','") - @RequestParam("assetIds") String[] strAssetIds) throws ThingsboardException { + @RequestParam("assetIds") String[] strAssetIds) throws ThingsboardException, ExecutionException, InterruptedException { checkArrayParameter("assetIds", strAssetIds); - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - CustomerId customerId = user.getCustomerId(); - List assetIds = new ArrayList<>(); - for (String strAssetId : strAssetIds) { - assetIds.add(new AssetId(toUUID(strAssetId))); - } - ListenableFuture> assets; - if (customerId == null || customerId.isNullUid()) { - assets = assetService.findAssetsByTenantIdAndIdsAsync(tenantId, assetIds); - } else { - assets = assetService.findAssetsByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, assetIds); - } - return checkNotNull(assets.get()); - } catch (Exception e) { - throw handleException(e); + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + CustomerId customerId = user.getCustomerId(); + List assetIds = new ArrayList<>(); + for (String strAssetId : strAssetIds) { + assetIds.add(new AssetId(toUUID(strAssetId))); } + ListenableFuture> assets; + if (customerId == null || customerId.isNullUid()) { + assets = assetService.findAssetsByTenantIdAndIdsAsync(tenantId, assetIds); + } else { + assets = assetService.findAssetsByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, assetIds); + } + return checkNotNull(assets.get()); } @ApiOperation(value = "Find related assets (findByQuery)", @@ -415,25 +384,21 @@ public class AssetController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/assets", method = RequestMethod.POST) @ResponseBody - public List findByQuery(@RequestBody AssetSearchQuery query) throws ThingsboardException { + public List findByQuery(@RequestBody AssetSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException { checkNotNull(query); checkNotNull(query.getParameters()); checkNotNull(query.getAssetTypes()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); - try { - List assets = checkNotNull(assetService.findAssetsByQuery(getTenantId(), query).get()); - assets = assets.stream().filter(asset -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, Operation.READ, asset.getId(), asset); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - return assets; - } catch (Exception e) { - throw handleException(e); - } + List assets = checkNotNull(assetService.findAssetsByQuery(getTenantId(), query).get()); + assets = assets.stream().filter(asset -> { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, Operation.READ, asset.getId(), asset); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + return assets; } @ApiOperation(value = "Get Asset Types (getAssetTypes)", @@ -441,15 +406,11 @@ public class AssetController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/asset/types", method = RequestMethod.GET) @ResponseBody - public List getAssetTypes() throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - ListenableFuture> assetTypes = assetService.findAssetTypesByTenantId(tenantId); - return checkNotNull(assetTypes.get()); - } catch (Exception e) { - throw handleException(e); - } + public List getAssetTypes() throws ThingsboardException, ExecutionException, InterruptedException { + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + ListenableFuture> assetTypes = assetService.findAssetTypesByTenantId(tenantId); + return checkNotNull(assetTypes.get()); } @ApiOperation(value = "Assign asset to edge (assignAssetToEdge)", @@ -525,33 +486,29 @@ public class AssetController extends BaseController { @ApiParam(value = "Timestamp. Assets with creation time after it won't be queried") @RequestParam(required = false) Long endTime) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - checkEdgeId(edgeId, Operation.READ); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - PageData nonFilteredResult; - if (type != null && type.trim().length() > 0) { - nonFilteredResult = assetService.findAssetsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink); - } else { - nonFilteredResult = assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); - } - List filteredAssets = nonFilteredResult.getData().stream().filter(asset -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, Operation.READ, asset.getId(), asset); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - PageData filteredResult = new PageData<>(filteredAssets, - nonFilteredResult.getTotalPages(), - nonFilteredResult.getTotalElements(), - nonFilteredResult.hasNext()); - return checkNotNull(filteredResult); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + checkEdgeId(edgeId, Operation.READ); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + PageData nonFilteredResult; + if (type != null && type.trim().length() > 0) { + nonFilteredResult = assetService.findAssetsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink); + } else { + nonFilteredResult = assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); } + List filteredAssets = nonFilteredResult.getData().stream().filter(asset -> { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, Operation.READ, asset.getId(), asset); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + PageData filteredResult = new PageData<>(filteredAssets, + nonFilteredResult.getTotalPages(), + nonFilteredResult.getTotalElements(), + nonFilteredResult.hasNext()); + return checkNotNull(filteredResult); } @ApiOperation(value = "Import the bulk of assets (processAssetsBulkImport)", 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 2e84dd6634..903ff70602 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java @@ -78,12 +78,8 @@ public class AssetProfileController extends BaseController { @ApiParam(value = ASSET_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_PROFILE_ID) String strAssetProfileId) throws ThingsboardException { checkParameter(ASSET_PROFILE_ID, strAssetProfileId); - try { - AssetProfileId assetProfileId = new AssetProfileId(toUUID(strAssetProfileId)); - return checkAssetProfileId(assetProfileId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + AssetProfileId assetProfileId = new AssetProfileId(toUUID(strAssetProfileId)); + return checkAssetProfileId(assetProfileId, Operation.READ); } @ApiOperation(value = "Get Asset Profile Info (getAssetProfileInfoById)", @@ -97,12 +93,8 @@ public class AssetProfileController extends BaseController { @ApiParam(value = ASSET_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_PROFILE_ID) String strAssetProfileId) throws ThingsboardException { checkParameter(ASSET_PROFILE_ID, strAssetProfileId); - try { - AssetProfileId assetProfileId = new AssetProfileId(toUUID(strAssetProfileId)); - return new AssetProfileInfo(checkAssetProfileId(assetProfileId, Operation.READ)); - } catch (Exception e) { - throw handleException(e); - } + AssetProfileId assetProfileId = new AssetProfileId(toUUID(strAssetProfileId)); + return new AssetProfileInfo(checkAssetProfileId(assetProfileId, Operation.READ)); } @ApiOperation(value = "Get Default Asset Profile (getDefaultAssetProfileInfo)", @@ -113,11 +105,7 @@ public class AssetProfileController extends BaseController { @RequestMapping(value = "/assetProfileInfo/default", method = RequestMethod.GET) @ResponseBody public AssetProfileInfo getDefaultAssetProfileInfo() throws ThingsboardException { - try { - return checkNotNull(assetProfileService.findDefaultAssetProfileInfo(getTenantId())); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(assetProfileService.findDefaultAssetProfileInfo(getTenantId())); } @ApiOperation(value = "Create Or Update Asset Profile (saveAssetProfile)", @@ -191,12 +179,8 @@ public class AssetProfileController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(assetProfileService.findAssetProfiles(getTenantId(), pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(assetProfileService.findAssetProfiles(getTenantId(), pageLink)); } @ApiOperation(value = "Get Asset Profile infos (getAssetProfileInfos)", @@ -217,11 +201,7 @@ public class AssetProfileController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(assetProfileService.findAssetProfileInfos(getTenantId(), pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(assetProfileService.findAssetProfileInfos(getTenantId(), pageLink)); } } 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 33792a703f..c38e3426bc 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java @@ -97,15 +97,11 @@ public class AuditLogController extends BaseController { @RequestParam(required = false) Long endTime, @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION) @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException { - try { - checkParameter("CustomerId", strCustomerId); - TenantId tenantId = getCurrentUser().getTenantId(); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - List actionTypes = parseActionTypesStr(actionTypesStr); - return checkNotNull(auditLogService.findAuditLogsByTenantIdAndCustomerId(tenantId, new CustomerId(UUID.fromString(strCustomerId)), actionTypes, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + checkParameter("CustomerId", strCustomerId); + TenantId tenantId = getCurrentUser().getTenantId(); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + List actionTypes = parseActionTypesStr(actionTypesStr); + return checkNotNull(auditLogService.findAuditLogsByTenantIdAndCustomerId(tenantId, new CustomerId(UUID.fromString(strCustomerId)), actionTypes, pageLink)); } @ApiOperation(value = "Get audit logs by user id (getAuditLogsByUserId)", @@ -135,15 +131,11 @@ public class AuditLogController extends BaseController { @RequestParam(required = false) Long endTime, @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION) @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException { - try { - checkParameter("UserId", strUserId); - TenantId tenantId = getCurrentUser().getTenantId(); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - List actionTypes = parseActionTypesStr(actionTypesStr); - return checkNotNull(auditLogService.findAuditLogsByTenantIdAndUserId(tenantId, new UserId(UUID.fromString(strUserId)), actionTypes, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + checkParameter("UserId", strUserId); + TenantId tenantId = getCurrentUser().getTenantId(); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + List actionTypes = parseActionTypesStr(actionTypesStr); + return checkNotNull(auditLogService.findAuditLogsByTenantIdAndUserId(tenantId, new UserId(UUID.fromString(strUserId)), actionTypes, pageLink)); } @ApiOperation(value = "Get audit logs by entity id (getAuditLogsByEntityId)", @@ -176,16 +168,12 @@ public class AuditLogController extends BaseController { @RequestParam(required = false) Long endTime, @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION) @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException { - try { - checkParameter("EntityId", strEntityId); - checkParameter("EntityType", strEntityType); - TenantId tenantId = getCurrentUser().getTenantId(); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - List actionTypes = parseActionTypesStr(actionTypesStr); - return checkNotNull(auditLogService.findAuditLogsByTenantIdAndEntityId(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), actionTypes, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + checkParameter("EntityId", strEntityId); + checkParameter("EntityType", strEntityType); + TenantId tenantId = getCurrentUser().getTenantId(); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + List actionTypes = parseActionTypesStr(actionTypesStr); + return checkNotNull(auditLogService.findAuditLogsByTenantIdAndEntityId(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), actionTypes, pageLink)); } @ApiOperation(value = "Get all audit logs (getAuditLogs)", @@ -212,14 +200,10 @@ public class AuditLogController extends BaseController { @RequestParam(required = false) Long endTime, @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION) @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - List actionTypes = parseActionTypesStr(actionTypesStr); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - return checkNotNull(auditLogService.findAuditLogsByTenantId(tenantId, actionTypes, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + List actionTypes = parseActionTypesStr(actionTypesStr); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + return checkNotNull(auditLogService.findAuditLogsByTenantId(tenantId, actionTypes, pageLink)); } private List parseActionTypesStr(String actionTypesStr) { 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 72be55a135..e8cc37d0d8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java @@ -86,12 +86,8 @@ public class AuthController extends BaseController { @RequestMapping(value = "/auth/user", method = RequestMethod.GET) public @ResponseBody User getUser() throws ThingsboardException { - try { - SecurityUser securityUser = getCurrentUser(); - return userService.findUserById(securityUser.getTenantId(), securityUser.getId()); - } catch (Exception e) { - throw handleException(e); - } + SecurityUser securityUser = getCurrentUser(); + return userService.findUserById(securityUser.getTenantId(), securityUser.getId()); } @ApiOperation(value = "Logout (logout)", @@ -111,31 +107,27 @@ public class AuthController extends BaseController { public ObjectNode changePassword( @ApiParam(value = "Change Password Request") @RequestBody ChangePasswordRequest changePasswordRequest) throws ThingsboardException { - try { - String currentPassword = changePasswordRequest.getCurrentPassword(); - String newPassword = changePasswordRequest.getNewPassword(); - SecurityUser securityUser = getCurrentUser(); - UserCredentials userCredentials = userService.findUserCredentialsByUserId(TenantId.SYS_TENANT_ID, securityUser.getId()); - if (!passwordEncoder.matches(currentPassword, userCredentials.getPassword())) { - throw new ThingsboardException("Current password doesn't match!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - systemSecurityService.validatePassword(securityUser.getTenantId(), newPassword, userCredentials); - if (passwordEncoder.matches(newPassword, userCredentials.getPassword())) { - throw new ThingsboardException("New password should be different from existing!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - userCredentials.setPassword(passwordEncoder.encode(newPassword)); - userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials); + String currentPassword = changePasswordRequest.getCurrentPassword(); + String newPassword = changePasswordRequest.getNewPassword(); + SecurityUser securityUser = getCurrentUser(); + UserCredentials userCredentials = userService.findUserCredentialsByUserId(TenantId.SYS_TENANT_ID, securityUser.getId()); + if (!passwordEncoder.matches(currentPassword, userCredentials.getPassword())) { + throw new ThingsboardException("Current password doesn't match!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } + systemSecurityService.validatePassword(securityUser.getTenantId(), newPassword, userCredentials); + if (passwordEncoder.matches(newPassword, userCredentials.getPassword())) { + throw new ThingsboardException("New password should be different from existing!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } + userCredentials.setPassword(passwordEncoder.encode(newPassword)); + userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials); - sendEntityNotificationMsg(getTenantId(), userCredentials.getUserId(), EdgeEventActionType.CREDENTIALS_UPDATED); + sendEntityNotificationMsg(getTenantId(), userCredentials.getUserId(), EdgeEventActionType.CREDENTIALS_UPDATED); - eventPublisher.publishEvent(new UserAuthDataChangedEvent(securityUser.getId())); - ObjectNode response = JacksonUtil.newObjectNode(); - response.put("token", tokenFactory.createAccessJwtToken(securityUser).getToken()); - response.put("refreshToken", tokenFactory.createRefreshToken(securityUser).getToken()); - return response; - } catch (Exception e) { - throw handleException(e); - } + eventPublisher.publishEvent(new UserAuthDataChangedEvent(securityUser.getId())); + ObjectNode response = JacksonUtil.newObjectNode(); + response.put("token", tokenFactory.createAccessJwtToken(securityUser).getToken()); + response.put("refreshToken", tokenFactory.createRefreshToken(securityUser).getToken()); + return response; } @ApiOperation(value = "Get the current User password policy (getUserPasswordPolicy)", @@ -143,13 +135,9 @@ public class AuthController extends BaseController { @RequestMapping(value = "/noauth/userPasswordPolicy", method = RequestMethod.GET) @ResponseBody public UserPasswordPolicy getUserPasswordPolicy() throws ThingsboardException { - try { - SecuritySettings securitySettings = - checkNotNull(systemSecurityService.getSecuritySettings(TenantId.SYS_TENANT_ID)); - return securitySettings.getPasswordPolicy(); - } catch (Exception e) { - throw handleException(e); - } + SecuritySettings securitySettings = + checkNotNull(systemSecurityService.getSecuritySettings(TenantId.SYS_TENANT_ID)); + return securitySettings.getPasswordPolicy(); } @ApiOperation(value = "Check Activate User Token (checkActivateToken)", @@ -244,37 +232,33 @@ public class AuthController extends BaseController { @RequestBody ActivateUserRequest activateRequest, @RequestParam(required = false, defaultValue = "true") boolean sendActivationMail, HttpServletRequest request) throws ThingsboardException { - try { - String activateToken = activateRequest.getActivateToken(); - String password = activateRequest.getPassword(); - systemSecurityService.validatePassword(TenantId.SYS_TENANT_ID, password, null); - String encodedPassword = passwordEncoder.encode(password); - UserCredentials credentials = userService.activateUserCredentials(TenantId.SYS_TENANT_ID, activateToken, encodedPassword); - User user = userService.findUserById(TenantId.SYS_TENANT_ID, credentials.getUserId()); - UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); - SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); - userService.setUserCredentialsEnabled(user.getTenantId(), user.getId(), true); - String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request); - String loginUrl = String.format("%s/login", baseUrl); - String email = user.getEmail(); + String activateToken = activateRequest.getActivateToken(); + String password = activateRequest.getPassword(); + systemSecurityService.validatePassword(TenantId.SYS_TENANT_ID, password, null); + String encodedPassword = passwordEncoder.encode(password); + UserCredentials credentials = userService.activateUserCredentials(TenantId.SYS_TENANT_ID, activateToken, encodedPassword); + User user = userService.findUserById(TenantId.SYS_TENANT_ID, credentials.getUserId()); + UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); + SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); + userService.setUserCredentialsEnabled(user.getTenantId(), user.getId(), true); + String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request); + String loginUrl = String.format("%s/login", baseUrl); + String email = user.getEmail(); - if (sendActivationMail) { - try { - mailService.sendAccountActivatedEmail(loginUrl, email); - } catch (Exception e) { - log.info("Unable to send account activation email [{}]", e.getMessage()); - } + if (sendActivationMail) { + try { + mailService.sendAccountActivatedEmail(loginUrl, email); + } catch (Exception e) { + log.info("Unable to send account activation email [{}]", e.getMessage()); } + } - sendEntityNotificationMsg(user.getTenantId(), user.getId(), EdgeEventActionType.CREDENTIALS_UPDATED); + sendEntityNotificationMsg(user.getTenantId(), user.getId(), EdgeEventActionType.CREDENTIALS_UPDATED); - JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); - JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); + JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); + JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); - return new JwtTokenPair(accessToken.getToken(), refreshToken.getToken()); - } catch (Exception e) { - throw handleException(e); - } + return new JwtTokenPair(accessToken.getToken(), refreshToken.getToken()); } @ApiOperation(value = "Reset password (resetPassword)", @@ -288,87 +272,79 @@ public class AuthController extends BaseController { @ApiParam(value = "Reset password request.") @RequestBody ResetPasswordRequest resetPasswordRequest, HttpServletRequest request) throws ThingsboardException { - try { - String resetToken = resetPasswordRequest.getResetToken(); - String password = resetPasswordRequest.getPassword(); - UserCredentials userCredentials = userService.findUserCredentialsByResetToken(TenantId.SYS_TENANT_ID, resetToken); - if (userCredentials != null) { - systemSecurityService.validatePassword(TenantId.SYS_TENANT_ID, password, userCredentials); - if (passwordEncoder.matches(password, userCredentials.getPassword())) { - throw new ThingsboardException("New password should be different from existing!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - String encodedPassword = passwordEncoder.encode(password); - userCredentials.setPassword(encodedPassword); - userCredentials.setResetToken(null); - userCredentials = userService.replaceUserCredentials(TenantId.SYS_TENANT_ID, userCredentials); - User user = userService.findUserById(TenantId.SYS_TENANT_ID, userCredentials.getUserId()); - UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); - SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled(), principal); - String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request); - String loginUrl = String.format("%s/login", baseUrl); - String email = user.getEmail(); - mailService.sendPasswordWasResetEmail(loginUrl, email); + String resetToken = resetPasswordRequest.getResetToken(); + String password = resetPasswordRequest.getPassword(); + UserCredentials userCredentials = userService.findUserCredentialsByResetToken(TenantId.SYS_TENANT_ID, resetToken); + if (userCredentials != null) { + systemSecurityService.validatePassword(TenantId.SYS_TENANT_ID, password, userCredentials); + if (passwordEncoder.matches(password, userCredentials.getPassword())) { + throw new ThingsboardException("New password should be different from existing!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } + String encodedPassword = passwordEncoder.encode(password); + userCredentials.setPassword(encodedPassword); + userCredentials.setResetToken(null); + userCredentials = userService.replaceUserCredentials(TenantId.SYS_TENANT_ID, userCredentials); + User user = userService.findUserById(TenantId.SYS_TENANT_ID, userCredentials.getUserId()); + UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); + SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled(), principal); + String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request); + String loginUrl = String.format("%s/login", baseUrl); + String email = user.getEmail(); + mailService.sendPasswordWasResetEmail(loginUrl, email); - eventPublisher.publishEvent(new UserAuthDataChangedEvent(securityUser.getId())); - JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); - JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); + eventPublisher.publishEvent(new UserAuthDataChangedEvent(securityUser.getId())); + JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); + JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); - return new JwtTokenPair(accessToken.getToken(), refreshToken.getToken()); - } else { - throw new ThingsboardException("Invalid reset token!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - } catch (Exception e) { - throw handleException(e); + return new JwtTokenPair(accessToken.getToken(), refreshToken.getToken()); + } else { + throw new ThingsboardException("Invalid reset token!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } } private void logLogoutAction(HttpServletRequest request) throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); - RestAuthenticationDetails details = new RestAuthenticationDetails(request); - String clientAddress = details.getClientAddress(); - String browser = "Unknown"; - String os = "Unknown"; - String device = "Unknown"; - if (details.getUserAgent() != null) { - Client userAgent = details.getUserAgent(); - if (userAgent.userAgent != null) { - browser = userAgent.userAgent.family; - if (userAgent.userAgent.major != null) { - browser += " " + userAgent.userAgent.major; - if (userAgent.userAgent.minor != null) { - browser += "." + userAgent.userAgent.minor; - if (userAgent.userAgent.patch != null) { - browser += "." + userAgent.userAgent.patch; - } + SecurityUser user = getCurrentUser(); + RestAuthenticationDetails details = new RestAuthenticationDetails(request); + String clientAddress = details.getClientAddress(); + String browser = "Unknown"; + String os = "Unknown"; + String device = "Unknown"; + if (details.getUserAgent() != null) { + Client userAgent = details.getUserAgent(); + if (userAgent.userAgent != null) { + browser = userAgent.userAgent.family; + if (userAgent.userAgent.major != null) { + browser += " " + userAgent.userAgent.major; + if (userAgent.userAgent.minor != null) { + browser += "." + userAgent.userAgent.minor; + if (userAgent.userAgent.patch != null) { + browser += "." + userAgent.userAgent.patch; } } } - if (userAgent.os != null) { - os = userAgent.os.family; - if (userAgent.os.major != null) { - os += " " + userAgent.os.major; - if (userAgent.os.minor != null) { - os += "." + userAgent.os.minor; - if (userAgent.os.patch != null) { - os += "." + userAgent.os.patch; - if (userAgent.os.patchMinor != null) { - os += "." + userAgent.os.patchMinor; - } + } + if (userAgent.os != null) { + os = userAgent.os.family; + if (userAgent.os.major != null) { + os += " " + userAgent.os.major; + if (userAgent.os.minor != null) { + os += "." + userAgent.os.minor; + if (userAgent.os.patch != null) { + os += "." + userAgent.os.patch; + if (userAgent.os.patchMinor != null) { + os += "." + userAgent.os.patchMinor; } } } } - if (userAgent.device != null) { - device = userAgent.device.family; - } } - auditLogService.logEntityAction( - user.getTenantId(), user.getCustomerId(), user.getId(), - user.getName(), user.getId(), null, ActionType.LOGOUT, null, clientAddress, browser, os, device); - - } catch (Exception e) { - throw handleException(e); + if (userAgent.device != null) { + device = userAgent.device.family; + } } + auditLogService.logEntityAction( + user.getTenantId(), user.getCustomerId(), user.getId(), + user.getName(), user.getId(), null, ActionType.LOGOUT, null, clientAddress, browser, os, device); + } } 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 31f64254af..9a1a83d351 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java @@ -57,11 +57,7 @@ public class ComponentDescriptorController extends BaseController { @ApiParam(value = "Component Descriptor class name", required = true) @PathVariable("componentDescriptorClazz") String strComponentDescriptorClazz) throws ThingsboardException { checkParameter("strComponentDescriptorClazz", strComponentDescriptorClazz); - try { - return checkComponentDescriptorByClazz(strComponentDescriptorClazz); - } catch (Exception e) { - throw handleException(e); - } + return checkComponentDescriptorByClazz(strComponentDescriptorClazz); } @ApiOperation(value = "Get Component Descriptors (getComponentDescriptorsByType)", @@ -76,11 +72,7 @@ public class ComponentDescriptorController extends BaseController { @ApiParam(value = "Type of the Rule Chain", allowableValues = "CORE,EDGE") @RequestParam(value = "ruleChainType", required = false) String strRuleChainType) throws ThingsboardException { checkParameter("componentType", strComponentType); - try { - return checkComponentDescriptorsByType(ComponentType.valueOf(strComponentType), getRuleChainType(strRuleChainType)); - } catch (Exception e) { - throw handleException(e); - } + return checkComponentDescriptorsByType(ComponentType.valueOf(strComponentType), getRuleChainType(strRuleChainType)); } @ApiOperation(value = "Get Component Descriptors (getComponentDescriptorsByTypes)", @@ -95,15 +87,11 @@ public class ComponentDescriptorController extends BaseController { @ApiParam(value = "Type of the Rule Chain", allowableValues = "CORE,EDGE") @RequestParam(value = "ruleChainType", required = false) String strRuleChainType) throws ThingsboardException { checkArrayParameter("componentTypes", strComponentTypes); - try { - Set componentTypes = new HashSet<>(); - for (String strComponentType : strComponentTypes) { - componentTypes.add(ComponentType.valueOf(strComponentType)); - } - return checkComponentDescriptorsByTypes(componentTypes, getRuleChainType(strRuleChainType)); - } catch (Exception e) { - throw handleException(e); + Set componentTypes = new HashSet<>(); + for (String strComponentType : strComponentTypes) { + componentTypes.add(ComponentType.valueOf(strComponentType)); } + return checkComponentDescriptorsByTypes(componentTypes, getRuleChainType(strRuleChainType)); } private RuleChainType getRuleChainType(String strRuleChainType) { 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 591eaa1318..7d33f4e0d7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -79,16 +79,12 @@ public class CustomerController extends BaseController { @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION) @PathVariable(CUSTOMER_ID) String strCustomerId) throws ThingsboardException { checkParameter(CUSTOMER_ID, strCustomerId); - try { - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - Customer customer = checkCustomerId(customerId, Operation.READ); - if (!customer.getAdditionalInfo().isNull()) { - processDashboardIdFromAdditionalInfo((ObjectNode) customer.getAdditionalInfo(), HOME_DASHBOARD); - } - return customer; - } catch (Exception e) { - throw handleException(e); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + Customer customer = checkCustomerId(customerId, Operation.READ); + if (!customer.getAdditionalInfo().isNull()) { + processDashboardIdFromAdditionalInfo((ObjectNode) customer.getAdditionalInfo(), HOME_DASHBOARD); } + return customer; } @@ -102,17 +98,13 @@ public class CustomerController extends BaseController { @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION) @PathVariable(CUSTOMER_ID) String strCustomerId) throws ThingsboardException { checkParameter(CUSTOMER_ID, strCustomerId); - try { - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - Customer customer = checkCustomerId(customerId, Operation.READ); - ObjectMapper objectMapper = new ObjectMapper(); - ObjectNode infoObject = objectMapper.createObjectNode(); - infoObject.put("title", customer.getTitle()); - infoObject.put(IS_PUBLIC, customer.isPublic()); - return infoObject; - } catch (Exception e) { - throw handleException(e); - } + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + Customer customer = checkCustomerId(customerId, Operation.READ); + ObjectMapper objectMapper = new ObjectMapper(); + ObjectNode infoObject = objectMapper.createObjectNode(); + infoObject.put("title", customer.getTitle()); + infoObject.put(IS_PUBLIC, customer.isPublic()); + return infoObject; } @ApiOperation(value = "Get Customer Title (getCustomerTitleById)", @@ -125,13 +117,9 @@ public class CustomerController extends BaseController { @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION) @PathVariable(CUSTOMER_ID) String strCustomerId) throws ThingsboardException { checkParameter(CUSTOMER_ID, strCustomerId); - try { - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - Customer customer = checkCustomerId(customerId, Operation.READ); - return customer.getTitle(); - } catch (Exception e) { - throw handleException(e); - } + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + Customer customer = checkCustomerId(customerId, Operation.READ); + return customer.getTitle(); } @ApiOperation(value = "Create or update Customer (saveCustomer)", @@ -182,13 +170,9 @@ public class CustomerController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(customerService.findCustomersByTenantId(tenantId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(customerService.findCustomersByTenantId(tenantId, pageLink)); } @ApiOperation(value = "Get Tenant Customer by Customer title (getTenantCustomer)", @@ -199,11 +183,7 @@ public class CustomerController extends BaseController { public Customer getTenantCustomer( @ApiParam(value = "A string value representing the Customer title.") @RequestParam String customerTitle) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(customerService.findCustomerByTenantIdAndTitle(tenantId, customerTitle), "Customer with title [" + customerTitle + "] is not found"); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(customerService.findCustomerByTenantIdAndTitle(tenantId, customerTitle), "Customer with title [" + customerTitle + "] is not found"); } } 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 9ceeb0d14c..4588cf96fd 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -140,12 +140,8 @@ public class DashboardController extends BaseController { @ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION) @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { checkParameter(DASHBOARD_ID, strDashboardId); - try { - DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); - return checkDashboardInfoId(dashboardId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); + return checkDashboardInfoId(dashboardId, Operation.READ); } @ApiOperation(value = "Get Dashboard (getDashboardById)", @@ -159,12 +155,8 @@ public class DashboardController extends BaseController { @ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION) @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { checkParameter(DASHBOARD_ID, strDashboardId); - try { - DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); - return checkDashboardId(dashboardId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); + return checkDashboardId(dashboardId, Operation.READ); } @ApiOperation(value = "Create Or Update Dashboard (saveDashboard)", @@ -362,14 +354,10 @@ public class DashboardController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); - checkTenantId(tenantId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); + checkTenantId(tenantId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink)); } @ApiOperation(value = "Get Tenant Dashboards (getTenantDashboards)", @@ -392,16 +380,12 @@ public class DashboardController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (mobile != null && mobile) { - return checkNotNull(dashboardService.findMobileDashboardsByTenantId(tenantId, pageLink)); - } else { - return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (mobile != null && mobile) { + return checkNotNull(dashboardService.findMobileDashboardsByTenantId(tenantId, pageLink)); + } else { + return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink)); } } @@ -428,18 +412,14 @@ public class DashboardController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter(CUSTOMER_ID, strCustomerId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (mobile != null && mobile) { - return checkNotNull(dashboardService.findMobileDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } else { - return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (mobile != null && mobile) { + return checkNotNull(dashboardService.findMobileDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); + } else { + return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); } } @@ -453,31 +433,27 @@ public class DashboardController extends BaseController { @RequestMapping(value = "/dashboard/home", method = RequestMethod.GET) @ResponseBody public HomeDashboard getHomeDashboard() throws ThingsboardException { - try { - SecurityUser securityUser = getCurrentUser(); - if (securityUser.isSystemAdmin()) { - return null; + SecurityUser securityUser = getCurrentUser(); + if (securityUser.isSystemAdmin()) { + return null; + } + User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId()); + JsonNode additionalInfo = user.getAdditionalInfo(); + HomeDashboard homeDashboard; + homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); + if (homeDashboard == null) { + if (securityUser.isCustomerUser()) { + Customer customer = customerService.findCustomerById(securityUser.getTenantId(), securityUser.getCustomerId()); + additionalInfo = customer.getAdditionalInfo(); + homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); } - User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId()); - JsonNode additionalInfo = user.getAdditionalInfo(); - HomeDashboard homeDashboard; - homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); if (homeDashboard == null) { - if (securityUser.isCustomerUser()) { - Customer customer = customerService.findCustomerById(securityUser.getTenantId(), securityUser.getCustomerId()); - additionalInfo = customer.getAdditionalInfo(); - homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); - } - if (homeDashboard == null) { - Tenant tenant = tenantService.findTenantById(securityUser.getTenantId()); - additionalInfo = tenant.getAdditionalInfo(); - homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); - } + Tenant tenant = tenantService.findTenantById(securityUser.getTenantId()); + additionalInfo = tenant.getAdditionalInfo(); + homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); } - return homeDashboard; - } catch (Exception e) { - throw handleException(e); } + return homeDashboard; } @ApiOperation(value = "Get Home Dashboard Info (getHomeDashboardInfo)", @@ -490,31 +466,27 @@ public class DashboardController extends BaseController { @RequestMapping(value = "/dashboard/home/info", method = RequestMethod.GET) @ResponseBody public HomeDashboardInfo getHomeDashboardInfo() throws ThingsboardException { - try { - SecurityUser securityUser = getCurrentUser(); - if (securityUser.isSystemAdmin()) { - return null; + SecurityUser securityUser = getCurrentUser(); + if (securityUser.isSystemAdmin()) { + return null; + } + User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId()); + JsonNode additionalInfo = user.getAdditionalInfo(); + HomeDashboardInfo homeDashboardInfo; + homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo); + if (homeDashboardInfo == null) { + if (securityUser.isCustomerUser()) { + Customer customer = customerService.findCustomerById(securityUser.getTenantId(), securityUser.getCustomerId()); + additionalInfo = customer.getAdditionalInfo(); + homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo); } - User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId()); - JsonNode additionalInfo = user.getAdditionalInfo(); - HomeDashboardInfo homeDashboardInfo; - homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo); if (homeDashboardInfo == null) { - if (securityUser.isCustomerUser()) { - Customer customer = customerService.findCustomerById(securityUser.getTenantId(), securityUser.getCustomerId()); - additionalInfo = customer.getAdditionalInfo(); - homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo); - } - if (homeDashboardInfo == null) { - Tenant tenant = tenantService.findTenantById(securityUser.getTenantId()); - additionalInfo = tenant.getAdditionalInfo(); - homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo); - } + Tenant tenant = tenantService.findTenantById(securityUser.getTenantId()); + additionalInfo = tenant.getAdditionalInfo(); + homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo); } - return homeDashboardInfo; - } catch (Exception e) { - throw handleException(e); } + return homeDashboardInfo; } @ApiOperation(value = "Get Tenant Home Dashboard Info (getTenantHomeDashboardInfo)", @@ -525,22 +497,18 @@ public class DashboardController extends BaseController { @RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.GET) @ResponseBody public HomeDashboardInfo getTenantHomeDashboardInfo() throws ThingsboardException { - try { - Tenant tenant = tenantService.findTenantById(getTenantId()); - JsonNode additionalInfo = tenant.getAdditionalInfo(); - DashboardId dashboardId = null; - boolean hideDashboardToolbar = true; - if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID) && !additionalInfo.get(HOME_DASHBOARD_ID).isNull()) { - String strDashboardId = additionalInfo.get(HOME_DASHBOARD_ID).asText(); - dashboardId = new DashboardId(toUUID(strDashboardId)); - if (additionalInfo.has(HOME_DASHBOARD_HIDE_TOOLBAR)) { - hideDashboardToolbar = additionalInfo.get(HOME_DASHBOARD_HIDE_TOOLBAR).asBoolean(); - } + Tenant tenant = tenantService.findTenantById(getTenantId()); + JsonNode additionalInfo = tenant.getAdditionalInfo(); + DashboardId dashboardId = null; + boolean hideDashboardToolbar = true; + if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID) && !additionalInfo.get(HOME_DASHBOARD_ID).isNull()) { + String strDashboardId = additionalInfo.get(HOME_DASHBOARD_ID).asText(); + dashboardId = new DashboardId(toUUID(strDashboardId)); + if (additionalInfo.has(HOME_DASHBOARD_HIDE_TOOLBAR)) { + hideDashboardToolbar = additionalInfo.get(HOME_DASHBOARD_HIDE_TOOLBAR).asBoolean(); } - return new HomeDashboardInfo(dashboardId, hideDashboardToolbar); - } catch (Exception e) { - throw handleException(e); } + return new HomeDashboardInfo(dashboardId, hideDashboardToolbar); } @ApiOperation(value = "Update Tenant Home Dashboard Info (getTenantHomeDashboardInfo)", @@ -554,27 +522,23 @@ public class DashboardController extends BaseController { @ApiParam(value = "A JSON object that represents home dashboard id and other parameters", required = true) @RequestBody HomeDashboardInfo homeDashboardInfo) throws ThingsboardException { - try { - if (homeDashboardInfo.getDashboardId() != null) { - checkDashboardId(homeDashboardInfo.getDashboardId(), Operation.READ); - } - Tenant tenant = tenantService.findTenantById(getTenantId()); - JsonNode additionalInfo = tenant.getAdditionalInfo(); - if (additionalInfo == null || !(additionalInfo instanceof ObjectNode)) { - additionalInfo = JacksonUtil.OBJECT_MAPPER.createObjectNode(); - } - if (homeDashboardInfo.getDashboardId() != null) { - ((ObjectNode) additionalInfo).put(HOME_DASHBOARD_ID, homeDashboardInfo.getDashboardId().getId().toString()); - ((ObjectNode) additionalInfo).put(HOME_DASHBOARD_HIDE_TOOLBAR, homeDashboardInfo.isHideDashboardToolbar()); - } else { - ((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_ID); - ((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_HIDE_TOOLBAR); - } - tenant.setAdditionalInfo(additionalInfo); - tenantService.saveTenant(tenant); - } catch (Exception e) { - throw handleException(e); + if (homeDashboardInfo.getDashboardId() != null) { + checkDashboardId(homeDashboardInfo.getDashboardId(), Operation.READ); + } + Tenant tenant = tenantService.findTenantById(getTenantId()); + JsonNode additionalInfo = tenant.getAdditionalInfo(); + if (additionalInfo == null || !(additionalInfo instanceof ObjectNode)) { + additionalInfo = JacksonUtil.OBJECT_MAPPER.createObjectNode(); + } + if (homeDashboardInfo.getDashboardId() != null) { + ((ObjectNode) additionalInfo).put(HOME_DASHBOARD_ID, homeDashboardInfo.getDashboardId().getId().toString()); + ((ObjectNode) additionalInfo).put(HOME_DASHBOARD_HIDE_TOOLBAR, homeDashboardInfo.isHideDashboardToolbar()); + } else { + ((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_ID); + ((ObjectNode) additionalInfo).remove(HOME_DASHBOARD_HIDE_TOOLBAR); } + tenant.setAdditionalInfo(additionalInfo); + tenantService.saveTenant(tenant); } private HomeDashboardInfo extractHomeDashboardInfoFromAdditionalInfo(JsonNode additionalInfo) { @@ -681,28 +645,24 @@ public class DashboardController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("edgeId", strEdgeId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - checkEdgeId(edgeId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - PageData nonFilteredResult = dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); - List filteredDashboards = nonFilteredResult.getData().stream().filter(dashboardInfo -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.DASHBOARD, Operation.READ, dashboardInfo.getId(), dashboardInfo); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - PageData filteredResult = new PageData<>(filteredDashboards, - nonFilteredResult.getTotalPages(), - nonFilteredResult.getTotalElements(), - nonFilteredResult.hasNext()); - return checkNotNull(filteredResult); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + checkEdgeId(edgeId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + PageData nonFilteredResult = dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); + List filteredDashboards = nonFilteredResult.getData().stream().filter(dashboardInfo -> { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.DASHBOARD, Operation.READ, dashboardInfo.getId(), dashboardInfo); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + PageData filteredResult = new PageData<>(filteredDashboards, + nonFilteredResult.getTotalPages(), + nonFilteredResult.getTotalElements(), + nonFilteredResult.hasNext()); + return checkNotNull(filteredResult); } private Set customerIdFromStr(String[] strCustomerIds) { 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 ae1558381f..8155e18384 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -77,6 +77,7 @@ import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_AUTHORITY_PARAGRAPH; @@ -308,16 +309,12 @@ public class DeviceController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(deviceService.findDevicesByTenantIdAndType(tenantId, type, pageLink)); - } else { - return checkNotNull(deviceService.findDevicesByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(deviceService.findDevicesByTenantIdAndType(tenantId, type, pageLink)); + } else { + return checkNotNull(deviceService.findDevicesByTenantId(tenantId, pageLink)); } } @@ -343,19 +340,15 @@ public class DeviceController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder ) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(deviceService.findDeviceInfosByTenantIdAndType(tenantId, type, pageLink)); - } else if (deviceProfileId != null && deviceProfileId.length() > 0) { - DeviceProfileId profileId = new DeviceProfileId(toUUID(deviceProfileId)); - return checkNotNull(deviceService.findDeviceInfosByTenantIdAndDeviceProfileId(tenantId, profileId, pageLink)); - } else { - return checkNotNull(deviceService.findDeviceInfosByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(deviceService.findDeviceInfosByTenantIdAndType(tenantId, type, pageLink)); + } else if (deviceProfileId != null && deviceProfileId.length() > 0) { + DeviceProfileId profileId = new DeviceProfileId(toUUID(deviceProfileId)); + return checkNotNull(deviceService.findDeviceInfosByTenantIdAndDeviceProfileId(tenantId, profileId, pageLink)); + } else { + return checkNotNull(deviceService.findDeviceInfosByTenantId(tenantId, pageLink)); } } @@ -368,12 +361,8 @@ public class DeviceController extends BaseController { public Device getTenantDevice( @ApiParam(value = DEVICE_NAME_DESCRIPTION) @RequestParam String deviceName) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(deviceService.findDeviceByTenantIdAndName(tenantId, deviceName)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(deviceService.findDeviceByTenantIdAndName(tenantId, deviceName)); } @ApiOperation(value = "Get Customer Devices (getCustomerDevices)", @@ -398,18 +387,14 @@ public class DeviceController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); - } else { - return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); + } else { + return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, pageLink)); } } @@ -437,21 +422,17 @@ public class DeviceController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); - } else if (deviceProfileId != null && deviceProfileId.length() > 0) { - DeviceProfileId profileId = new DeviceProfileId(toUUID(deviceProfileId)); - return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerIdAndDeviceProfileId(tenantId, customerId, profileId, pageLink)); - } else { - return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); + } else if (deviceProfileId != null && deviceProfileId.length() > 0) { + DeviceProfileId profileId = new DeviceProfileId(toUUID(deviceProfileId)); + return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerIdAndDeviceProfileId(tenantId, customerId, profileId, pageLink)); + } else { + return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink)); } } @@ -462,26 +443,22 @@ public class DeviceController extends BaseController { @ResponseBody public List getDevicesByIds( @ApiParam(value = "A list of devices ids, separated by comma ','") - @RequestParam("deviceIds") String[] strDeviceIds) throws ThingsboardException { + @RequestParam("deviceIds") String[] strDeviceIds) throws ThingsboardException, ExecutionException, InterruptedException { checkArrayParameter("deviceIds", strDeviceIds); - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - CustomerId customerId = user.getCustomerId(); - List deviceIds = new ArrayList<>(); - for (String strDeviceId : strDeviceIds) { - deviceIds.add(new DeviceId(toUUID(strDeviceId))); - } - ListenableFuture> devices; - if (customerId == null || customerId.isNullUid()) { - devices = deviceService.findDevicesByTenantIdAndIdsAsync(tenantId, deviceIds); - } else { - devices = deviceService.findDevicesByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, deviceIds); - } - return checkNotNull(devices.get()); - } catch (Exception e) { - throw handleException(e); + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + CustomerId customerId = user.getCustomerId(); + List deviceIds = new ArrayList<>(); + for (String strDeviceId : strDeviceIds) { + deviceIds.add(new DeviceId(toUUID(strDeviceId))); + } + ListenableFuture> devices; + if (customerId == null || customerId.isNullUid()) { + devices = deviceService.findDevicesByTenantIdAndIdsAsync(tenantId, deviceIds); + } else { + devices = deviceService.findDevicesByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, deviceIds); } + return checkNotNull(devices.get()); } @ApiOperation(value = "Find related devices (findByQuery)", @@ -493,25 +470,21 @@ public class DeviceController extends BaseController { @ResponseBody public List findByQuery( @ApiParam(value = "The device search query JSON") - @RequestBody DeviceSearchQuery query) throws ThingsboardException { + @RequestBody DeviceSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException { checkNotNull(query); checkNotNull(query.getParameters()); checkNotNull(query.getDeviceTypes()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); - try { - List devices = checkNotNull(deviceService.findDevicesByQuery(getCurrentUser().getTenantId(), query).get()); - devices = devices.stream().filter(device -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, Operation.READ, device.getId(), device); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - return devices; - } catch (Exception e) { - throw handleException(e); - } + List devices = checkNotNull(deviceService.findDevicesByQuery(getCurrentUser().getTenantId(), query).get()); + devices = devices.stream().filter(device -> { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, Operation.READ, device.getId(), device); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + return devices; } @ApiOperation(value = "Get Device Types (getDeviceTypes)", @@ -520,15 +493,11 @@ public class DeviceController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/device/types", method = RequestMethod.GET) @ResponseBody - public List getDeviceTypes() throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - ListenableFuture> deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId); - return checkNotNull(deviceTypes.get()); - } catch (Exception e) { - throw handleException(e); - } + public List getDeviceTypes() throws ThingsboardException, ExecutionException, InterruptedException { + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + ListenableFuture> deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId); + return checkNotNull(deviceTypes.get()); } @ApiOperation(value = "Claim device (claimDevice)", @@ -722,33 +691,29 @@ public class DeviceController extends BaseController { @ApiParam(value = "Timestamp. Devices with creation time after it won't be queried") @RequestParam(required = false) Long endTime) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - checkEdgeId(edgeId, Operation.READ); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - PageData nonFilteredResult; - if (type != null && type.trim().length() > 0) { - nonFilteredResult = deviceService.findDevicesByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink); - } else { - nonFilteredResult = deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink); - } - List filteredDevices = nonFilteredResult.getData().stream().filter(device -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, Operation.READ, device.getId(), device); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - PageData filteredResult = new PageData<>(filteredDevices, - nonFilteredResult.getTotalPages(), - nonFilteredResult.getTotalElements(), - nonFilteredResult.hasNext()); - return checkNotNull(filteredResult); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + checkEdgeId(edgeId, Operation.READ); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + PageData nonFilteredResult; + if (type != null && type.trim().length() > 0) { + nonFilteredResult = deviceService.findDevicesByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink); + } else { + nonFilteredResult = deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink); } + List filteredDevices = nonFilteredResult.getData().stream().filter(device -> { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, Operation.READ, device.getId(), device); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + PageData filteredResult = new PageData<>(filteredDevices, + nonFilteredResult.getTotalPages(), + nonFilteredResult.getTotalElements(), + nonFilteredResult.hasNext()); + return checkNotNull(filteredResult); } @ApiOperation(value = "Count devices by device profile (countByDeviceProfileAndEmptyOtaPackage)", @@ -766,14 +731,10 @@ public class DeviceController extends BaseController { @PathVariable("deviceProfileId") String deviceProfileId) throws ThingsboardException { checkParameter("OtaPackageType", otaPackageType); checkParameter("DeviceProfileId", deviceProfileId); - try { - return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage( - getTenantId(), - new DeviceProfileId(UUID.fromString(deviceProfileId)), - OtaPackageType.valueOf(otaPackageType)); - } catch (Exception e) { - throw handleException(e); - } + return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage( + getTenantId(), + new DeviceProfileId(UUID.fromString(deviceProfileId)), + OtaPackageType.valueOf(otaPackageType)); } @ApiOperation(value = "Import the bulk of devices (processDevicesBulkImport)", 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 bd2a5a2ad9..20bf9c00ad 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -87,12 +87,8 @@ public class DeviceProfileController extends BaseController { @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException { checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId); - try { - DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); - return checkDeviceProfileId(deviceProfileId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); + return checkDeviceProfileId(deviceProfileId, Operation.READ); } @ApiOperation(value = "Get Device Profile Info (getDeviceProfileInfoById)", @@ -106,12 +102,8 @@ public class DeviceProfileController extends BaseController { @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException { checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId); - try { - DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); - return new DeviceProfileInfo(checkDeviceProfileId(deviceProfileId, Operation.READ)); - } catch (Exception e) { - throw handleException(e); - } + DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); + return new DeviceProfileInfo(checkDeviceProfileId(deviceProfileId, Operation.READ)); } @ApiOperation(value = "Get Default Device Profile (getDefaultDeviceProfileInfo)", @@ -122,11 +114,7 @@ public class DeviceProfileController extends BaseController { @RequestMapping(value = "/deviceProfileInfo/default", method = RequestMethod.GET) @ResponseBody public DeviceProfileInfo getDefaultDeviceProfileInfo() throws ThingsboardException { - try { - return checkNotNull(deviceProfileService.findDefaultDeviceProfileInfo(getTenantId())); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(deviceProfileService.findDefaultDeviceProfileInfo(getTenantId())); } @ApiOperation(value = "Get time-series keys (getTimeseriesKeys)", @@ -150,11 +138,7 @@ public class DeviceProfileController extends BaseController { deviceProfileId = null; } - try { - return timeseriesService.findAllKeysByDeviceProfileId(getTenantId(), deviceProfileId); - } catch (Exception e) { - throw handleException(e); - } + return timeseriesService.findAllKeysByDeviceProfileId(getTenantId(), deviceProfileId); } @ApiOperation(value = "Get attribute keys (getAttributesKeys)", @@ -178,11 +162,7 @@ public class DeviceProfileController extends BaseController { deviceProfileId = null; } - try { - return attributesService.findAllKeysByDeviceProfileId(getTenantId(), deviceProfileId); - } catch (Exception e) { - throw handleException(e); - } + return attributesService.findAllKeysByDeviceProfileId(getTenantId(), deviceProfileId); } @ApiOperation(value = "Create Or Update Device Profile (saveDeviceProfile)", @@ -256,12 +236,8 @@ public class DeviceProfileController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(deviceProfileService.findDeviceProfiles(getTenantId(), pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(deviceProfileService.findDeviceProfiles(getTenantId(), pageLink)); } @ApiOperation(value = "Get Device Profiles for transport type (getDeviceProfileInfos)", @@ -284,11 +260,7 @@ public class DeviceProfileController extends BaseController { @RequestParam(required = false) String sortOrder, @ApiParam(value = "Type of the transport", allowableValues = TRANSPORT_TYPE_ALLOWABLE_VALUES) @RequestParam(required = false) String transportType) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(deviceProfileService.findDeviceProfileInfos(getTenantId(), pageLink, transportType)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(deviceProfileService.findDeviceProfileInfos(getTenantId(), pageLink, transportType)); } } 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 6a5dd44d0b..8afe813b9f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -66,6 +66,7 @@ import org.thingsboard.server.service.security.permission.Resource; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION; @@ -116,12 +117,8 @@ public class EdgeController extends BaseController { public Edge getEdgeById(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); - try { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - return checkEdgeId(edgeId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + return checkEdgeId(edgeId, Operation.READ); } @ApiOperation(value = "Get Edge Info (getEdgeInfoById)", @@ -133,12 +130,8 @@ public class EdgeController extends BaseController { public EdgeInfo getEdgeInfoById(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); - try { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - return checkEdgeInfoId(edgeId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + return checkEdgeInfoId(edgeId, Operation.READ); } @ApiOperation(value = "Create Or Update Edge (saveEdge)", @@ -203,13 +196,9 @@ public class EdgeController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink)); } @ApiOperation(value = "Assign edge to customer (assignEdgeToCustomer)", @@ -285,16 +274,12 @@ public class EdgeController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(edgeService.findEdgesByTenantIdAndType(tenantId, type, pageLink)); - } else { - return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(edgeService.findEdgesByTenantIdAndType(tenantId, type, pageLink)); + } else { + return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink)); } } @@ -318,16 +303,12 @@ public class EdgeController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(edgeService.findEdgeInfosByTenantIdAndType(tenantId, type, pageLink)); - } else { - return checkNotNull(edgeService.findEdgeInfosByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(edgeService.findEdgeInfosByTenantIdAndType(tenantId, type, pageLink)); + } else { + return checkNotNull(edgeService.findEdgeInfosByTenantId(tenantId, pageLink)); } } @@ -340,12 +321,8 @@ public class EdgeController extends BaseController { @ResponseBody public Edge getTenantEdge(@ApiParam(value = "Unique name of the edge", required = true) @RequestParam String edgeName) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(edgeService.findEdgeByTenantIdAndName(tenantId, edgeName)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(edgeService.findEdgeByTenantIdAndName(tenantId, edgeName)); } @ApiOperation(value = "Set root rule chain for provided edge (setEdgeRootRuleChain)", @@ -391,22 +368,18 @@ public class EdgeController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - PageData result; - if (type != null && type.trim().length() > 0) { - result = edgeService.findEdgesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink); - } else { - result = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink); - } - return checkNotNull(result); - } catch (Exception e) { - throw handleException(e); + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + PageData result; + if (type != null && type.trim().length() > 0) { + result = edgeService.findEdgesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink); + } else { + result = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink); } + return checkNotNull(result); } @ApiOperation(value = "Get Customer Edge Infos (getCustomerEdgeInfos)", @@ -431,22 +404,18 @@ public class EdgeController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - PageData result; - if (type != null && type.trim().length() > 0) { - result = edgeService.findEdgeInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink); - } else { - result = edgeService.findEdgeInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink); - } - return checkNotNull(result); - } catch (Exception e) { - throw handleException(e); + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + PageData result; + if (type != null && type.trim().length() > 0) { + result = edgeService.findEdgeInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink); + } else { + result = edgeService.findEdgeInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink); } + return checkNotNull(result); } @ApiOperation(value = "Get Edges By Ids (getEdgesByIds)", @@ -457,27 +426,23 @@ public class EdgeController extends BaseController { @ResponseBody public List getEdgesByIds( @ApiParam(value = "A list of edges ids, separated by comma ','", required = true) - @RequestParam("edgeIds") String[] strEdgeIds) throws ThingsboardException { + @RequestParam("edgeIds") String[] strEdgeIds) throws ThingsboardException, ExecutionException, InterruptedException { checkArrayParameter("edgeIds", strEdgeIds); - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - CustomerId customerId = user.getCustomerId(); - List edgeIds = new ArrayList<>(); - for (String strEdgeId : strEdgeIds) { - edgeIds.add(new EdgeId(toUUID(strEdgeId))); - } - ListenableFuture> edgesFuture; - if (customerId == null || customerId.isNullUid()) { - edgesFuture = edgeService.findEdgesByTenantIdAndIdsAsync(tenantId, edgeIds); - } else { - edgesFuture = edgeService.findEdgesByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, edgeIds); - } - List edges = edgesFuture.get(); - return checkNotNull(edges); - } catch (Exception e) { - throw handleException(e); + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + CustomerId customerId = user.getCustomerId(); + List edgeIds = new ArrayList<>(); + for (String strEdgeId : strEdgeIds) { + edgeIds.add(new EdgeId(toUUID(strEdgeId))); } + ListenableFuture> edgesFuture; + if (customerId == null || customerId.isNullUid()) { + edgesFuture = edgeService.findEdgesByTenantIdAndIdsAsync(tenantId, edgeIds); + } else { + edgesFuture = edgeService.findEdgesByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, edgeIds); + } + List edges = edgesFuture.get(); + return checkNotNull(edges); } @ApiOperation(value = "Find related edges (findByQuery)", @@ -488,27 +453,23 @@ public class EdgeController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edges", method = RequestMethod.POST) @ResponseBody - public List findByQuery(@RequestBody EdgeSearchQuery query) throws ThingsboardException { + public List findByQuery(@RequestBody EdgeSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException { checkNotNull(query); checkNotNull(query.getParameters()); checkNotNull(query.getEdgeTypes()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - List edges = checkNotNull(edgeService.findEdgesByQuery(tenantId, query).get()); - edges = edges.stream().filter(edge -> { - try { - accessControlService.checkPermission(user, Resource.EDGE, Operation.READ, edge.getId(), edge); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - return edges; - } catch (Exception e) { - throw handleException(e); - } + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + List edges = checkNotNull(edgeService.findEdgesByQuery(tenantId, query).get()); + edges = edges.stream().filter(edge -> { + try { + accessControlService.checkPermission(user, Resource.EDGE, Operation.READ, edge.getId(), edge); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + return edges; } @ApiOperation(value = "Get Edge Types (getEdgeTypes)", @@ -518,15 +479,11 @@ public class EdgeController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edge/types", method = RequestMethod.GET) @ResponseBody - public List getEdgeTypes() throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - ListenableFuture> edgeTypes = edgeService.findEdgeTypesByTenantId(tenantId); - return checkNotNull(edgeTypes.get()); - } catch (Exception e) { - throw handleException(e); - } + public List getEdgeTypes() throws ThingsboardException, ExecutionException, InterruptedException { + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + ListenableFuture> edgeTypes = edgeService.findEdgeTypesByTenantId(tenantId); + return checkNotNull(edgeTypes.get()); } @ApiOperation(value = "Sync edge (syncEdge)", @@ -537,22 +494,18 @@ public class EdgeController extends BaseController { public DeferredResult syncEdge(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("edgeId") String strEdgeId) throws ThingsboardException { checkParameter("edgeId", strEdgeId); - try { - final DeferredResult response = new DeferredResult<>(); - if (isEdgesEnabled()) { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - edgeId = checkNotNull(edgeId); - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - ToEdgeSyncRequest request = new ToEdgeSyncRequest(UUID.randomUUID(), tenantId, edgeId); - edgeRpcService.processSyncRequest(request, fromEdgeSyncResponse -> reply(response, fromEdgeSyncResponse)); - } else { - throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL); - } - return response; - } catch (Exception e) { - throw handleException(e); + final DeferredResult response = new DeferredResult<>(); + if (isEdgesEnabled()) { + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + edgeId = checkNotNull(edgeId); + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + ToEdgeSyncRequest request = new ToEdgeSyncRequest(UUID.randomUUID(), tenantId, edgeId); + edgeRpcService.processSyncRequest(request, fromEdgeSyncResponse -> reply(response, fromEdgeSyncResponse)); + } else { + throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL); } + return response; } private void reply(DeferredResult response, FromEdgeSyncResponse fromEdgeSyncResponse) { @@ -570,15 +523,11 @@ public class EdgeController extends BaseController { @ResponseBody public String findMissingToRelatedRuleChains(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("edgeId") String strEdgeId) throws ThingsboardException { - try { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - edgeId = checkNotNull(edgeId); - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - return edgeService.findMissingToRelatedRuleChains(tenantId, edgeId, TbRuleChainInputNode.class.getName()); - } catch (Exception e) { - throw handleException(e); - } + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + edgeId = checkNotNull(edgeId); + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + return edgeService.findMissingToRelatedRuleChains(tenantId, edgeId, TbRuleChainInputNode.class.getName()); } @ApiOperation(value = "Import the bulk of edges (processEdgesBulkImport)", diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java index 5dd43736a8..6281336185 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java @@ -81,14 +81,10 @@ public class EdgeEventController extends BaseController { @ApiParam(value = "Timestamp. Edge events with creation time after it won't be queried") @RequestParam(required = false) Long endTime) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - checkEdgeId(edgeId, Operation.READ); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - return checkNotNull(edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, false)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + checkEdgeId(edgeId, Operation.READ); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + return checkNotNull(edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, false)); } } diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java index bc6c628bff..9b7e5c1ba4 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java @@ -61,11 +61,7 @@ public class EntityQueryController extends BaseController { @ApiParam(value = "A JSON value representing the entity count query. See API call notes above for more details.") @RequestBody EntityCountQuery query) throws ThingsboardException { checkNotNull(query); - try { - return this.entityQueryService.countEntitiesByQuery(getCurrentUser(), query); - } catch (Exception e) { - throw handleException(e); - } + return this.entityQueryService.countEntitiesByQuery(getCurrentUser(), query); } @ApiOperation(value = "Find Entity Data by Query", notes = ENTITY_DATA_QUERY_DESCRIPTION) @@ -76,11 +72,7 @@ public class EntityQueryController extends BaseController { @ApiParam(value = "A JSON value representing the entity data query. See API call notes above for more details.") @RequestBody EntityDataQuery query) throws ThingsboardException { checkNotNull(query); - try { - return this.entityQueryService.findEntityDataByQuery(getCurrentUser(), query); - } catch (Exception e) { - throw handleException(e); - } + return this.entityQueryService.findEntityDataByQuery(getCurrentUser(), query); } @ApiOperation(value = "Find Alarms by Query", notes = ALARM_DATA_QUERY_DESCRIPTION) @@ -91,11 +83,7 @@ public class EntityQueryController extends BaseController { @ApiParam(value = "A JSON value representing the alarm data query. See API call notes above for more details.") @RequestBody AlarmDataQuery query) throws ThingsboardException { checkNotNull(query); - try { - return this.entityQueryService.findAlarmDataByQuery(getCurrentUser(), query); - } catch (Exception e) { - throw handleException(e); - } + return this.entityQueryService.findAlarmDataByQuery(getCurrentUser(), query); } @ApiOperation(value = "Find Entity Keys by Query", @@ -112,15 +100,11 @@ public class EntityQueryController extends BaseController { @RequestParam("attributes") boolean isAttributes) throws ThingsboardException { TenantId tenantId = getTenantId(); checkNotNull(query); - try { - EntityDataPageLink pageLink = query.getPageLink(); - if (pageLink.getPageSize() > MAX_PAGE_SIZE) { - pageLink.setPageSize(MAX_PAGE_SIZE); - } - return entityQueryService.getKeysByQuery(getCurrentUser(), tenantId, query, isTimeseries, isAttributes); - } catch (Exception e) { - throw handleException(e); + EntityDataPageLink pageLink = query.getPageLink(); + if (pageLink.getPageSize() > MAX_PAGE_SIZE) { + pageLink.setPageSize(MAX_PAGE_SIZE); } + return entityQueryService.getKeysByQuery(getCurrentUser(), tenantId, query, isTimeseries, isAttributes); } } 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 efc4237b3f..46c68d0be8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -41,6 +41,7 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.thingsboard.server.controller.ControllerConstants.ENTITY_ID_PARAM_DESCRIPTION; @@ -143,21 +144,17 @@ public class EntityRelationController extends BaseController { @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId, @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType) throws ThingsboardException { - try { - checkParameter(FROM_ID, strFromId); - checkParameter(FROM_TYPE, strFromType); - checkParameter(RELATION_TYPE, strRelationType); - checkParameter(TO_ID, strToId); - checkParameter(TO_TYPE, strToType); - EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); - EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId); - checkEntityId(fromId, Operation.READ); - checkEntityId(toId, Operation.READ); - RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); - return checkNotNull(relationService.getRelation(getTenantId(), fromId, toId, strRelationType, typeGroup)); - } catch (Exception e) { - throw handleException(e); - } + checkParameter(FROM_ID, strFromId); + checkParameter(FROM_TYPE, strFromType); + checkParameter(RELATION_TYPE, strRelationType); + checkParameter(TO_ID, strToId); + checkParameter(TO_TYPE, strToType); + EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); + EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId); + checkEntityId(fromId, Operation.READ); + checkEntityId(toId, Operation.READ); + RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); + return checkNotNull(relationService.getRelation(getTenantId(), fromId, toId, strRelationType, typeGroup)); } @ApiOperation(value = "Get List of Relations (findByFrom)", @@ -176,11 +173,7 @@ public class EntityRelationController extends BaseController { EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); checkEntityId(entityId, Operation.READ); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findByFrom(getTenantId(), entityId, typeGroup))); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findByFrom(getTenantId(), entityId, typeGroup))); } @ApiOperation(value = "Get List of Relation Infos (findInfoByFrom)", @@ -193,17 +186,13 @@ public class EntityRelationController extends BaseController { public List findInfoByFrom(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId, @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType, @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) - @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { + @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException, ExecutionException, InterruptedException { checkParameter(FROM_ID, strFromId); checkParameter(FROM_TYPE, strFromType); EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); checkEntityId(entityId, Operation.READ); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByFrom(getTenantId(), entityId, typeGroup).get())); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByFrom(getTenantId(), entityId, typeGroup).get())); } @ApiOperation(value = "Get List of Relations (findByFrom)", @@ -224,11 +213,7 @@ public class EntityRelationController extends BaseController { EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); checkEntityId(entityId, Operation.READ); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findByFromAndType(getTenantId(), entityId, strRelationType, typeGroup))); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findByFromAndType(getTenantId(), entityId, strRelationType, typeGroup))); } @ApiOperation(value = "Get List of Relations (findByTo)", @@ -247,11 +232,7 @@ public class EntityRelationController extends BaseController { EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId); checkEntityId(entityId, Operation.READ); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findByTo(getTenantId(), entityId, typeGroup))); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findByTo(getTenantId(), entityId, typeGroup))); } @ApiOperation(value = "Get List of Relation Infos (findInfoByTo)", @@ -264,17 +245,13 @@ public class EntityRelationController extends BaseController { public List findInfoByTo(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId, @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType, @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) - @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { + @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException, ExecutionException, InterruptedException { checkParameter(TO_ID, strToId); checkParameter(TO_TYPE, strToType); EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId); checkEntityId(entityId, Operation.READ); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByTo(getTenantId(), entityId, typeGroup).get())); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByTo(getTenantId(), entityId, typeGroup).get())); } @ApiOperation(value = "Get List of Relations (findByTo)", @@ -295,11 +272,7 @@ public class EntityRelationController extends BaseController { EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId); checkEntityId(entityId, Operation.READ); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findByToAndType(getTenantId(), entityId, strRelationType, typeGroup))); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findByToAndType(getTenantId(), entityId, strRelationType, typeGroup))); } @ApiOperation(value = "Find related entities (findByQuery)", @@ -310,16 +283,12 @@ public class EntityRelationController extends BaseController { @RequestMapping(value = "/relations", method = RequestMethod.POST) @ResponseBody public List findByQuery(@ApiParam(value = "A JSON value representing the entity relations query object.", required = true) - @RequestBody EntityRelationsQuery query) throws ThingsboardException { + @RequestBody EntityRelationsQuery query) throws ThingsboardException, ExecutionException, InterruptedException { checkNotNull(query); checkNotNull(query.getParameters()); checkNotNull(query.getFilters()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findByQuery(getTenantId(), query).get())); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findByQuery(getTenantId(), query).get())); } @ApiOperation(value = "Find related entity infos (findInfoByQuery)", @@ -330,16 +299,12 @@ public class EntityRelationController extends BaseController { @RequestMapping(value = "/relations/info", method = RequestMethod.POST) @ResponseBody public List findInfoByQuery(@ApiParam(value = "A JSON value representing the entity relations query object.", required = true) - @RequestBody EntityRelationsQuery query) throws ThingsboardException { + @RequestBody EntityRelationsQuery query) throws ThingsboardException, ExecutionException, InterruptedException { checkNotNull(query); checkNotNull(query.getParameters()); checkNotNull(query.getFilters()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); - try { - return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByQuery(getTenantId(), query).get())); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByQuery(getTenantId(), query).get())); } private void checkCanCreateRelation(EntityId entityId) throws ThingsboardException { 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 0f299fefae..9ab8ab80a9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -54,6 +54,7 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID; @@ -105,11 +106,7 @@ public class EntityViewController extends BaseController { @ApiParam(value = ENTITY_VIEW_ID_PARAM_DESCRIPTION) @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { checkParameter(ENTITY_VIEW_ID, strEntityViewId); - try { - return checkEntityViewId(new EntityViewId(toUUID(strEntityViewId)), Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + return checkEntityViewId(new EntityViewId(toUUID(strEntityViewId)), Operation.READ); } @ApiOperation(value = "Get Entity View info (getEntityViewInfoById)", @@ -123,12 +120,8 @@ public class EntityViewController extends BaseController { @ApiParam(value = ENTITY_VIEW_ID_PARAM_DESCRIPTION) @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { checkParameter(ENTITY_VIEW_ID, strEntityViewId); - try { - EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); - return checkEntityViewInfoId(entityViewId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); + return checkEntityViewInfoId(entityViewId, Operation.READ); } @ApiOperation(value = "Save or update entity view (saveEntityView)", @@ -177,12 +170,8 @@ public class EntityViewController extends BaseController { public EntityView getTenantEntityView( @ApiParam(value = "Entity View name") @RequestParam String entityViewName) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(entityViewService.findEntityViewByTenantIdAndName(tenantId, entityViewName)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(entityViewService.findEntityViewByTenantIdAndName(tenantId, entityViewName)); } @ApiOperation(value = "Assign Entity View to customer (assignEntityViewToCustomer)", @@ -249,18 +238,14 @@ public class EntityViewController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter(CUSTOMER_ID, strCustomerId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId, customerId, pageLink, type)); - } else { - return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId, customerId, pageLink, type)); + } else { + return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); } } @@ -286,18 +271,14 @@ public class EntityViewController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); - } else { - return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); + } else { + return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink)); } } @@ -320,17 +301,13 @@ public class EntityViewController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - - if (type != null && type.trim().length() > 0) { - return checkNotNull(entityViewService.findEntityViewByTenantIdAndType(tenantId, pageLink, type)); - } else { - return checkNotNull(entityViewService.findEntityViewByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + + if (type != null && type.trim().length() > 0) { + return checkNotNull(entityViewService.findEntityViewByTenantIdAndType(tenantId, pageLink, type)); + } else { + return checkNotNull(entityViewService.findEntityViewByTenantId(tenantId, pageLink)); } } @@ -353,16 +330,12 @@ public class EntityViewController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (type != null && type.trim().length() > 0) { - return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndType(tenantId, type, pageLink)); - } else { - return checkNotNull(entityViewService.findEntityViewInfosByTenantId(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (type != null && type.trim().length() > 0) { + return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndType(tenantId, type, pageLink)); + } else { + return checkNotNull(entityViewService.findEntityViewInfosByTenantId(tenantId, pageLink)); } } @@ -375,25 +348,21 @@ public class EntityViewController extends BaseController { @ResponseBody public List findByQuery( @ApiParam(value = "The entity view search query JSON") - @RequestBody EntityViewSearchQuery query) throws ThingsboardException { + @RequestBody EntityViewSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException { checkNotNull(query); checkNotNull(query.getParameters()); checkNotNull(query.getEntityViewTypes()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); - try { - List entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(getTenantId(), query).get()); - entityViews = entityViews.stream().filter(entityView -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - return entityViews; - } catch (Exception e) { - throw handleException(e); - } + List entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(getTenantId(), query).get()); + entityViews = entityViews.stream().filter(entityView -> { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + return entityViews; } @ApiOperation(value = "Get Entity View Types (getEntityViewTypes)", @@ -402,15 +371,11 @@ public class EntityViewController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/entityView/types", method = RequestMethod.GET) @ResponseBody - public List getEntityViewTypes() throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); - TenantId tenantId = user.getTenantId(); - ListenableFuture> entityViewTypes = entityViewService.findEntityViewTypesByTenantId(tenantId); - return checkNotNull(entityViewTypes.get()); - } catch (Exception e) { - throw handleException(e); - } + public List getEntityViewTypes() throws ThingsboardException, ExecutionException, InterruptedException { + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + ListenableFuture> entityViewTypes = entityViewService.findEntityViewTypesByTenantId(tenantId); + return checkNotNull(entityViewTypes.get()); } @ApiOperation(value = "Make entity view publicly available (assignEntityViewToPublicCustomer)", @@ -497,32 +462,28 @@ public class EntityViewController extends BaseController { @RequestParam(required = false) Long startTime, @RequestParam(required = false) Long endTime) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - checkEdgeId(edgeId, Operation.READ); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - PageData nonFilteredResult; - if (type != null && type.trim().length() > 0) { - nonFilteredResult = entityViewService.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink); - } else { - nonFilteredResult = entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); - } - List filteredEntityViews = nonFilteredResult.getData().stream().filter(entityView -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); - PageData filteredResult = new PageData<>(filteredEntityViews, - nonFilteredResult.getTotalPages(), - nonFilteredResult.getTotalElements(), - nonFilteredResult.hasNext()); - return checkNotNull(filteredResult); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + checkEdgeId(edgeId, Operation.READ); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + PageData nonFilteredResult; + if (type != null && type.trim().length() > 0) { + nonFilteredResult = entityViewService.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink); + } else { + nonFilteredResult = entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); } + List filteredEntityViews = nonFilteredResult.getData().stream().filter(entityView -> { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView); + return true; + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + PageData filteredResult = new PageData<>(filteredEntityViews, + nonFilteredResult.getTotalPages(), + nonFilteredResult.getTotalElements(), + nonFilteredResult.hasNext()); + return checkNotNull(filteredResult); } } diff --git a/application/src/main/java/org/thingsboard/server/controller/EventController.java b/application/src/main/java/org/thingsboard/server/controller/EventController.java index db040599fa..42e2af5fed 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EventController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EventController.java @@ -248,14 +248,10 @@ public class EventController extends BaseController { @RequestBody EventFilter eventFilter) throws ThingsboardException { checkParameter("EntityId", strEntityId); checkParameter("EntityType", strEntityType); - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); - checkEntityId(entityId, Operation.WRITE); + EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); + checkEntityId(entityId, Operation.WRITE); - eventService.removeEvents(getTenantId(), entityId, eventFilter, startTime, endTime); - } catch (Exception e) { - throw handleException(e); - } + eventService.removeEvents(getTenantId(), entityId, eventFilter, startTime, endTime); } private static EventType resolveEventType(String eventType) throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java index 8a47ed2c15..883bdb806d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java +++ b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java @@ -64,11 +64,7 @@ public class Lwm2mController extends BaseController { public LwM2MServerSecurityConfigDefault getLwm2mBootstrapSecurityInfo( @ApiParam(value = IS_BOOTSTRAP_SERVER_PARAM_DESCRIPTION) @PathVariable(IS_BOOTSTRAP_SERVER) boolean bootstrapServer) throws ThingsboardException { - try { - return lwM2MService.getServerSecurityInfo(bootstrapServer); - } catch (Exception e) { - throw handleException(e); - } + return lwM2MService.getServerSecurityInfo(bootstrapServer); } @ApiOperation(hidden = true, value = "Save device with credentials (Deprecated)") diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java index f08f8f380a..599b519cd9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java @@ -54,12 +54,8 @@ public class OAuth2ConfigTemplateController extends BaseController { @RequestMapping(method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) public OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(@RequestBody OAuth2ClientRegistrationTemplate clientRegistrationTemplate) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.WRITE); - return oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.WRITE); + return oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); } @ApiOperation(value = "Delete OAuth2 client registration template by id (deleteClientRegistrationTemplate)" + SYSTEM_AUTHORITY_PARAGRAPH, @@ -70,13 +66,9 @@ public class OAuth2ConfigTemplateController extends BaseController { public void deleteClientRegistrationTemplate(@ApiParam(value = "String representation of client registration template id to delete", example = "139b1f81-2f5d-11ec-9dbe-9b627e1a88f4") @PathVariable(CLIENT_REGISTRATION_TEMPLATE_ID) String strClientRegistrationTemplateId) throws ThingsboardException { checkParameter(CLIENT_REGISTRATION_TEMPLATE_ID, strClientRegistrationTemplateId); - try { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.DELETE); - OAuth2ClientRegistrationTemplateId clientRegistrationTemplateId = new OAuth2ClientRegistrationTemplateId(toUUID(strClientRegistrationTemplateId)); - oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(clientRegistrationTemplateId); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.DELETE); + OAuth2ClientRegistrationTemplateId clientRegistrationTemplateId = new OAuth2ClientRegistrationTemplateId(toUUID(strClientRegistrationTemplateId)); + oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(clientRegistrationTemplateId); } @ApiOperation(value = "Get the list of all OAuth2 client registration templates (getClientRegistrationTemplates)" + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, @@ -85,12 +77,8 @@ public class OAuth2ConfigTemplateController extends BaseController { @RequestMapping(method = RequestMethod.GET, produces = "application/json") @ResponseBody public List getClientRegistrationTemplates() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.READ); - return oAuth2ConfigTemplateService.findAllClientRegistrationTemplates(); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.READ); + return oAuth2ConfigTemplateService.findAllClientRegistrationTemplates(); } } 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 5904fab60d..9edf74c96b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -69,25 +69,21 @@ public class OAuth2Controller extends BaseController { "If platform type is not one of allowable values - it will just be ignored", allowableValues = "WEB, ANDROID, IOS") @RequestParam(required = false) String platform) throws ThingsboardException { - try { - if (log.isDebugEnabled()) { - log.debug("Executing getOAuth2Clients: [{}][{}][{}]", request.getScheme(), request.getServerName(), request.getServerPort()); - Enumeration headerNames = request.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String header = headerNames.nextElement(); - log.debug("Header: {} {}", header, request.getHeader(header)); - } + if (log.isDebugEnabled()) { + log.debug("Executing getOAuth2Clients: [{}][{}][{}]", request.getScheme(), request.getServerName(), request.getServerPort()); + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String header = headerNames.nextElement(); + log.debug("Header: {} {}", header, request.getHeader(header)); } - PlatformType platformType = null; - if (StringUtils.isNotEmpty(platform)) { - try { - platformType = PlatformType.valueOf(platform); - } catch (Exception e) {} - } - return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request), pkgName, platformType); - } catch (Exception e) { - throw handleException(e); } + PlatformType platformType = null; + if (StringUtils.isNotEmpty(platform)) { + try { + platformType = PlatformType.valueOf(platform); + } catch (Exception e) {} + } + return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request), pkgName, platformType); } @ApiOperation(value = "Get current OAuth2 settings (getCurrentOAuth2Info)", notes = SYSTEM_AUTHORITY_PARAGRAPH) @@ -95,12 +91,8 @@ public class OAuth2Controller extends BaseController { @RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json") @ResponseBody public OAuth2Info getCurrentOAuth2Info() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); - return oAuth2Service.findOAuth2Info(); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); + return oAuth2Service.findOAuth2Info(); } @ApiOperation(value = "Save OAuth2 settings (saveOAuth2Info)", notes = SYSTEM_AUTHORITY_PARAGRAPH) @@ -108,13 +100,9 @@ public class OAuth2Controller extends BaseController { @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) public OAuth2Info saveOAuth2Info(@RequestBody OAuth2Info oauth2Info) throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.WRITE); - oAuth2Service.saveOAuth2Info(oauth2Info); - return oAuth2Service.findOAuth2Info(); - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.WRITE); + oAuth2Service.saveOAuth2Info(oauth2Info); + return oAuth2Service.findOAuth2Info(); } @ApiOperation(value = "Get OAuth2 log in processing URL (getLoginProcessingUrl)", notes = "Returns the URL enclosed in " + @@ -125,12 +113,8 @@ public class OAuth2Controller extends BaseController { @RequestMapping(value = "/oauth2/loginProcessingUrl", method = RequestMethod.GET) @ResponseBody public String getLoginProcessingUrl() throws ThingsboardException { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); - return "\"" + oAuth2Configuration.getLoginProcessingUrl() + "\""; - } catch (Exception e) { - throw handleException(e); - } + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); + return "\"" + oAuth2Configuration.getLoginProcessingUrl() + "\""; } } diff --git a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java index 0040187f02..a673507c81 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java @@ -87,24 +87,20 @@ public class OtaPackageController extends BaseController { public ResponseEntity downloadOtaPackage(@ApiParam(value = OTA_PACKAGE_ID_PARAM_DESCRIPTION) @PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException { checkParameter(OTA_PACKAGE_ID, strOtaPackageId); - try { - OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); - OtaPackage otaPackage = checkOtaPackageId(otaPackageId, Operation.READ); - - if (otaPackage.hasUrl()) { - return ResponseEntity.badRequest().build(); - } + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + OtaPackage otaPackage = checkOtaPackageId(otaPackageId, Operation.READ); - ByteArrayResource resource = new ByteArrayResource(otaPackage.getData().array()); - return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackage.getFileName()) - .header("x-filename", otaPackage.getFileName()) - .contentLength(resource.contentLength()) - .contentType(parseMediaType(otaPackage.getContentType())) - .body(resource); - } catch (Exception e) { - throw handleException(e); + if (otaPackage.hasUrl()) { + return ResponseEntity.badRequest().build(); } + + ByteArrayResource resource = new ByteArrayResource(otaPackage.getData().array()); + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackage.getFileName()) + .header("x-filename", otaPackage.getFileName()) + .contentLength(resource.contentLength()) + .contentType(parseMediaType(otaPackage.getContentType())) + .body(resource); } @ApiOperation(value = "Get OTA Package Info (getOtaPackageInfoById)", @@ -117,12 +113,8 @@ public class OtaPackageController extends BaseController { public OtaPackageInfo getOtaPackageInfoById(@ApiParam(value = OTA_PACKAGE_ID_PARAM_DESCRIPTION) @PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException { checkParameter(OTA_PACKAGE_ID, strOtaPackageId); - try { - OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); - return checkNotNull(otaPackageService.findOtaPackageInfoById(getTenantId(), otaPackageId)); - } catch (Exception e) { - throw handleException(e); - } + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + return checkNotNull(otaPackageService.findOtaPackageInfoById(getTenantId(), otaPackageId)); } @ApiOperation(value = "Get OTA Package (getOtaPackageById)", @@ -135,12 +127,8 @@ public class OtaPackageController extends BaseController { public OtaPackage getOtaPackageById(@ApiParam(value = OTA_PACKAGE_ID_PARAM_DESCRIPTION) @PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException { checkParameter(OTA_PACKAGE_ID, strOtaPackageId); - try { - OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); - return checkOtaPackageId(otaPackageId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + return checkOtaPackageId(otaPackageId, Operation.READ); } @ApiOperation(value = "Create Or Update OTA Package Info (saveOtaPackageInfo)", @@ -204,12 +192,8 @@ public class OtaPackageController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantId(getTenantId(), pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantId(getTenantId(), pageLink)); } @ApiOperation(value = "Get OTA Package Infos (getOtaPackages)", @@ -235,13 +219,9 @@ public class OtaPackageController extends BaseController { @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("deviceProfileId", strDeviceProfileId); checkParameter("type", strType); - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(), - new DeviceProfileId(toUUID(strDeviceProfileId)), OtaPackageType.valueOf(strType), pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(), + new DeviceProfileId(toUUID(strDeviceProfileId)), OtaPackageType.valueOf(strType), pageLink)); } @ApiOperation(value = "Delete OTA Package (deleteOtaPackage)", diff --git a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java index 300d522dbe..2394270118 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java @@ -159,12 +159,8 @@ public class RpcV2Controller extends AbstractRpcController { @ApiParam(value = RPC_ID_PARAM_DESCRIPTION, required = true) @PathVariable(RPC_ID) String strRpc) throws ThingsboardException { checkParameter("RpcId", strRpc); - try { - RpcId rpcId = new RpcId(UUID.fromString(strRpc)); - return checkRpcId(rpcId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + RpcId rpcId = new RpcId(UUID.fromString(strRpc)); + return checkRpcId(rpcId, Operation.READ); } @ApiOperation(value = "Get persistent RPC requests", notes = "Allows to query RPC calls for specific device using pagination." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @@ -187,43 +183,39 @@ public class RpcV2Controller extends AbstractRpcController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("DeviceId", strDeviceId); - try { - if (rpcStatus != null && rpcStatus.equals(RpcStatus.DELETED)) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "RpcStatus: DELETED"); - } - - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - DeviceId deviceId = new DeviceId(UUID.fromString(strDeviceId)); - final DeferredResult response = new DeferredResult<>(); + if (rpcStatus != null && rpcStatus.equals(RpcStatus.DELETED)) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "RpcStatus: DELETED"); + } - accessValidator.validate(getCurrentUser(), Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<>() { - @Override - public void onSuccess(@Nullable DeferredResult result) { - PageData rpcCalls; - if (rpcStatus != null) { - rpcCalls = rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink); - } else { - rpcCalls = rpcService.findAllByDeviceId(tenantId, deviceId, pageLink); - } - response.setResult(new ResponseEntity<>(rpcCalls, HttpStatus.OK)); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + DeviceId deviceId = new DeviceId(UUID.fromString(strDeviceId)); + final DeferredResult response = new DeferredResult<>(); + + accessValidator.validate(getCurrentUser(), Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<>() { + @Override + public void onSuccess(@Nullable DeferredResult result) { + PageData rpcCalls; + if (rpcStatus != null) { + rpcCalls = rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink); + } else { + rpcCalls = rpcService.findAllByDeviceId(tenantId, deviceId, pageLink); } + response.setResult(new ResponseEntity<>(rpcCalls, HttpStatus.OK)); + } - @Override - public void onFailure(Throwable e) { - ResponseEntity entity; - if (e instanceof ToErrorResponseEntity) { - entity = ((ToErrorResponseEntity) e).toErrorResponseEntity(); - } else { - entity = new ResponseEntity(HttpStatus.UNAUTHORIZED); - } - response.setResult(entity); + @Override + public void onFailure(Throwable e) { + ResponseEntity entity; + if (e instanceof ToErrorResponseEntity) { + entity = ((ToErrorResponseEntity) e).toErrorResponseEntity(); + } else { + entity = new ResponseEntity(HttpStatus.UNAUTHORIZED); } - })); - return response; - } catch (Exception e) { - throw handleException(e); - } + response.setResult(entity); + } + })); + return response; } @ApiOperation(value = "Delete persistent RPC", notes = "Deletes the persistent RPC request." + TENANT_AUTHORITY_PARAGRAPH) @@ -234,25 +226,21 @@ public class RpcV2Controller extends AbstractRpcController { @ApiParam(value = RPC_ID_PARAM_DESCRIPTION, required = true) @PathVariable(RPC_ID) String strRpc) throws ThingsboardException { checkParameter("RpcId", strRpc); - try { - RpcId rpcId = new RpcId(UUID.fromString(strRpc)); - Rpc rpc = checkRpcId(rpcId, Operation.DELETE); - - if (rpc != null) { - if (rpc.getStatus().equals(RpcStatus.QUEUED)) { - RemoveRpcActorMsg removeMsg = new RemoveRpcActorMsg(getTenantId(), rpc.getDeviceId(), rpc.getUuidId()); - log.trace("[{}] Forwarding msg {} to queue actor!", rpc.getDeviceId(), rpc); - tbClusterService.pushMsgToCore(removeMsg, null); - } + RpcId rpcId = new RpcId(UUID.fromString(strRpc)); + Rpc rpc = checkRpcId(rpcId, Operation.DELETE); + + if (rpc != null) { + if (rpc.getStatus().equals(RpcStatus.QUEUED)) { + RemoveRpcActorMsg removeMsg = new RemoveRpcActorMsg(getTenantId(), rpc.getDeviceId(), rpc.getUuidId()); + log.trace("[{}] Forwarding msg {} to queue actor!", rpc.getDeviceId(), rpc); + tbClusterService.pushMsgToCore(removeMsg, null); + } - rpcService.deleteRpc(getTenantId(), rpcId); - rpc.setStatus(RpcStatus.DELETED); + rpcService.deleteRpc(getTenantId(), rpcId); + rpc.setStatus(RpcStatus.DELETED); - TbMsg msg = TbMsg.newMsg(RPC_DELETED, rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc)); - tbClusterService.pushMsgToRuleEngine(getTenantId(), rpc.getDeviceId(), msg, null); - } - } catch (Exception e) { - throw handleException(e); + TbMsg msg = TbMsg.newMsg(RPC_DELETED, rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc)); + tbClusterService.pushMsgToRuleEngine(getTenantId(), rpc.getDeviceId(), msg, null); } } } 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 85b8e21095..5ae8426966 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -166,12 +167,8 @@ public class RuleChainController extends BaseController { @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION) @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); - try { - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - return checkRuleChain(ruleChainId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + return checkRuleChain(ruleChainId, Operation.READ); } @ApiOperation(value = "Get Rule Chain output labels (getRuleChainOutputLabels)", @@ -184,13 +181,9 @@ public class RuleChainController extends BaseController { @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION) @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); - try { - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - checkRuleChain(ruleChainId, Operation.READ); - return tbRuleChainService.getRuleChainOutputLabels(getTenantId(), ruleChainId); - } catch (Exception e) { - throw handleException(e); - } + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + checkRuleChain(ruleChainId, Operation.READ); + return tbRuleChainService.getRuleChainOutputLabels(getTenantId(), ruleChainId); } @ApiOperation(value = "Get output labels usage (getRuleChainOutputLabelsUsage)", @@ -203,13 +196,9 @@ public class RuleChainController extends BaseController { @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION) @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); - try { - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - checkRuleChain(ruleChainId, Operation.READ); - return tbRuleChainService.getOutputLabelUsage(getCurrentUser().getTenantId(), ruleChainId); - } catch (Exception e) { - throw handleException(e); - } + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + checkRuleChain(ruleChainId, Operation.READ); + return tbRuleChainService.getOutputLabelUsage(getCurrentUser().getTenantId(), ruleChainId); } @ApiOperation(value = "Get Rule Chain (getRuleChainById)", @@ -221,13 +210,9 @@ public class RuleChainController extends BaseController { @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION) @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); - try { - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - checkRuleChain(ruleChainId, Operation.READ); - return ruleChainService.loadRuleChainMetaData(getTenantId(), ruleChainId); - } catch (Exception e) { - throw handleException(e); - } + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + checkRuleChain(ruleChainId, Operation.READ); + return ruleChainService.loadRuleChainMetaData(getTenantId(), ruleChainId); } @ApiOperation(value = "Create Or Update Rule Chain (saveRuleChain)", @@ -319,17 +304,13 @@ public class RuleChainController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - RuleChainType type = RuleChainType.CORE; - if (typeStr != null && typeStr.trim().length() > 0) { - type = RuleChainType.valueOf(typeStr); - } - return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, type, pageLink)); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + RuleChainType type = RuleChainType.CORE; + if (typeStr != null && typeStr.trim().length() > 0) { + type = RuleChainType.valueOf(typeStr); } + return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, type, pageLink)); } @ApiOperation(value = "Delete rule chain (deleteRuleChain)", @@ -357,25 +338,21 @@ public class RuleChainController extends BaseController { @ApiParam(value = RULE_NODE_ID_PARAM_DESCRIPTION) @PathVariable(RULE_NODE_ID) String strRuleNodeId) throws ThingsboardException { checkParameter(RULE_NODE_ID, strRuleNodeId); - try { - RuleNodeId ruleNodeId = new RuleNodeId(toUUID(strRuleNodeId)); - checkRuleNode(ruleNodeId, Operation.READ); - TenantId tenantId = getCurrentUser().getTenantId(); - List events = eventService.findLatestEvents(tenantId, ruleNodeId, EventType.DEBUG_RULE_NODE, 2); - JsonNode result = null; - if (events != null) { - for (EventInfo event : events) { - JsonNode body = event.getBody(); - if (body.has("type") && body.get("type").asText().equals("IN")) { - result = body; - break; - } + RuleNodeId ruleNodeId = new RuleNodeId(toUUID(strRuleNodeId)); + checkRuleNode(ruleNodeId, Operation.READ); + TenantId tenantId = getCurrentUser().getTenantId(); + List events = eventService.findLatestEvents(tenantId, ruleNodeId, EventType.DEBUG_RULE_NODE, 2); + JsonNode result = null; + if (events != null) { + for (EventInfo event : events) { + JsonNode body = event.getBody(); + if (body.has("type") && body.get("type").asText().equals("IN")) { + result = body; + break; } } - return result; - } catch (Exception e) { - throw handleException(e); } + return result; } @ApiOperation(value = "Is MVEL script executor enabled", @@ -396,74 +373,70 @@ public class RuleChainController extends BaseController { @ApiParam(value = "Script language: JS or MVEL") @RequestParam(required = false) ScriptLanguage scriptLang, @ApiParam(value = "Test JS request. See API call description above.") - @RequestBody JsonNode inputParams) throws ThingsboardException { + @RequestBody JsonNode inputParams) throws ThingsboardException, JsonProcessingException { + String script = inputParams.get("script").asText(); + String scriptType = inputParams.get("scriptType").asText(); + JsonNode argNamesJson = inputParams.get("argNames"); + String[] argNames = objectMapper.treeToValue(argNamesJson, String[].class); + + String data = inputParams.get("msg").asText(); + JsonNode metadataJson = inputParams.get("metadata"); + Map metadata = objectMapper.convertValue(metadataJson, new TypeReference>() { + }); + String msgType = inputParams.get("msgType").asText(); + String output = ""; + String errorText = ""; + ScriptEngine engine = null; try { - String script = inputParams.get("script").asText(); - String scriptType = inputParams.get("scriptType").asText(); - JsonNode argNamesJson = inputParams.get("argNames"); - String[] argNames = objectMapper.treeToValue(argNamesJson, String[].class); - - String data = inputParams.get("msg").asText(); - JsonNode metadataJson = inputParams.get("metadata"); - Map metadata = objectMapper.convertValue(metadataJson, new TypeReference>() { - }); - String msgType = inputParams.get("msgType").asText(); - String output = ""; - String errorText = ""; - ScriptEngine engine = null; - try { - if (scriptLang == null) { - scriptLang = ScriptLanguage.JS; - } - if (ScriptLanguage.JS.equals(scriptLang)) { - engine = new RuleNodeJsScriptEngine(getTenantId(), jsInvokeService, script, argNames); - } else { - if (mvelInvokeService == null) { - throw new IllegalArgumentException("MVEL script engine is disabled!"); - } - engine = new RuleNodeMvelScriptEngine(getTenantId(), mvelInvokeService, script, argNames); - } - TbMsg inMsg = TbMsg.newMsg(msgType, null, new TbMsgMetaData(metadata), TbMsgDataType.JSON, data); - switch (scriptType) { - case "update": - output = msgToOutput(engine.executeUpdateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS)); - break; - case "generate": - output = msgToOutput(engine.executeGenerateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS)); - break; - case "filter": - boolean result = engine.executeFilterAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); - output = Boolean.toString(result); - break; - case "switch": - Set states = engine.executeSwitchAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); - output = objectMapper.writeValueAsString(states); - break; - case "json": - JsonNode json = engine.executeJsonAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); - output = objectMapper.writeValueAsString(json); - break; - case "string": - output = engine.executeToStringAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); - break; - default: - throw new IllegalArgumentException("Unsupported script type: " + scriptType); - } - } catch (Exception e) { - log.error("Error evaluating JS function", e); - errorText = e.getMessage(); - } finally { - if (engine != null) { - engine.destroy(); + if (scriptLang == null) { + scriptLang = ScriptLanguage.JS; + } + if (ScriptLanguage.JS.equals(scriptLang)) { + engine = new RuleNodeJsScriptEngine(getTenantId(), jsInvokeService, script, argNames); + } else { + if (mvelInvokeService == null) { + throw new IllegalArgumentException("MVEL script engine is disabled!"); } + engine = new RuleNodeMvelScriptEngine(getTenantId(), mvelInvokeService, script, argNames); + } + TbMsg inMsg = TbMsg.newMsg(msgType, null, new TbMsgMetaData(metadata), TbMsgDataType.JSON, data); + switch (scriptType) { + case "update": + output = msgToOutput(engine.executeUpdateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS)); + break; + case "generate": + output = msgToOutput(engine.executeGenerateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS)); + break; + case "filter": + boolean result = engine.executeFilterAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); + output = Boolean.toString(result); + break; + case "switch": + Set states = engine.executeSwitchAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); + output = objectMapper.writeValueAsString(states); + break; + case "json": + JsonNode json = engine.executeJsonAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); + output = objectMapper.writeValueAsString(json); + break; + case "string": + output = engine.executeToStringAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); + break; + default: + throw new IllegalArgumentException("Unsupported script type: " + scriptType); } - ObjectNode result = objectMapper.createObjectNode(); - result.put("output", output); - result.put("error", errorText); - return result; } catch (Exception e) { - throw handleException(e); + log.error("Error evaluating JS function", e); + errorText = e.getMessage(); + } finally { + if (engine != null) { + engine.destroy(); + } } + ObjectNode result = objectMapper.createObjectNode(); + result.put("output", output); + result.put("error", errorText); + return result; } @ApiOperation(value = "Export Rule Chains", notes = "Exports all tenant rule chains as one JSON." + TENANT_AUTHORITY_PARAGRAPH) @@ -473,13 +446,9 @@ public class RuleChainController extends BaseController { public RuleChainData exportRuleChains( @ApiParam(value = "A limit of rule chains to export.", required = true) @RequestParam("limit") int limit) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - PageLink pageLink = new PageLink(limit); - return checkNotNull(ruleChainService.exportTenantRuleChains(tenantId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = new PageLink(limit); + return checkNotNull(ruleChainService.exportTenantRuleChains(tenantId, pageLink)); } @ApiOperation(value = "Import Rule Chains", notes = "Imports all tenant rule chains as one JSON." + TENANT_AUTHORITY_PARAGRAPH) @@ -491,19 +460,15 @@ public class RuleChainController extends BaseController { @RequestBody RuleChainData ruleChainData, @ApiParam(value = "Enables overwrite for existing rule chains with the same name.") @RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - List importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite); - for (RuleChainImportResult importResult : importResults) { - if (importResult.getError() == null) { - tbClusterService.broadcastEntityStateChangeEvent(importResult.getTenantId(), importResult.getRuleChainId(), - importResult.isUpdated() ? ComponentLifecycleEvent.UPDATED : ComponentLifecycleEvent.CREATED); - } + TenantId tenantId = getCurrentUser().getTenantId(); + List importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite); + for (RuleChainImportResult importResult : importResults) { + if (importResult.getError() == null) { + tbClusterService.broadcastEntityStateChangeEvent(importResult.getTenantId(), importResult.getRuleChainId(), + importResult.isUpdated() ? ComponentLifecycleEvent.UPDATED : ComponentLifecycleEvent.CREATED); } - return importResults; - } catch (Exception e) { - throw handleException(e); } + return importResults; } private String msgToOutput(TbMsg msg) throws Exception { @@ -601,15 +566,11 @@ public class RuleChainController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter(EDGE_ID, strEdgeId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - checkEdgeId(edgeId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = getCurrentUser().getTenantId(); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + checkEdgeId(edgeId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink)); } @ApiOperation(value = "Set Edge Template Root Rule Chain (setEdgeTemplateRootRuleChain)", @@ -661,17 +622,13 @@ public class RuleChainController extends BaseController { @RequestMapping(value = "/ruleChain/autoAssignToEdgeRuleChains", method = RequestMethod.GET) @ResponseBody public List getAutoAssignToEdgeRuleChains() throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - List result = new ArrayList<>(); - PageDataIterableByTenant autoAssignRuleChainsIterator = - new PageDataIterableByTenant<>(ruleChainService::findAutoAssignToEdgeRuleChainsByTenantId, tenantId, DEFAULT_PAGE_SIZE); - for (RuleChain ruleChain : autoAssignRuleChainsIterator) { - result.add(ruleChain); - } - return checkNotNull(result); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = getCurrentUser().getTenantId(); + List result = new ArrayList<>(); + PageDataIterableByTenant autoAssignRuleChainsIterator = + new PageDataIterableByTenant<>(ruleChainService::findAutoAssignToEdgeRuleChainsByTenantId, tenantId, DEFAULT_PAGE_SIZE); + for (RuleChain ruleChain : autoAssignRuleChainsIterator) { + result.add(ruleChain); } + return checkNotNull(result); } } 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 ec0b530117..c4415071f0 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -82,20 +82,16 @@ public class TbResourceController extends BaseController { public ResponseEntity downloadResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); - try { - TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); - TbResource tbResource = checkResourceId(resourceId, Operation.READ); + TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); + TbResource tbResource = checkResourceId(resourceId, Operation.READ); - ByteArrayResource resource = new ByteArrayResource(Base64.getDecoder().decode(tbResource.getData().getBytes())); - return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + tbResource.getFileName()) - .header("x-filename", tbResource.getFileName()) - .contentLength(resource.contentLength()) - .contentType(MediaType.APPLICATION_OCTET_STREAM) - .body(resource); - } catch (Exception e) { - throw handleException(e); - } + ByteArrayResource resource = new ByteArrayResource(Base64.getDecoder().decode(tbResource.getData().getBytes())); + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + tbResource.getFileName()) + .header("x-filename", tbResource.getFileName()) + .contentLength(resource.contentLength()) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(resource); } @ApiOperation(value = "Get Resource Info (getResourceInfoById)", @@ -108,12 +104,8 @@ public class TbResourceController extends BaseController { public TbResourceInfo getResourceInfoById(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); - try { - TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); - return checkResourceInfoId(resourceId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); + return checkResourceInfoId(resourceId, Operation.READ); } @ApiOperation(value = "Get Resource (getResourceById)", @@ -126,12 +118,8 @@ public class TbResourceController extends BaseController { public TbResource getResourceById(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); - try { - TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); - return checkResourceId(resourceId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); + return checkResourceId(resourceId, Operation.READ); } @ApiOperation(value = "Create Or Update Resource (saveResource)", @@ -171,15 +159,11 @@ public class TbResourceController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { - return checkNotNull(resourceService.findTenantResourcesByTenantId(getTenantId(), pageLink)); - } else { - return checkNotNull(resourceService.findAllTenantResourcesByTenantId(getTenantId(), pageLink)); - } - } catch (Exception e) { - throw handleException(e); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { + return checkNotNull(resourceService.findTenantResourcesByTenantId(getTenantId(), pageLink)); + } else { + return checkNotNull(resourceService.findAllTenantResourcesByTenantId(getTenantId(), pageLink)); } } @@ -200,12 +184,8 @@ public class TbResourceController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = new PageLink(pageSize, page, textSearch); - return checkNotNull(resourceService.findLwM2mObjectPage(getTenantId(), sortProperty, sortOrder, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = new PageLink(pageSize, page, textSearch); + return checkNotNull(resourceService.findLwM2mObjectPage(getTenantId(), sortProperty, sortOrder, pageLink)); } @ApiOperation(value = "Get LwM2M Objects (getLwm2mListObjects)", @@ -221,11 +201,7 @@ public class TbResourceController extends BaseController { @RequestParam String sortProperty, @ApiParam(value = "LwM2M Object ids.", required = true) @RequestParam(required = false) String[] objectIds) throws ThingsboardException { - try { - return checkNotNull(resourceService.findLwM2mObject(getTenantId(), sortOrder, sortProperty, objectIds)); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(resourceService.findLwM2mObject(getTenantId(), sortOrder, sortProperty, objectIds)); } @ApiOperation(value = "Delete Resource (deleteResource)", 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 4498b51c6c..29fd1fc8b4 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -185,11 +185,7 @@ public class TelemetryController extends BaseController { public DeferredResult getAttributeKeys( @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException { - try { - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback); - } catch (Exception e) { - throw handleException(e); - } + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback); } @ApiOperation(value = "Get all attribute keys by scope (getAttributeKeysByScope)", @@ -206,12 +202,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope) throws ThingsboardException { - try { - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, - (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); - } catch (Exception e) { - throw handleException(e); - } + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, + (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); } @ApiOperation(value = "Get attributes (getAttributes)", @@ -229,13 +221,9 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, - (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); - } catch (Exception e) { - throw handleException(e); - } + SecurityUser user = getCurrentUser(); + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, + (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); } @@ -257,13 +245,9 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, - (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); - } catch (Exception e) { - throw handleException(e); - } + SecurityUser user = getCurrentUser(); + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, + (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); } @ApiOperation(value = "Get time-series keys (getTimeseriesKeys)", @@ -276,12 +260,8 @@ public class TelemetryController extends BaseController { public DeferredResult getTimeseriesKeys( @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException { - try { - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, - (result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor())); - } catch (Exception e) { - throw handleException(e); - } + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, + (result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor())); } @ApiOperation(value = "Get latest time-series value (getLatestTimeseries)", @@ -306,13 +286,9 @@ public class TelemetryController extends BaseController { @ApiParam(value = TELEMETRY_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr, @ApiParam(value = STRICT_DATA_TYPES_DESCRIPTION) @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, - (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); - } catch (Exception e) { - throw handleException(e); - } + SecurityUser user = getCurrentUser(); + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, + (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); } @ApiOperation(value = "Get time-series data (getTimeseries)", @@ -349,19 +325,15 @@ public class TelemetryController extends BaseController { @RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy, @ApiParam(value = STRICT_DATA_TYPES_DESCRIPTION) @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { - try { - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, - (result, tenantId, entityId) -> { - // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted - Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); - List queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg, orderBy)) - .collect(Collectors.toList()); - - Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor()); - }); - } catch (Exception e) { - throw handleException(e); - } + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, + (result, tenantId, entityId) -> { + // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted + Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); + List queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg, orderBy)) + .collect(Collectors.toList()); + + Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor()); + }); } @ApiOperation(value = "Save device attributes (saveDeviceAttributes)", @@ -385,12 +357,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("deviceId") String deviceIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); - return saveAttributes(getTenantId(), entityId, scope, request); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); + return saveAttributes(getTenantId(), entityId, scope, request); } @ApiOperation(value = "Save entity attributes (saveEntityAttributesV1)", @@ -413,12 +381,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveAttributes(getTenantId(), entityId, scope, request); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveAttributes(getTenantId(), entityId, scope, request); } @ApiOperation(value = "Save entity attributes (saveEntityAttributesV2)", @@ -441,12 +405,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveAttributes(getTenantId(), entityId, scope, request); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveAttributes(getTenantId(), entityId, scope, request); } @@ -470,12 +430,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = TELEMETRY_SCOPE_DESCRIPTION, required = true, allowableValues = "ANY") @PathVariable("scope") String scope, @ApiParam(value = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody String requestBody) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveTelemetry(getTenantId(), entityId, requestBody, 0L); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveTelemetry(getTenantId(), entityId, requestBody, 0L); } @ApiOperation(value = "Save or update time-series data with TTL (saveEntityTelemetryWithTTL)", @@ -500,12 +456,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = TELEMETRY_SCOPE_DESCRIPTION, required = true, allowableValues = "ANY") @PathVariable("scope") String scope, @ApiParam(value = "A long value representing TTL (Time to Live) parameter.", required = true) @PathVariable("ttl") Long ttl, @ApiParam(value = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody String requestBody) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveTelemetry(getTenantId(), entityId, requestBody, ttl); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveTelemetry(getTenantId(), entityId, requestBody, ttl); } @ApiOperation(value = "Delete entity time-series data (deleteEntityTimeseries)", @@ -538,12 +490,8 @@ public class TelemetryController extends BaseController { @RequestParam(name = "endTs", required = false) Long endTs, @ApiParam(value = "If the parameter is set to true, the latest telemetry will be rewritten in case that current latest value was removed, otherwise, in case that parameter is set to false the new latest value will not set.") @RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); } private DeferredResult deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys, @@ -608,12 +556,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(DEVICE_ID) String deviceIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); - return deleteAttributes(entityId, scope, keysStr); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); + return deleteAttributes(entityId, scope, keysStr); } @ApiOperation(value = "Delete entity attributes (deleteEntityAttributes)", @@ -636,12 +580,8 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return deleteAttributes(entityId, scope, keysStr); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return deleteAttributes(entityId, scope, keysStr); } private DeferredResult deleteAttributes(EntityId entityIdSrc, String scope, String keysStr) throws ThingsboardException { 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 b44c5327cd..7971cd437a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantController.java @@ -79,16 +79,12 @@ public class TenantController extends BaseController { @ApiParam(value = TENANT_ID_PARAM_DESCRIPTION) @PathVariable(TENANT_ID) String strTenantId) throws ThingsboardException { checkParameter(TENANT_ID, strTenantId); - try { - TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); - Tenant tenant = checkTenantId(tenantId, Operation.READ); - if (!tenant.getAdditionalInfo().isNull()) { - processDashboardIdFromAdditionalInfo((ObjectNode) tenant.getAdditionalInfo(), HOME_DASHBOARD); - } - return tenant; - } catch (Exception e) { - throw handleException(e); + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); + Tenant tenant = checkTenantId(tenantId, Operation.READ); + if (!tenant.getAdditionalInfo().isNull()) { + processDashboardIdFromAdditionalInfo((ObjectNode) tenant.getAdditionalInfo(), HOME_DASHBOARD); } + return tenant; } @ApiOperation(value = "Get Tenant Info (getTenantInfoById)", @@ -101,12 +97,8 @@ public class TenantController extends BaseController { @ApiParam(value = TENANT_ID_PARAM_DESCRIPTION) @PathVariable(TENANT_ID) String strTenantId) throws ThingsboardException { checkParameter(TENANT_ID, strTenantId); - try { - TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); - return checkTenantInfoId(tenantId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); + return checkTenantInfoId(tenantId, Operation.READ); } @ApiOperation(value = "Create Or update Tenant (saveTenant)", @@ -154,12 +146,8 @@ public class TenantController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(tenantService.findTenants(pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(tenantService.findTenants(pageLink)); } @ApiOperation(value = "Get Tenants Info (getTenants)", notes = "Returns a page of tenant info objects registered in the platform. " @@ -179,12 +167,8 @@ public class TenantController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder ) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(tenantService.findTenantInfos(pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(tenantService.findTenantInfos(pageLink)); } } 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 e5a33da948..5b3d721e14 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java @@ -75,12 +75,8 @@ public class TenantProfileController extends BaseController { @ApiParam(value = TENANT_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { checkParameter("tenantProfileId", strTenantProfileId); - try { - TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); - return checkTenantProfileId(tenantProfileId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); + return checkTenantProfileId(tenantProfileId, Operation.READ); } @ApiOperation(value = "Get Tenant Profile Info (getTenantProfileInfoById)", @@ -92,12 +88,8 @@ public class TenantProfileController extends BaseController { @ApiParam(value = TENANT_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { checkParameter("tenantProfileId", strTenantProfileId); - try { - TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); - return checkNotNull(tenantProfileService.findTenantProfileInfoById(getTenantId(), tenantProfileId)); - } catch (Exception e) { - throw handleException(e); - } + TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); + return checkNotNull(tenantProfileService.findTenantProfileInfoById(getTenantId(), tenantProfileId)); } @ApiOperation(value = "Get default Tenant Profile Info (getDefaultTenantProfileInfo)", @@ -106,11 +98,7 @@ public class TenantProfileController extends BaseController { @RequestMapping(value = "/tenantProfileInfo/default", method = RequestMethod.GET) @ResponseBody public EntityInfo getDefaultTenantProfileInfo() throws ThingsboardException { - try { - return checkNotNull(tenantProfileService.findDefaultTenantProfileInfo(getTenantId())); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(tenantProfileService.findDefaultTenantProfileInfo(getTenantId())); } @ApiOperation(value = "Create Or update Tenant Profile (saveTenantProfile)", @@ -171,19 +159,15 @@ public class TenantProfileController extends BaseController { @ResponseBody public TenantProfile saveTenantProfile(@ApiParam(value = "A JSON value representing the tenant profile.") @RequestBody TenantProfile tenantProfile) throws ThingsboardException { - try { - TenantProfile oldProfile; - if (tenantProfile.getId() == null) { - accessControlService.checkPermission(getCurrentUser(), Resource.TENANT_PROFILE, Operation.CREATE); - oldProfile = null; - } else { - oldProfile = checkTenantProfileId(tenantProfile.getId(), Operation.WRITE); - } - - return tbTenantProfileService.save(getTenantId(), tenantProfile, oldProfile); - } catch (Exception e) { - throw handleException(e); + TenantProfile oldProfile; + if (tenantProfile.getId() == null) { + accessControlService.checkPermission(getCurrentUser(), Resource.TENANT_PROFILE, Operation.CREATE); + oldProfile = null; + } else { + oldProfile = checkTenantProfileId(tenantProfile.getId(), Operation.WRITE); } + + return tbTenantProfileService.save(getTenantId(), tenantProfile, oldProfile); } @ApiOperation(value = "Delete Tenant Profile (deleteTenantProfile)", @@ -193,14 +177,10 @@ public class TenantProfileController extends BaseController { @ResponseStatus(value = HttpStatus.OK) public void deleteTenantProfile(@ApiParam(value = TENANT_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { - try { - checkParameter("tenantProfileId", strTenantProfileId); - TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); - TenantProfile profile = checkTenantProfileId(tenantProfileId, Operation.DELETE); - tbTenantProfileService.delete(getTenantId(), profile); - } catch (Exception e) { - throw handleException(e); - } + checkParameter("tenantProfileId", strTenantProfileId); + TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); + TenantProfile profile = checkTenantProfileId(tenantProfileId, Operation.DELETE); + tbTenantProfileService.delete(getTenantId(), profile); } @ApiOperation(value = "Make tenant profile default (setDefaultTenantProfile)", @@ -212,14 +192,10 @@ public class TenantProfileController extends BaseController { @ApiParam(value = TENANT_PROFILE_ID_PARAM_DESCRIPTION) @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { checkParameter("tenantProfileId", strTenantProfileId); - try { - TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); - TenantProfile tenantProfile = checkTenantProfileId(tenantProfileId, Operation.WRITE); - tenantProfileService.setDefaultTenantProfile(getTenantId(), tenantProfileId); - return tenantProfile; - } catch (Exception e) { - throw handleException(e); - } + TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); + TenantProfile tenantProfile = checkTenantProfileId(tenantProfileId, Operation.WRITE); + tenantProfileService.setDefaultTenantProfile(getTenantId(), tenantProfileId); + return tenantProfile; } @ApiOperation(value = "Get Tenant Profiles (getTenantProfiles)", notes = "Returns a page of tenant profiles registered in the platform. " + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH) @@ -237,12 +213,8 @@ public class TenantProfileController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(tenantProfileService.findTenantProfiles(getTenantId(), pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(tenantProfileService.findTenantProfiles(getTenantId(), pageLink)); } @ApiOperation(value = "Get Tenant Profiles Info (getTenantProfileInfos)", notes = "Returns a page of tenant profile info objects registered in the platform. " @@ -261,11 +233,7 @@ public class TenantProfileController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(tenantProfileService.findTenantProfileInfos(getTenantId(), pageLink)); - } catch (Exception e) { - throw handleException(e); - } + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(tenantProfileService.findTenantProfileInfos(getTenantId(), pageLink)); } } 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 e96a73b65c..222851389c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -112,22 +112,18 @@ public class UserController extends BaseController { @ApiParam(value = USER_ID_PARAM_DESCRIPTION) @PathVariable(USER_ID) String strUserId) throws ThingsboardException { checkParameter(USER_ID, strUserId); - try { - UserId userId = new UserId(toUUID(strUserId)); - User user = checkUserId(userId, Operation.READ); - if (user.getAdditionalInfo().isObject()) { - ObjectNode additionalInfo = (ObjectNode) user.getAdditionalInfo(); - processDashboardIdFromAdditionalInfo(additionalInfo, DEFAULT_DASHBOARD); - processDashboardIdFromAdditionalInfo(additionalInfo, HOME_DASHBOARD); - UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); - if (userCredentials.isEnabled() && !additionalInfo.has("userCredentialsEnabled")) { - additionalInfo.put("userCredentialsEnabled", true); - } + UserId userId = new UserId(toUUID(strUserId)); + User user = checkUserId(userId, Operation.READ); + if (user.getAdditionalInfo().isObject()) { + ObjectNode additionalInfo = (ObjectNode) user.getAdditionalInfo(); + processDashboardIdFromAdditionalInfo(additionalInfo, DEFAULT_DASHBOARD); + processDashboardIdFromAdditionalInfo(additionalInfo, HOME_DASHBOARD); + UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); + if (userCredentials.isEnabled() && !additionalInfo.has("userCredentialsEnabled")) { + additionalInfo.put("userCredentialsEnabled", true); } - return user; - } catch (Exception e) { - throw handleException(e); } + return user; } @ApiOperation(value = "Check Token Access Enabled (isUserTokenAccessEnabled)", @@ -152,23 +148,19 @@ public class UserController extends BaseController { @ApiParam(value = USER_ID_PARAM_DESCRIPTION) @PathVariable(USER_ID) String strUserId) throws ThingsboardException { checkParameter(USER_ID, strUserId); - try { - if (!userTokenAccessEnabled) { - throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, - ThingsboardErrorCode.PERMISSION_DENIED); - } - UserId userId = new UserId(toUUID(strUserId)); - SecurityUser authUser = getCurrentUser(); - User user = checkUserId(userId, Operation.READ); - UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); - UserCredentials credentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), userId); - SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); - JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); - JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); - return new JwtTokenPair(accessToken.getToken(), refreshToken.getToken()); - } catch (Exception e) { - throw handleException(e); + if (!userTokenAccessEnabled) { + throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, + ThingsboardErrorCode.PERMISSION_DENIED); } + UserId userId = new UserId(toUUID(strUserId)); + SecurityUser authUser = getCurrentUser(); + User user = checkUserId(userId, Operation.READ); + UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); + UserCredentials credentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), userId); + SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); + JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); + JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); + return new JwtTokenPair(accessToken.getToken(), refreshToken.getToken()); } @ApiOperation(value = "Save Or update User (saveUser)", @@ -203,23 +195,19 @@ public class UserController extends BaseController { @ApiParam(value = "Email of the user", required = true) @RequestParam(value = "email") String email, HttpServletRequest request) throws ThingsboardException { - try { - User user = checkNotNull(userService.findUserByEmail(getCurrentUser().getTenantId(), email)); + User user = checkNotNull(userService.findUserByEmail(getCurrentUser().getTenantId(), email)); - accessControlService.checkPermission(getCurrentUser(), Resource.USER, Operation.READ, - user.getId(), user); + accessControlService.checkPermission(getCurrentUser(), Resource.USER, Operation.READ, + user.getId(), user); - UserCredentials userCredentials = userService.findUserCredentialsByUserId(getCurrentUser().getTenantId(), user.getId()); - if (!userCredentials.isEnabled() && userCredentials.getActivateToken() != null) { - String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); - String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, - userCredentials.getActivateToken()); - mailService.sendActivationEmail(activateUrl, email); - } else { - throw new ThingsboardException("User is already activated!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - } catch (Exception e) { - throw handleException(e); + UserCredentials userCredentials = userService.findUserCredentialsByUserId(getCurrentUser().getTenantId(), user.getId()); + if (!userCredentials.isEnabled() && userCredentials.getActivateToken() != null) { + String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); + String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, + userCredentials.getActivateToken()); + mailService.sendActivationEmail(activateUrl, email); + } else { + throw new ThingsboardException("User is already activated!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } } @@ -234,21 +222,17 @@ public class UserController extends BaseController { @PathVariable(USER_ID) String strUserId, HttpServletRequest request) throws ThingsboardException { checkParameter(USER_ID, strUserId); - try { - UserId userId = new UserId(toUUID(strUserId)); - User user = checkUserId(userId, Operation.READ); - SecurityUser authUser = getCurrentUser(); - UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), user.getId()); - if (!userCredentials.isEnabled() && userCredentials.getActivateToken() != null) { - String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); - String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, - userCredentials.getActivateToken()); - return activateUrl; - } else { - throw new ThingsboardException("User is already activated!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - } catch (Exception e) { - throw handleException(e); + UserId userId = new UserId(toUUID(strUserId)); + User user = checkUserId(userId, Operation.READ); + SecurityUser authUser = getCurrentUser(); + UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), user.getId()); + if (!userCredentials.isEnabled() && userCredentials.getActivateToken() != null) { + String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); + String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, + userCredentials.getActivateToken()); + return activateUrl; + } else { + throw new ThingsboardException("User is already activated!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } } @@ -287,16 +271,12 @@ public class UserController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - SecurityUser currentUser = getCurrentUser(); - if (Authority.TENANT_ADMIN.equals(currentUser.getAuthority())) { - return checkNotNull(userService.findUsersByTenantId(currentUser.getTenantId(), pageLink)); - } else { - return checkNotNull(userService.findCustomerUsers(currentUser.getTenantId(), currentUser.getCustomerId(), pageLink)); - } - } catch (Exception e) { - throw handleException(e); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + SecurityUser currentUser = getCurrentUser(); + if (Authority.TENANT_ADMIN.equals(currentUser.getAuthority())) { + return checkNotNull(userService.findUsersByTenantId(currentUser.getTenantId(), pageLink)); + } else { + return checkNotNull(userService.findCustomerUsers(currentUser.getTenantId(), currentUser.getCustomerId(), pageLink)); } } @@ -319,13 +299,9 @@ public class UserController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("tenantId", strTenantId); - try { - TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(userService.findTenantAdmins(tenantId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId)); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(userService.findTenantAdmins(tenantId, pageLink)); } @ApiOperation(value = "Get Customer Users (getCustomerUsers)", @@ -347,15 +323,11 @@ public class UserController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("customerId", strCustomerId); - try { - CustomerId customerId = new CustomerId(toUUID(strCustomerId)); - checkCustomerId(customerId, Operation.READ); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(userService.findCustomerUsers(tenantId, customerId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + checkCustomerId(customerId, Operation.READ); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(userService.findCustomerUsers(tenantId, customerId, pageLink)); } @ApiOperation(value = "Enable/Disable User credentials (setUserCredentialsEnabled)", @@ -369,17 +341,13 @@ public class UserController extends BaseController { @ApiParam(value = "Disable (\"true\") or enable (\"false\") the credentials.", defaultValue = "true") @RequestParam(required = false, defaultValue = "true") boolean userCredentialsEnabled) throws ThingsboardException { checkParameter(USER_ID, strUserId); - try { - UserId userId = new UserId(toUUID(strUserId)); - User user = checkUserId(userId, Operation.WRITE); - TenantId tenantId = getCurrentUser().getTenantId(); - userService.setUserCredentialsEnabled(tenantId, userId, userCredentialsEnabled); + UserId userId = new UserId(toUUID(strUserId)); + User user = checkUserId(userId, Operation.WRITE); + TenantId tenantId = getCurrentUser().getTenantId(); + userService.setUserCredentialsEnabled(tenantId, userId, userCredentialsEnabled); - if (!userCredentialsEnabled) { - eventPublisher.publishEvent(new UserAuthDataChangedEvent(userId)); - } - } catch (Exception e) { - throw handleException(e); + if (!userCredentialsEnabled) { + eventPublisher.publishEvent(new UserAuthDataChangedEvent(userId)); } } 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 725d8ca2b4..a8b6ef5671 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java @@ -70,12 +70,8 @@ public class WidgetTypeController extends AutoCommitController { @ApiParam(value = WIDGET_TYPE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("widgetTypeId") String strWidgetTypeId) throws ThingsboardException { checkParameter("widgetTypeId", strWidgetTypeId); - try { - WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); - return checkWidgetTypeId(widgetTypeId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); + return checkWidgetTypeId(widgetTypeId, Operation.READ); } @ApiOperation(value = "Create Or Update Widget Type (saveWidgetType)", @@ -93,32 +89,28 @@ public class WidgetTypeController extends AutoCommitController { @ResponseBody public WidgetTypeDetails saveWidgetType( @ApiParam(value = "A JSON value representing the Widget Type Details.", required = true) - @RequestBody WidgetTypeDetails widgetTypeDetails) throws ThingsboardException { - try { - var currentUser = getCurrentUser(); - if (Authority.SYS_ADMIN.equals(currentUser.getAuthority())) { - widgetTypeDetails.setTenantId(TenantId.SYS_TENANT_ID); - } else { - widgetTypeDetails.setTenantId(currentUser.getTenantId()); - } + @RequestBody WidgetTypeDetails widgetTypeDetails) throws Exception { + var currentUser = getCurrentUser(); + if (Authority.SYS_ADMIN.equals(currentUser.getAuthority())) { + widgetTypeDetails.setTenantId(TenantId.SYS_TENANT_ID); + } else { + widgetTypeDetails.setTenantId(currentUser.getTenantId()); + } - checkEntity(widgetTypeDetails.getId(), widgetTypeDetails, Resource.WIDGET_TYPE); - WidgetTypeDetails savedWidgetTypeDetails = widgetTypeService.saveWidgetType(widgetTypeDetails); + checkEntity(widgetTypeDetails.getId(), widgetTypeDetails, Resource.WIDGET_TYPE); + WidgetTypeDetails savedWidgetTypeDetails = widgetTypeService.saveWidgetType(widgetTypeDetails); - if (!Authority.SYS_ADMIN.equals(currentUser.getAuthority())) { - WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(widgetTypeDetails.getTenantId(), widgetTypeDetails.getBundleAlias()); - if (widgetsBundle != null) { - autoCommit(currentUser, widgetsBundle.getId()); - } + if (!Authority.SYS_ADMIN.equals(currentUser.getAuthority())) { + WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(widgetTypeDetails.getTenantId(), widgetTypeDetails.getBundleAlias()); + if (widgetsBundle != null) { + autoCommit(currentUser, widgetsBundle.getId()); } + } - sendEntityNotificationMsg(getTenantId(), savedWidgetTypeDetails.getId(), - widgetTypeDetails.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); + sendEntityNotificationMsg(getTenantId(), savedWidgetTypeDetails.getId(), + widgetTypeDetails.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); - return checkNotNull(savedWidgetTypeDetails); - } catch (Exception e) { - throw handleException(e); - } + return checkNotNull(savedWidgetTypeDetails); } @ApiOperation(value = "Delete widget type (deleteWidgetType)", @@ -128,26 +120,22 @@ public class WidgetTypeController extends AutoCommitController { @ResponseStatus(value = HttpStatus.OK) public void deleteWidgetType( @ApiParam(value = WIDGET_TYPE_ID_PARAM_DESCRIPTION, required = true) - @PathVariable("widgetTypeId") String strWidgetTypeId) throws ThingsboardException { + @PathVariable("widgetTypeId") String strWidgetTypeId) throws Exception { checkParameter("widgetTypeId", strWidgetTypeId); - try { - var currentUser = getCurrentUser(); - WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); - WidgetTypeDetails wtd = checkWidgetTypeId(widgetTypeId, Operation.DELETE); - widgetTypeService.deleteWidgetType(currentUser.getTenantId(), widgetTypeId); - - if (wtd != null && !Authority.SYS_ADMIN.equals(currentUser.getAuthority())) { - WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(wtd.getTenantId(), wtd.getBundleAlias()); - if (widgetsBundle != null) { - autoCommit(currentUser, widgetsBundle.getId()); - } + var currentUser = getCurrentUser(); + WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); + WidgetTypeDetails wtd = checkWidgetTypeId(widgetTypeId, Operation.DELETE); + widgetTypeService.deleteWidgetType(currentUser.getTenantId(), widgetTypeId); + + if (wtd != null && !Authority.SYS_ADMIN.equals(currentUser.getAuthority())) { + WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(wtd.getTenantId(), wtd.getBundleAlias()); + if (widgetsBundle != null) { + autoCommit(currentUser, widgetsBundle.getId()); } + } - sendEntityNotificationMsg(getTenantId(), widgetTypeId, EdgeEventActionType.DELETED); + sendEntityNotificationMsg(getTenantId(), widgetTypeId, EdgeEventActionType.DELETED); - } catch (Exception e) { - throw handleException(e); - } } @ApiOperation(value = "Get all Widget types for specified Bundle (getBundleWidgetTypes)", @@ -160,17 +148,13 @@ public class WidgetTypeController extends AutoCommitController { @RequestParam boolean isSystem, @ApiParam(value = "Widget Bundle alias", required = true) @RequestParam String bundleAlias) throws ThingsboardException { - try { - TenantId tenantId; - if (isSystem) { - tenantId = TenantId.SYS_TENANT_ID; - } else { - tenantId = getCurrentUser().getTenantId(); - } - return checkNotNull(widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(tenantId, bundleAlias)); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId; + if (isSystem) { + tenantId = TenantId.SYS_TENANT_ID; + } else { + tenantId = getCurrentUser().getTenantId(); } + return checkNotNull(widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(tenantId, bundleAlias)); } @ApiOperation(value = "Get all Widget types details for specified Bundle (getBundleWidgetTypes)", @@ -183,17 +167,13 @@ public class WidgetTypeController extends AutoCommitController { @RequestParam boolean isSystem, @ApiParam(value = "Widget Bundle alias", required = true) @RequestParam String bundleAlias) throws ThingsboardException { - try { - TenantId tenantId; - if (isSystem) { - tenantId = TenantId.SYS_TENANT_ID; - } else { - tenantId = getCurrentUser().getTenantId(); - } - return checkNotNull(widgetTypeService.findWidgetTypesDetailsByTenantIdAndBundleAlias(tenantId, bundleAlias)); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId; + if (isSystem) { + tenantId = TenantId.SYS_TENANT_ID; + } else { + tenantId = getCurrentUser().getTenantId(); } + return checkNotNull(widgetTypeService.findWidgetTypesDetailsByTenantIdAndBundleAlias(tenantId, bundleAlias)); } @ApiOperation(value = "Get Widget Type Info objects (getBundleWidgetTypesInfos)", @@ -206,17 +186,13 @@ public class WidgetTypeController extends AutoCommitController { @RequestParam boolean isSystem, @ApiParam(value = "Widget Bundle alias", required = true) @RequestParam String bundleAlias) throws ThingsboardException { - try { - TenantId tenantId; - if (isSystem) { - tenantId = TenantId.SYS_TENANT_ID; - } else { - tenantId = getCurrentUser().getTenantId(); - } - return checkNotNull(widgetTypeService.findWidgetTypesInfosByTenantIdAndBundleAlias(tenantId, bundleAlias)); - } catch (Exception e) { - throw handleException(e); + TenantId tenantId; + if (isSystem) { + tenantId = TenantId.SYS_TENANT_ID; + } else { + tenantId = getCurrentUser().getTenantId(); } + return checkNotNull(widgetTypeService.findWidgetTypesInfosByTenantIdAndBundleAlias(tenantId, bundleAlias)); } @ApiOperation(value = "Get Widget Type (getWidgetType)", @@ -231,20 +207,16 @@ public class WidgetTypeController extends AutoCommitController { @RequestParam String bundleAlias, @ApiParam(value = "Widget Type alias", required = true) @RequestParam String alias) throws ThingsboardException { - try { - TenantId tenantId; - if (isSystem) { - tenantId = TenantId.fromUUID(ModelConstants.NULL_UUID); - } else { - tenantId = getCurrentUser().getTenantId(); - } - WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdBundleAliasAndAlias(tenantId, bundleAlias, alias); - checkNotNull(widgetType); - accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, Operation.READ, widgetType.getId(), widgetType); - return widgetType; - } catch (Exception e) { - throw handleException(e); + TenantId tenantId; + if (isSystem) { + tenantId = TenantId.fromUUID(ModelConstants.NULL_UUID); + } else { + tenantId = getCurrentUser().getTenantId(); } + WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdBundleAliasAndAlias(tenantId, bundleAlias, alias); + checkNotNull(widgetType); + accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, Operation.READ, widgetType.getId(), widgetType); + return widgetType; } } 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 23ec498d0e..e5d8708283 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java @@ -74,12 +74,8 @@ public class WidgetsBundleController extends BaseController { @ApiParam(value = WIDGET_BUNDLE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException { checkParameter("widgetsBundleId", strWidgetsBundleId); - try { - WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); - return checkWidgetsBundleId(widgetsBundleId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } + WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); + return checkWidgetsBundleId(widgetsBundleId, Operation.READ); } @ApiOperation(value = "Create Or Update Widget Bundle (saveWidgetsBundle)", @@ -141,16 +137,12 @@ public class WidgetsBundleController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { - return checkNotNull(widgetsBundleService.findSystemWidgetsBundlesByPageLink(getTenantId(), pageLink)); - } else { - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, pageLink)); - } - } catch (Exception e) { - throw handleException(e); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { + return checkNotNull(widgetsBundleService.findSystemWidgetsBundlesByPageLink(getTenantId(), pageLink)); + } else { + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, pageLink)); } } @@ -160,15 +152,11 @@ public class WidgetsBundleController extends BaseController { @RequestMapping(value = "/widgetsBundles", method = RequestMethod.GET) @ResponseBody public List getWidgetsBundles() throws ThingsboardException { - try { - if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { - return checkNotNull(widgetsBundleService.findSystemWidgetsBundles(getTenantId())); - } else { - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantId(tenantId)); - } - } catch (Exception e) { - throw handleException(e); + if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { + return checkNotNull(widgetsBundleService.findSystemWidgetsBundles(getTenantId())); + } else { + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantId(tenantId)); } } From 3734a0da49db4180045155bf90b562f6071ed4ad Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 4 Nov 2022 15:29:15 +0200 Subject: [PATCH 017/488] Notifications api permission checks, WS improvements, new target configs --- .../main/data/upgrade/3.4.2/schema_update.sql | 1 + .../server/controller/BaseController.java | 18 +- .../controller/NotificationController.java | 27 ++- .../NotificationTargetController.java | 76 +++++-- .../controller/plugin/TbWebSocketHandler.java | 3 +- .../DefaultNotificationProcessingService.java | 44 ++-- .../queue/DefaultTbCoreConsumerService.java | 6 +- .../service/security/permission/Resource.java | 3 +- .../permission/SysAdminPermissions.java | 2 + .../permission/TenantAdminPermissions.java | 3 +- .../DefaultSubscriptionManagerService.java | 9 +- .../DefaultTbLocalSubscriptionService.java | 3 +- .../SubscriptionManagerService.java | 2 +- .../subscription/TbSubscriptionType.java | 2 +- .../subscription/TbSubscriptionUtils.java | 22 +- .../telemetry/DefaultWebSocketService.java | 193 +++++++++--------- .../thingsboard/server/service/ws/WsCmd.java | 20 ++ .../DefaultNotificationCommandsHandler.java | 77 +++++-- .../NotificationCommandsHandler.java | 3 + .../cmd/MarkNotificationAsReadCmd.java | 3 +- .../cmd/NotificationCmdsWrapper.java | 10 +- .../cmd/NotificationsCountSubCmd.java | 29 +++ .../notification/cmd/NotificationsSubCmd.java | 4 +- .../cmd/NotificationsUnsubCmd.java | 7 +- .../cmd/UnreadNotificationsCountUpdate.java | 44 ++++ .../sub/NotificationsCountSubscription.java | 47 +++++ .../sub}/NotificationsSubscription.java | 11 +- .../sub/NotificationsSubscriptionUpdate.java | 1 + .../ws/telemetry/cmd/v2/CmdUpdateType.java | 3 +- .../controller/TbTestWebSocketClient.java | 1 + .../NotificationsWebSocketClient.java | 50 +++-- .../notification/NotificationsWsApiTest.java | 67 +++++- common/cluster-api/src/main/proto/queue.proto | 11 +- .../dao/notification/NotificationService.java | 6 +- .../NotificationTargetService.java | 9 +- .../server/dao/user/UserService.java | 2 + .../notification/NotificationRequest.java | 9 +- .../AllUsersNotificationTargetConfig.java | 28 +++ ...CustomerUsersNotificationTargetConfig.java | 32 +++ .../targets/NotificationTargetConfig.java | 4 +- .../targets/NotificationTargetConfigType.java | 3 + .../SingleUserNotificationTargetConfig.java | 4 +- .../UserListNotificationTargetConfig.java | 5 +- .../org/thingsboard/server/dao/DaoUtil.java | 6 +- .../DefaultNotificationService.java | 15 +- .../DefaultNotificationTargetService.java | 53 +++-- .../dao/notification/NotificationDao.java | 4 +- .../sql/notification/JpaNotificationDao.java | 9 +- .../notification/NotificationRepository.java | 9 +- .../server/dao/sql/user/JpaUserDao.java | 5 + .../thingsboard/server/dao/user/UserDao.java | 3 + .../server/dao/user/UserServiceImpl.java | 5 + .../resources/sql/schema-entities-idx.sql | 2 + 53 files changed, 752 insertions(+), 263 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/WsCmd.java create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsCountSubCmd.java create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsCountUpdate.java create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsCountSubscription.java rename application/src/main/java/org/thingsboard/server/service/{subscription => ws/notification/sub}/NotificationsSubscription.java (85%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/AllUsersNotificationTargetConfig.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/CustomerUsersNotificationTargetConfig.java diff --git a/application/src/main/data/upgrade/3.4.2/schema_update.sql b/application/src/main/data/upgrade/3.4.2/schema_update.sql index 17c75773ad..77f078e67f 100644 --- a/application/src/main/data/upgrade/3.4.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.4.2/schema_update.sql @@ -49,5 +49,6 @@ CREATE TABLE IF NOT EXISTS notification ( severity VARCHAR(32), status VARCHAR(32) ) PARTITION BY RANGE (created_time); +CREATE INDEX IF NOT EXISTS idx_notification_id ON notification(id); CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_and_created_time ON notification(recipient_id, created_time DESC); CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_and_status_and_created_time ON notification(recipient_id, status, created_time DESC); diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index ffc3fcf3cd..8f5fee218f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityViewInfo; +import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.OtaPackageInfo; @@ -58,6 +59,7 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetInfo; import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; @@ -75,6 +77,7 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.RpcId; @@ -130,8 +133,8 @@ import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.action.EntityActionService; import org.thingsboard.server.service.component.ComponentDiscoveryService; -import org.thingsboard.server.service.edge.EdgeNotificationService; import org.thingsboard.server.service.edge.rpc.EdgeRpcService; import org.thingsboard.server.service.entitiy.TbNotificationEntityService; import org.thingsboard.server.service.ota.OtaPackageStateService; @@ -284,6 +287,9 @@ public abstract class BaseController { @Autowired protected TbNotificationEntityService notificationEntityService; + @Autowired + protected EntityActionService entityActionService; + @Autowired protected QueueService queueService; @@ -912,6 +918,16 @@ public abstract class BaseController { return error != null ? (Exception.class.isInstance(error) ? (Exception) error : new Exception(error)) : null; } + protected > void logEntityAction(SecurityUser user, EntityType entityType, E savedEntity, ActionType actionType) { + logEntityAction(user, entityType, null, savedEntity, actionType, null); + } + + protected > void logEntityAction(SecurityUser user, EntityType entityType, E entity, E savedEntity, ActionType actionType, Exception e) { + EntityId entityId = savedEntity != null ? savedEntity.getId() : emptyId(entityType); + entityActionService.logEntityAction(user, entityId, savedEntity != null ? savedEntity : entity, + user.getCustomerId(), actionType, e); + } + protected void sendEntityNotificationMsg(TenantId tenantId, EntityId entityId, EdgeEventActionType action) { sendNotificationMsgToEdge(tenantId, null, entityId, null, null, action); } diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index c3cfdc24e8..1bf771218a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -28,6 +28,8 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; @@ -81,9 +83,15 @@ public class NotificationController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public NotificationRequest createNotificationRequest(@RequestBody NotificationRequest notificationRequest, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { - accessControlService.checkPermission(user, Resource.NOTIFICATION, Operation.CREATE); - // read permission for target's users - return notificationProcessingService.processNotificationRequest(user.getTenantId(), notificationRequest); + accessControlService.checkPermission(user, Resource.NOTIFICATION_REQUEST, Operation.CREATE, null, notificationRequest); + try { + NotificationRequest savedNotificationRequest = notificationProcessingService.processNotificationRequest(user.getTenantId(), notificationRequest); + logEntityAction(user, EntityType.NOTIFICATION_REQUEST, savedNotificationRequest, ActionType.ADDED); + return savedNotificationRequest; + } catch (Exception e) { + logEntityAction(user, EntityType.NOTIFICATION_REQUEST, notificationRequest, null, ActionType.ADDED, e); + throw e; + } } @GetMapping("/notification/request/{id}") @@ -102,16 +110,23 @@ public class NotificationController extends BaseController { @RequestParam(required = false) String sortProperty, @RequestParam(required = false) String sortOrder, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { - accessControlService.checkPermission(user, Resource.NOTIFICATION, Operation.CREATE); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); return notificationService.findNotificationRequestsByTenantIdAndPageLink(user.getTenantId(), pageLink); } @DeleteMapping("/notification/request/{id}") public void deleteNotificationRequest(@PathVariable UUID id, - @AuthenticationPrincipal SecurityUser user) { + @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { NotificationRequestId notificationRequestId = new NotificationRequestId(id); - notificationProcessingService.deleteNotificationRequest(user.getTenantId(), notificationRequestId); + NotificationRequest notificationRequest = notificationService.findNotificationRequestById(user.getTenantId(), notificationRequestId); + accessControlService.checkPermission(user, Resource.NOTIFICATION_REQUEST, Operation.DELETE, notificationRequestId, notificationRequest); + try { + notificationProcessingService.deleteNotificationRequest(user.getTenantId(), notificationRequestId); + logEntityAction(user, EntityType.NOTIFICATION_REQUEST, notificationRequest, ActionType.DELETED); + } catch (Exception e) { + logEntityAction(user, EntityType.NOTIFICATION_REQUEST, notificationRequest, notificationRequest, ActionType.DELETED, e); + throw e; + } } } 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 3eec3b6ffc..34451ddede 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java @@ -27,24 +27,26 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.NotificationTargetId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfigType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.notification.NotificationTargetService; -import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.security.permission.Resource; -import java.util.ArrayList; import java.util.List; import java.util.UUID; -import java.util.stream.Collectors; @RestController @TbCoreComponent @@ -52,37 +54,59 @@ import java.util.stream.Collectors; @RequiredArgsConstructor @Slf4j public class NotificationTargetController extends BaseController { - // fixme: permission check, log action, events private final NotificationTargetService notificationTargetService; - private final UserService userService; @PostMapping("/target") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public NotificationTarget saveNotificationTarget(@RequestBody NotificationTarget notificationTarget, - @AuthenticationPrincipal SecurityUser user) { - // fixme: read permission check for users in target - return notificationTargetService.saveNotificationTarget(user.getTenantId(), notificationTarget); + @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { + accessControlService.checkPermission(user, Resource.NOTIFICATION_TARGET, Operation.CREATE, null, notificationTarget); + if (!user.isSystemAdmin()) { + NotificationTargetConfig targetConfig = notificationTarget.getConfiguration(); + if (targetConfig.getType() == NotificationTargetConfigType.SINGLE_USER || + targetConfig.getType() == NotificationTargetConfigType.USER_LIST) { + PageData recipients = notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), notificationTarget.getConfiguration(), null); + for (User recipient : recipients.getData()) { + accessControlService.checkPermission(user, Resource.USER, Operation.READ, recipient.getId(), recipient); + } + } + } + + try { + NotificationTarget savedNotificationTarget = notificationTargetService.saveNotificationTarget(user.getTenantId(), notificationTarget); + logEntityAction(user, EntityType.NOTIFICATION_TARGET, savedNotificationTarget, + notificationTarget.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + return savedNotificationTarget; + } catch (Exception e) { + logEntityAction(user, EntityType.NOTIFICATION_TARGET, notificationTarget, null, + notificationTarget.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); + throw e; + } } @GetMapping("/target/{id}") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public NotificationTarget getNotificationTargetById(@PathVariable UUID id, - @AuthenticationPrincipal SecurityUser user) { + @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { NotificationTargetId notificationTargetId = new NotificationTargetId(id); - return notificationTargetService.findNotificationTargetById(user.getTenantId(), notificationTargetId); + NotificationTarget notificationTarget = notificationTargetService.findNotificationTargetById(user.getTenantId(), notificationTargetId); + accessControlService.checkPermission(user, Resource.NOTIFICATION_TARGET, Operation.READ, notificationTargetId, notificationTarget); + return notificationTarget; } - @GetMapping("/target/{id}/recipients") + @PostMapping("/target/recipients") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - public List getRecipientsForNotificationTarget(@PathVariable UUID id, - @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { - NotificationTargetId notificationTargetId = new NotificationTargetId(id); - // fixme: to page data - // todo: check read permission for recipients - List recipients = new ArrayList<>(); - for (UserId userId : notificationTargetService.findRecipientsForNotificationTarget(user.getTenantId(), notificationTargetId)) { - recipients.add(checkUserId(userId, Operation.READ)); + public PageData getRecipientsForNotificationTargetConfig(@RequestBody NotificationTarget notificationTarget, + @RequestParam int pageSize, + @RequestParam int page, + @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { + PageLink pageLink = createPageLink(pageSize, page, null, null, null); + PageData recipients = notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), notificationTarget.getConfiguration(), pageLink); + if (!user.isSystemAdmin()) { + for (User recipient : recipients.getData()) { + accessControlService.checkPermission(user, Resource.USER, Operation.READ, recipient.getId(), recipient); + } } return recipients; } @@ -102,9 +126,17 @@ public class NotificationTargetController extends BaseController { @DeleteMapping("/target/{id}") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public void deleteNotificationTarget(@PathVariable UUID id, - @AuthenticationPrincipal SecurityUser user) { + @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { NotificationTargetId notificationTargetId = new NotificationTargetId(id); - notificationTargetService.deleteNotificationTarget(user.getTenantId(), notificationTargetId); + NotificationTarget notificationTarget = checkNotNull(notificationTargetService.findNotificationTargetById(user.getTenantId(), notificationTargetId)); + accessControlService.checkPermission(user, Resource.NOTIFICATION_TARGET, Operation.DELETE, notificationTargetId, notificationTarget); + + try { + notificationTargetService.deleteNotificationTarget(user.getTenantId(), notificationTargetId); + logEntityAction(user, EntityType.NOTIFICATION_TARGET, notificationTarget, ActionType.DELETED); + } catch (Exception e) { + logEntityAction(user, EntityType.NOTIFICATION_TARGET, null, notificationTarget, ActionType.DELETED, e); + } } } diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index 187603afc5..7cb0a8258e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.BeanCreationNotAllowedException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; import org.springframework.web.socket.CloseStatus; @@ -69,7 +70,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke private static final ConcurrentMap externalSessionMap = new ConcurrentHashMap<>(); - @Autowired + @Autowired @Lazy private WebSocketService webSocketService; @Autowired diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java index 57b295bddd..99ede1fba9 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java @@ -32,10 +32,10 @@ import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.notification.NotificationProcessingService; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationTargetService; -import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.discovery.NotificationsTopicService; import org.thingsboard.server.queue.discovery.PartitionService; @@ -43,9 +43,7 @@ import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import org.thingsboard.server.service.telemetry.AbstractSubscriptionService; -import java.util.ArrayList; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -56,32 +54,23 @@ public class DefaultNotificationProcessingService extends AbstractSubscriptionSe private final NotificationTargetService notificationTargetService; private final NotificationService notificationService; - private final UserService userService; private final DbCallbackExecutorService dbCallbackExecutorService; private final NotificationsTopicService notificationsTopicService; public DefaultNotificationProcessingService(TbClusterService clusterService, PartitionService partitionService, NotificationTargetService notificationTargetService, - NotificationService notificationService, UserService userService, + NotificationService notificationService, DbCallbackExecutorService dbCallbackExecutorService, NotificationsTopicService notificationsTopicService) { super(clusterService, partitionService); this.notificationTargetService = notificationTargetService; this.notificationService = notificationService; - this.userService = userService; this.dbCallbackExecutorService = dbCallbackExecutorService; this.notificationsTopicService = notificationsTopicService; } @Override public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { - List recipientsIds = notificationTargetService.findRecipientsForNotificationTarget(tenantId, notificationRequest.getTargetId()); - List recipients = new ArrayList<>(); - for (UserId recipientId : recipientsIds) { - User recipient = userService.findUserById(tenantId, recipientId); // todo: add caching - recipients.add(recipient); - } - notificationRequest.setTenantId(tenantId); NotificationRequest savedNotificationRequest = notificationService.createNotificationRequest(tenantId, notificationRequest); @@ -90,20 +79,31 @@ public class DefaultNotificationProcessingService extends AbstractSubscriptionSe // todo: delayed sending; check all delayed notification requests on start up, schedule send } - for (User recipient : recipients) { + DaoUtil.processBatches(pageLink -> { + return notificationTargetService.findRecipientsForNotificationTarget(tenantId, notificationRequest.getTargetId(), pageLink); + }, 100, recipients -> { dbCallbackExecutorService.submit(() -> { - Notification notification = createNotification(recipient, savedNotificationRequest); - onNotificationUpdate(recipient.getTenantId(), recipient.getId(), notification); + for (User recipient : recipients) { + try { + Notification notification = createNotification(recipient, savedNotificationRequest); + onNotificationUpdate(recipient.getTenantId(), recipient.getId(), notification, true); + } catch (Exception e) { + log.error("Failed to create notification for recipient {}", recipient.getId(), e); + } + } }); - } + }); return savedNotificationRequest; } @Override public void markNotificationAsRead(TenantId tenantId, UserId recipientId, NotificationId notificationId) { - Notification notification = notificationService.updateNotificationStatus(tenantId, notificationId, NotificationStatus.READ); - onNotificationUpdate(tenantId, recipientId, notification); + boolean updated = notificationService.updateNotificationStatus(tenantId, recipientId, notificationId, NotificationStatus.READ); + if (updated) { + Notification notification = notificationService.findNotificationById(tenantId, notificationId); + onNotificationUpdate(tenantId, recipientId, notification, false); + } } @Override @@ -134,11 +134,11 @@ public class DefaultNotificationProcessingService extends AbstractSubscriptionSe return TbNodeUtils.processTemplate(template, context); } - private void onNotificationUpdate(TenantId tenantId, UserId recipientId, Notification notification) { + private void onNotificationUpdate(TenantId tenantId, UserId recipientId, Notification notification, boolean isNew) { forwardToSubscriptionManagerServiceOrSendToCore(tenantId, recipientId, subscriptionManagerService -> { - subscriptionManagerService.onNotificationUpdate(tenantId, recipientId, notification, TbCallback.EMPTY); + subscriptionManagerService.onNotificationUpdate(tenantId, recipientId, notification, isNew, TbCallback.EMPTY); }, () -> { - return TbSubscriptionUtils.notificationUpdateToProto(tenantId, recipientId, notification); + return TbSubscriptionUtils.notificationUpdateToProto(tenantId, recipientId, notification, isNew); }); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index 0a7abdab51..1ee7c3cd16 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -464,6 +464,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService subscriptions = subscriptionsByEntityId.get(recipientId); if (subscriptions != null) { NotificationsSubscriptionUpdate subscriptionUpdate = NotificationsSubscriptionUpdate.builder() .notification(notification) + .isNewNotification(isNew) .build(); subscriptions.stream() - .filter(subscription -> subscription.getType() == TbSubscriptionType.NOTIFICATIONS) + .filter(subscription -> subscription.getType() == TbSubscriptionType.NOTIFICATIONS + || subscription.getType() == TbSubscriptionType.NOTIFICATIONS_COUNT) .forEach(subscription -> { if (serviceId.equals(subscription.getServiceId())) { localSubscriptionService.onSubscriptionUpdate(subscription.getSessionId(), @@ -362,7 +364,8 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene subscriptionsByEntityId.entrySet().stream() .filter(subEntry -> subEntry.getKey().getEntityType() == EntityType.USER) .flatMap(subEntry -> subEntry.getValue().stream() - .filter(sub -> sub.getType() == TbSubscriptionType.NOTIFICATIONS) + .filter(sub -> sub.getType() == TbSubscriptionType.NOTIFICATIONS + || sub.getType() == TbSubscriptionType.NOTIFICATIONS_COUNT) .filter(sub -> sub.getServiceId().equals(serviceId))) .forEach(subscription -> { localSubscriptionService.onSubscriptionUpdate(subscription.getSessionId(), subscription.getSubscriptionId(), subscriptionUpdate, TbCallback.EMPTY); 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 e9001f3be3..5a51d0acee 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 @@ -172,7 +172,8 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer @Override public void onSubscriptionUpdate(String sessionId, int subscriptionId, NotificationsSubscriptionUpdate update, TbCallback callback) { TbSubscription subscription = subscriptionsBySessionId.getOrDefault(sessionId, Collections.emptyMap()).get(subscriptionId); - if (subscription != null && subscription.getType() == TbSubscriptionType.NOTIFICATIONS) { + if (subscription != null && (subscription.getType() == TbSubscriptionType.NOTIFICATIONS + || subscription.getType() == TbSubscriptionType.NOTIFICATIONS_COUNT)) { subscriptionUpdateExecutor.submit(() -> subscription.getUpdateProcessor().accept(subscription, update)); } callback.onSuccess(); diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java index 6724674143..239f9c94f9 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java @@ -49,7 +49,7 @@ public interface SubscriptionManagerService extends ApplicationListener 0) { return new TelemetrySubscriptionUpdate(proto.getSubscriptionId(), SubscriptionErrorCode.forCode(proto.getErrorCode()), proto.getErrorMsg()); @@ -367,6 +385,7 @@ public class TbSubscriptionUtils { .setSessionId(subscription.getSessionId()) .setSubscriptionId(subscription.getSubscriptionId()) .setNotification(JacksonUtil.toString(update.getNotification())) + .setIsNewNotification(update.isNewNotification()) .build(); return TransportProtos.ToCoreNotificationMsg.newBuilder() .setToLocalSubscriptionServiceMsg(TransportProtos.LocalSubscriptionServiceMsgProto.newBuilder() @@ -375,13 +394,14 @@ public class TbSubscriptionUtils { .build(); } - public static ToCoreMsg notificationUpdateToProto(TenantId tenantId, UserId recipientId, Notification notification) { + public static ToCoreMsg notificationUpdateToProto(TenantId tenantId, UserId recipientId, Notification notification, boolean isNew) { TransportProtos.NotificationUpdateProto updateProto = TransportProtos.NotificationUpdateProto.newBuilder() .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) .setRecipientIdMSB(recipientId.getId().getMostSignificantBits()) .setRecipientIdLSB(recipientId.getId().getLeastSignificantBits()) .setNotification(JacksonUtil.toString(notification)) + .setIsNew(isNew) .build(); return ToCoreMsg.newBuilder() .setToSubscriptionMgrMsg(SubscriptionMgrMsgProto.newBuilder() diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java index f6df94b46f..e03e0fc3fb 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java @@ -22,8 +22,8 @@ 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 lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.socket.CloseStatus; @@ -65,13 +65,10 @@ import org.thingsboard.server.service.subscription.TbTimeseriesSubscription; import org.thingsboard.server.service.ws.SessionEvent; import org.thingsboard.server.service.ws.WebSocketMsgEndpoint; import org.thingsboard.server.service.ws.WebSocketSessionRef; +import org.thingsboard.server.service.ws.WsCmd; import org.thingsboard.server.service.ws.WsSessionMetaData; -import org.thingsboard.server.service.ws.notification.DefaultNotificationCommandsHandler; import org.thingsboard.server.service.ws.notification.NotificationCommandsHandler; import org.thingsboard.server.service.ws.notification.cmd.NotificationCmdsWrapper; -import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd; -import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; -import org.thingsboard.server.service.ws.notification.cmd.NotificationsUnsubCmd; import org.thingsboard.server.service.ws.telemetry.WebSocketService; import org.thingsboard.server.service.ws.telemetry.cmd.TelemetryPluginCmdsWrapper; import org.thingsboard.server.service.ws.telemetry.cmd.v1.AttributesSubscriptionCmd; @@ -105,6 +102,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -114,6 +112,7 @@ import java.util.stream.Collectors; @Service @TbCoreComponent @Slf4j +@RequiredArgsConstructor public class DefaultWebSocketService implements WebSocketService { public static final int NUMBER_OF_PING_ATTEMPTS = 3; @@ -130,53 +129,57 @@ public class DefaultWebSocketService implements WebSocketService { private final ConcurrentMap wsSessionsMap = new ConcurrentHashMap<>(); - @Autowired - private TbLocalSubscriptionService oldSubService; - - @Autowired - private TbEntityDataSubscriptionService entityDataSubService; - - @Autowired - private NotificationCommandsHandler notificationCmdsHandler; - - @Autowired - private WebSocketMsgEndpoint msgEndpoint; - - @Autowired - private AccessValidator accessValidator; - - @Autowired - private AttributesService attributesService; - - @Autowired - private TimeseriesService tsService; - - @Autowired - private TbServiceInfoProvider serviceInfoProvider; - - @Autowired - private TbTenantProfileCache tenantProfileCache; + private final TbLocalSubscriptionService oldSubService; + private final TbEntityDataSubscriptionService entityDataSubService; + private final NotificationCommandsHandler notificationCmdsHandler; + private final WebSocketMsgEndpoint msgEndpoint; + private final AccessValidator accessValidator; + private final AttributesService attributesService; + private final TimeseriesService tsService; + private final TbServiceInfoProvider serviceInfoProvider; + private final TbTenantProfileCache tenantProfileCache; @Value("${server.ws.ping_timeout:30000}") private long pingTimeout; - private ConcurrentMap> tenantSubscriptionsMap = new ConcurrentHashMap<>(); - private ConcurrentMap> customerSubscriptionsMap = new ConcurrentHashMap<>(); - private ConcurrentMap> regularUserSubscriptionsMap = new ConcurrentHashMap<>(); - private ConcurrentMap> publicUserSubscriptionsMap = new ConcurrentHashMap<>(); + private final ConcurrentMap> tenantSubscriptionsMap = new ConcurrentHashMap<>(); + private final ConcurrentMap> customerSubscriptionsMap = new ConcurrentHashMap<>(); + private final ConcurrentMap> regularUserSubscriptionsMap = new ConcurrentHashMap<>(); + private final ConcurrentMap> publicUserSubscriptionsMap = new ConcurrentHashMap<>(); private ExecutorService executor; + private ScheduledExecutorService pingExecutor; private String serviceId; - private ScheduledExecutorService pingExecutor; + private List> telemetryCmdsHandlers; + private List> notificationCmdsHandlers; @PostConstruct - public void initExecutor() { + public void init() { serviceId = serviceInfoProvider.getServiceId(); executor = ThingsBoardExecutors.newWorkStealingPool(50, getClass()); pingExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("telemetry-web-socket-ping")); pingExecutor.scheduleWithFixedDelay(this::sendPing, pingTimeout / NUMBER_OF_PING_ATTEMPTS, pingTimeout / NUMBER_OF_PING_ATTEMPTS, TimeUnit.MILLISECONDS); + + telemetryCmdsHandlers = List.of( + WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getAttrSubCmds, this::handleWsAttributesSubscriptionCmd), + WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getTsSubCmds, this::handleWsTimeseriesSubscriptionCmd), + WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getHistoryCmds, this::handleWsHistoryCmd), + WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getEntityDataCmds, this::handleWsEntityDataCmd), + WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getAlarmDataCmds, this::handleWsAlarmDataCmd), + WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getEntityCountCmds, this::handleWsEntityCountCmd), + WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getEntityDataUnsubscribeCmds, this::handleWsDataUnsubscribeCmd), + WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getAlarmDataUnsubscribeCmds, this::handleWsDataUnsubscribeCmd), + WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getAlarmDataUnsubscribeCmds, this::handleWsDataUnsubscribeCmd), + WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getEntityCountUnsubscribeCmds, this::handleWsDataUnsubscribeCmd) + ); + notificationCmdsHandlers = List.of( + WsCmdHandler.of(NotificationCmdsWrapper::getUnreadSubCmd, notificationCmdsHandler::handleUnreadNotificationsSubCmd), + WsCmdHandler.of(NotificationCmdsWrapper::getUnreadCountSubCmd, notificationCmdsHandler::handleUnreadNotificationsCountSubCmd), + WsCmdHandler.of(NotificationCmdsWrapper::getMarkAsReadCmd, notificationCmdsHandler::handleMarkAsReadCmd), + WsCmdHandler.of(NotificationCmdsWrapper::getUnsubCmd, notificationCmdsHandler::handleUnsubCmd) + ); } @PreDestroy @@ -231,77 +234,30 @@ public class DefaultWebSocketService implements WebSocketService { } } + private void processTelemetryCmds(WebSocketSessionRef sessionRef, String msg) throws JsonProcessingException { TelemetryPluginCmdsWrapper cmdsWrapper = jsonMapper.readValue(msg, TelemetryPluginCmdsWrapper.class); if (cmdsWrapper == null) { return; } - if (cmdsWrapper.getAttrSubCmds() != null) { - cmdsWrapper.getAttrSubCmds().forEach(cmd -> { - if (processSubscription(sessionRef, cmd)) { - handleWsAttributesSubscriptionCmd(sessionRef, cmd); - } - }); - } - if (cmdsWrapper.getTsSubCmds() != null) { - cmdsWrapper.getTsSubCmds().forEach(cmd -> { - if (processSubscription(sessionRef, cmd)) { - handleWsTimeseriesSubscriptionCmd(sessionRef, cmd); - } - }); - } - if (cmdsWrapper.getHistoryCmds() != null) { - cmdsWrapper.getHistoryCmds().forEach(cmd -> handleWsHistoryCmd(sessionRef, cmd)); - } - if (cmdsWrapper.getEntityDataCmds() != null) { - cmdsWrapper.getEntityDataCmds().forEach(cmd -> handleWsEntityDataCmd(sessionRef, cmd)); - } - if (cmdsWrapper.getAlarmDataCmds() != null) { - cmdsWrapper.getAlarmDataCmds().forEach(cmd -> handleWsAlarmDataCmd(sessionRef, cmd)); - } - if (cmdsWrapper.getEntityCountCmds() != null) { - cmdsWrapper.getEntityCountCmds().forEach(cmd -> handleWsEntityCountCmd(sessionRef, cmd)); - } - if (cmdsWrapper.getEntityDataUnsubscribeCmds() != null) { - cmdsWrapper.getEntityDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); - } - if (cmdsWrapper.getAlarmDataUnsubscribeCmds() != null) { - cmdsWrapper.getAlarmDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); - } - if (cmdsWrapper.getEntityCountUnsubscribeCmds() != null) { - cmdsWrapper.getEntityCountUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); + for (WsCmdListHandler cmdHandler : telemetryCmdsHandlers) { + List cmds = cmdHandler.extractCmds(cmdsWrapper); + if (cmds != null) { + cmdHandler.handle(sessionRef, cmds); + } } } private void processNotificationCmds(WebSocketSessionRef sessionRef, String msg) throws IOException { NotificationCmdsWrapper cmdsWrapper = jsonMapper.readValue(msg, NotificationCmdsWrapper.class); - if (cmdsWrapper.getUnreadSubCmd() != null) { - handleUnreadNotificationsSubCmd(sessionRef, cmdsWrapper.getUnreadSubCmd()); - } else if (cmdsWrapper.getUnreadUnsubCmd() != null) { - handleUnreadNotificationsUnsubCmd(sessionRef, cmdsWrapper.getUnreadUnsubCmd()); - } else if (cmdsWrapper.getMarkAsReadCmd() != null) { - handleMarkNotificationAsReadCmd(sessionRef, cmdsWrapper.getMarkAsReadCmd()); - } - } - - private void handleUnreadNotificationsSubCmd(WebSocketSessionRef sessionRef, NotificationsSubCmd cmd) { - String sessionId = sessionRef.getSessionId(); - if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) { - notificationCmdsHandler.handleUnreadNotificationsSubCmd(sessionRef, cmd); - } - } - - private void handleUnreadNotificationsUnsubCmd(WebSocketSessionRef sessionRef, NotificationsUnsubCmd cmd) { - String sessionId = sessionRef.getSessionId(); - if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) { - notificationCmdsHandler.handleUnsubCmd(sessionRef, cmd); - } - } - - private void handleMarkNotificationAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationAsReadCmd cmd) { - String sessionId = sessionRef.getSessionId(); - if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) { - notificationCmdsHandler.handleMarkAsReadCmd(sessionRef, cmd); + for (WsCmdHandler cmdHandler : notificationCmdsHandlers) { + WsCmd cmd = cmdHandler.extractCmd(cmdsWrapper); + if (cmd != null) { + String sessionId = sessionRef.getSessionId(); + if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) { + cmdHandler.handle(sessionRef, cmd); // todo: handle exceptions + } + } } } @@ -479,6 +435,10 @@ public class DefaultWebSocketService implements WebSocketService { } private void handleWsAttributesSubscriptionCmd(WebSocketSessionRef sessionRef, AttributesSubscriptionCmd cmd) { + if (!processSubscription(sessionRef, cmd)) { + return; + } + String sessionId = sessionRef.getSessionId(); log.debug("[{}] Processing: {}", sessionId, cmd); @@ -644,6 +604,10 @@ public class DefaultWebSocketService implements WebSocketService { } private void handleWsTimeseriesSubscriptionCmd(WebSocketSessionRef sessionRef, TimeseriesSubscriptionCmd cmd) { + if (!processSubscription(sessionRef, cmd)) { + return; + } + String sessionId = sessionRef.getSessionId(); log.debug("[{}] Processing: {}", sessionId, cmd); @@ -990,4 +954,37 @@ public class DefaultWebSocketService implements WebSocketService { private int getLimit(int limit) { return limit == 0 ? DEFAULT_LIMIT : limit; } + + @RequiredArgsConstructor(staticName = "of") + public static class WsCmdHandler { + private final java.util.function.Function cmdExtractor; + private final BiConsumer handler; + + public C extractCmd(W cmdsWrapper) { + return cmdExtractor.apply(cmdsWrapper); + } + + @SuppressWarnings("unchecked") + public void handle(WebSocketSessionRef sessionRef, Object cmd) { + handler.accept(sessionRef, (C) cmd); + } + } + + @RequiredArgsConstructor(staticName = "of") + public static class WsCmdListHandler { + private final java.util.function.Function> cmdExtractor; + private final BiConsumer handler; + + public List extractCmds(W cmdsWrapper) { + return cmdExtractor.apply(cmdsWrapper); + } + + @SuppressWarnings("unchecked") + public void handle(WebSocketSessionRef sessionRef, List cmds) { + cmds.forEach(cmd -> { + handler.accept(sessionRef, (C) cmd); + }); + } + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/WsCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/WsCmd.java new file mode 100644 index 0000000000..096c5930a5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/WsCmd.java @@ -0,0 +1,20 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws; + +public interface WsCmd { + int getCmdId(); +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java index ad3f9ca4b0..ee0afd3c79 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java @@ -16,6 +16,8 @@ package org.thingsboard.server.service.ws.notification; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.id.IdBased; import org.thingsboard.server.common.data.id.NotificationId; @@ -28,14 +30,16 @@ import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.dao.notification.NotificationProcessingService; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.subscription.NotificationsSubscription; +import org.thingsboard.server.service.ws.notification.cmd.NotificationsCountSubCmd; +import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscription; import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; import org.thingsboard.server.service.ws.WebSocketSessionRef; import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd; import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; -import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; +import org.thingsboard.server.service.ws.notification.sub.NotificationsCountSubscription; import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; import java.util.Set; @@ -48,10 +52,11 @@ import java.util.stream.Collectors; public class DefaultNotificationCommandsHandler implements NotificationCommandsHandler { private final NotificationService notificationService; - private final WebSocketService wsService; private final TbLocalSubscriptionService localSubscriptionService; private final NotificationProcessingService notificationProcessingService; private final TbServiceInfoProvider serviceInfoProvider; + @Autowired @Lazy + private WebSocketService wsService; @Override public void handleUnreadNotificationsSubCmd(WebSocketSessionRef sessionRef, NotificationsSubCmd cmd) { @@ -62,7 +67,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH .subscriptionId(cmd.getCmdId()) .tenantId(user.getTenantId()) .entityId(user.getId()) - .updateProcessor(this::handleSubscriptionUpdate) + .updateProcessor(this::handleNotificationsSubscriptionUpdate) .limit(cmd.getLimit()) .build(); localSubscriptionService.addSubscription(subscription); @@ -72,14 +77,20 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH } @Override - public void handleMarkAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationAsReadCmd cmd) { - NotificationId notificationId = new NotificationId(cmd.getNotificationId()); - notificationProcessingService.markNotificationAsRead(sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), notificationId); - } + public void handleUnreadNotificationsCountSubCmd(WebSocketSessionRef sessionRef, NotificationsCountSubCmd cmd) { + SecurityUser user = sessionRef.getSecurityCtx(); + NotificationsCountSubscription subscription = NotificationsCountSubscription.builder() + .serviceId(serviceInfoProvider.getServiceId()) + .sessionId(sessionRef.getSessionId()) + .subscriptionId(cmd.getCmdId()) + .tenantId(user.getTenantId()) + .entityId(user.getId()) + .updateProcessor(this::handleNotificationsCountSubscriptionUpdate) + .build(); + localSubscriptionService.addSubscription(subscription); - @Override - public void handleUnsubCmd(WebSocketSessionRef sessionRef, UnsubscribeCmd cmd) { - localSubscriptionService.cancelSubscription(sessionRef.getSessionId(), cmd.getCmdId()); + fetchUnreadNotificationsCount(subscription); + sendUpdate(sessionRef.getSessionId(), subscription.createUpdate()); } private void fetchUnreadNotifications(NotificationsSubscription subscription) { @@ -87,19 +98,24 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH (UserId) subscription.getEntityId(), subscription.getLimit()); subscription.getUnreadNotifications().clear(); subscription.getUnreadNotifications().putAll(notifications.getData().stream().collect(Collectors.toMap(IdBased::getUuidId, n -> n))); - subscription.getTotalUnreadCount().set((int) notifications.getTotalElements()); + subscription.getTotalUnreadCounter().set((int) notifications.getTotalElements()); + } + + private void fetchUnreadNotificationsCount(NotificationsCountSubscription subscription) { + int unreadCount = notificationService.countUnreadNotificationsByUserId(subscription.getTenantId(), (UserId) subscription.getEntityId()); + subscription.getUnreadCounter().set(unreadCount); } - private void handleSubscriptionUpdate(NotificationsSubscription subscription, NotificationsSubscriptionUpdate subscriptionUpdate) { + private void handleNotificationsSubscriptionUpdate(NotificationsSubscription subscription, NotificationsSubscriptionUpdate subscriptionUpdate) { if (subscriptionUpdate.getNotification() != null) { Notification notification = subscriptionUpdate.getNotification(); if (notification.getStatus() == NotificationStatus.READ) { fetchUnreadNotifications(subscription); sendUpdate(subscription.getSessionId(), subscription.createFullUpdate()); } else { - Notification previous = subscription.getUnreadNotifications().put(notification.getUuidId(), notification); - if (previous == null) { - subscription.getTotalUnreadCount().incrementAndGet(); + subscription.getUnreadNotifications().put(notification.getUuidId(), notification); + if (subscriptionUpdate.isNewNotification()) { + subscription.getTotalUnreadCounter().incrementAndGet(); Set beyondLimit = subscription.getUnreadNotifications().keySet().stream() .skip(subscription.getLimit()) .collect(Collectors.toSet()); @@ -116,8 +132,35 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH } } - private void sendUpdate(String sessionId, UnreadNotificationsUpdate update) { + private void handleNotificationsCountSubscriptionUpdate(NotificationsCountSubscription subscription, NotificationsSubscriptionUpdate subscriptionUpdate) { + if (subscriptionUpdate.getNotification() != null) { + Notification notification = subscriptionUpdate.getNotification(); + if (subscriptionUpdate.isNewNotification()) { + subscription.getUnreadCounter().incrementAndGet(); + } else if (notification.getStatus() == NotificationStatus.READ) { + // for now this can only happen when user marks notification as read + subscription.getUnreadCounter().decrementAndGet(); + } + } else if (subscriptionUpdate.isNotificationRequestDeleted()) { + fetchUnreadNotificationsCount(subscription); + } + sendUpdate(subscription.getSessionId(), subscription.createUpdate()); + } + + private void sendUpdate(String sessionId, CmdUpdate update) { wsService.sendWsMsg(sessionId, update); } + + @Override + public void handleMarkAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationAsReadCmd cmd) { + NotificationId notificationId = new NotificationId(cmd.getNotificationId()); + notificationProcessingService.markNotificationAsRead(sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), notificationId); + } + + @Override + public void handleUnsubCmd(WebSocketSessionRef sessionRef, UnsubscribeCmd cmd) { + localSubscriptionService.cancelSubscription(sessionRef.getSessionId(), cmd.getCmdId()); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/NotificationCommandsHandler.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/NotificationCommandsHandler.java index b45e512045..4cf1fb3809 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/NotificationCommandsHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/NotificationCommandsHandler.java @@ -18,12 +18,15 @@ package org.thingsboard.server.service.ws.notification; import org.thingsboard.server.service.ws.WebSocketSessionRef; import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd; import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; +import org.thingsboard.server.service.ws.notification.cmd.NotificationsCountSubCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; public interface NotificationCommandsHandler { void handleUnreadNotificationsSubCmd(WebSocketSessionRef sessionRef, NotificationsSubCmd cmd); + void handleUnreadNotificationsCountSubCmd(WebSocketSessionRef sessionRef, NotificationsCountSubCmd cmd); + void handleMarkAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationAsReadCmd cmd); void handleUnsubCmd(WebSocketSessionRef sessionRef, UnsubscribeCmd cmd); diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationAsReadCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationAsReadCmd.java index 50844623eb..cfc6e8e38b 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationAsReadCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationAsReadCmd.java @@ -18,13 +18,14 @@ package org.thingsboard.server.service.ws.notification.cmd; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.thingsboard.server.service.ws.WsCmd; import java.util.UUID; @Data @NoArgsConstructor @AllArgsConstructor -public class MarkNotificationAsReadCmd { +public class MarkNotificationAsReadCmd implements WsCmd { private int cmdId; private UUID notificationId; } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java index 367a6cc450..011e8cecca 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java @@ -16,13 +16,15 @@ package org.thingsboard.server.service.ws.notification.cmd; import lombok.Data; -import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd; -import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; @Data public class NotificationCmdsWrapper { - private NotificationsSubCmd unreadSubCmd; - private NotificationsUnsubCmd unreadUnsubCmd; + private NotificationsCountSubCmd unreadCountSubCmd; + + private NotificationsSubCmd unreadSubCmd; private MarkNotificationAsReadCmd markAsReadCmd; + + private NotificationsUnsubCmd unsubCmd; + } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsCountSubCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsCountSubCmd.java new file mode 100644 index 0000000000..2722d6ae7d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsCountSubCmd.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws.notification.cmd; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.thingsboard.server.service.ws.WsCmd; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class NotificationsCountSubCmd implements WsCmd { + private int cmdId; +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java index 376b34b18e..087b250314 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java @@ -17,12 +17,14 @@ package org.thingsboard.server.service.ws.notification.cmd; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import org.thingsboard.server.service.ws.WsCmd; @Data @NoArgsConstructor @AllArgsConstructor -public class NotificationsSubCmd { +public class NotificationsSubCmd implements WsCmd { private int cmdId; private int limit; } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java index 1daeb570cc..b81e76e81d 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java @@ -15,10 +15,15 @@ */ package org.thingsboard.server.service.ws.notification.cmd; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.service.ws.WsCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; @Data -public class NotificationsUnsubCmd implements UnsubscribeCmd { +@NoArgsConstructor +@AllArgsConstructor +public class NotificationsUnsubCmd implements UnsubscribeCmd, WsCmd { private int cmdId; } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsCountUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsCountUpdate.java new file mode 100644 index 0000000000..c839b36e8d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsCountUpdate.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws.notification.cmd; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Getter; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdateType; + +@Getter +public class UnreadNotificationsCountUpdate extends CmdUpdate { + + private final int totalUnreadCount; + + @Builder + @JsonCreator + public UnreadNotificationsCountUpdate(@JsonProperty("cmdId") int cmdId, @JsonProperty("errorCode") int errorCode, + @JsonProperty("errorMsg") String errorMsg, + @JsonProperty("totalUnreadCount") int totalUnreadCount) { + super(cmdId, errorCode, errorMsg); + this.totalUnreadCount = totalUnreadCount; + } + + @Override + public CmdUpdateType getCmdUpdateType() { + return CmdUpdateType.NOTIFICATIONS_COUNT; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsCountSubscription.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsCountSubscription.java new file mode 100644 index 0000000000..ab06d90001 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsCountSubscription.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws.notification.sub; + +import lombok.Builder; +import lombok.Getter; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.service.subscription.TbSubscription; +import org.thingsboard.server.service.subscription.TbSubscriptionType; +import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsCountUpdate; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiConsumer; + +@Getter +public class NotificationsCountSubscription extends TbSubscription { + + private final AtomicInteger unreadCounter = new AtomicInteger(); + + @Builder + public NotificationsCountSubscription(String serviceId, String sessionId, int subscriptionId, TenantId tenantId, EntityId entityId, + BiConsumer updateProcessor) { + super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.NOTIFICATIONS_COUNT, updateProcessor); + } + + public UnreadNotificationsCountUpdate createUpdate() { + return UnreadNotificationsCountUpdate.builder() + .cmdId(getSubscriptionId()) + .totalUnreadCount(unreadCounter.get()) + .build(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/NotificationsSubscription.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java similarity index 85% rename from application/src/main/java/org/thingsboard/server/service/subscription/NotificationsSubscription.java rename to application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java index 00d23770e1..25d6e8725e 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/NotificationsSubscription.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.subscription; +package org.thingsboard.server.service.ws.notification.sub; import lombok.Builder; import lombok.Getter; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.service.subscription.TbSubscription; +import org.thingsboard.server.service.subscription.TbSubscriptionType; import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate; -import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; import java.util.LinkedHashMap; import java.util.Map; @@ -34,7 +35,7 @@ public class NotificationsSubscription extends TbSubscription unreadNotifications = new LinkedHashMap<>(); private final int limit; - private final AtomicInteger totalUnreadCount = new AtomicInteger(); + private final AtomicInteger totalUnreadCounter = new AtomicInteger(); @Builder public NotificationsSubscription(String serviceId, String sessionId, int subscriptionId, TenantId tenantId, EntityId entityId, @@ -48,7 +49,7 @@ public class NotificationsSubscription extends TbSubscription currentNotifications = new LinkedHashMap<>(); @Getter - private int totalUnreadCount; + private UnreadNotificationsUpdate lastDataUpdate; @Getter - private UnreadNotificationsUpdate lastUpdate; + private UnreadNotificationsCountUpdate lastCountUpdate; public NotificationsWebSocketClient(String wsUrl, String token) throws URISyntaxException { super(new URI(wsUrl + "/api/ws/plugins/notifications?token=" + token)); @@ -47,38 +47,36 @@ public class NotificationsWebSocketClient extends TbTestWebSocketClient { public void subscribeForUnreadNotifications(int limit) { NotificationCmdsWrapper cmdsWrapper = new NotificationCmdsWrapper(); - cmdsWrapper.setUnreadSubCmd(new NotificationsSubCmd(newCmdId(), limit)); + cmdsWrapper.setUnreadSubCmd(new NotificationsSubCmd(1, limit)); sendCmd(cmdsWrapper); } - public void markNotificationAsRead(UUID notificationId) { + public void subscribeForUnreadNotificationsCount() { NotificationCmdsWrapper cmdsWrapper = new NotificationCmdsWrapper(); - cmdsWrapper.setMarkAsReadCmd(new MarkNotificationAsReadCmd(newCmdId(), notificationId)); + cmdsWrapper.setUnreadCountSubCmd(new NotificationsCountSubCmd(2)); sendCmd(cmdsWrapper); } - - private void handleUpdate(UnreadNotificationsUpdate update) { - totalUnreadCount = update.getTotalUnreadCount(); - if (update.getNotifications() != null) { - currentNotifications.clear(); - currentNotifications.putAll(update.getNotifications().stream().collect(Collectors.toMap(IdBased::getUuidId, n -> n))); - } else if (update.getUpdate() != null) { - Notification notification = update.getUpdate(); - currentNotifications.put(notification.getUuidId(), notification); - } + public void markNotificationAsRead(UUID notificationId) { + NotificationCmdsWrapper cmdsWrapper = new NotificationCmdsWrapper(); + cmdsWrapper.setMarkAsReadCmd(new MarkNotificationAsReadCmd(newCmdId(), notificationId)); + sendCmd(cmdsWrapper); } - public void sendCmd(NotificationCmdsWrapper cmdsWrapper) { - send(JacksonUtil.toString(cmdsWrapper)); + String cmd = JacksonUtil.toString(cmdsWrapper); + send(cmd); } @Override public void onMessage(String s) { - UnreadNotificationsUpdate update = JacksonUtil.fromString(s, UnreadNotificationsUpdate.class); - lastUpdate = update; - handleUpdate(update); + JsonNode update = JacksonUtil.toJsonNode(s); + CmdUpdateType updateType = CmdUpdateType.valueOf(update.get("cmdUpdateType").asText()); + if (updateType == CmdUpdateType.NOTIFICATIONS) { + lastDataUpdate = JacksonUtil.treeToValue(update, UnreadNotificationsUpdate.class); + } else if (updateType == CmdUpdateType.NOTIFICATIONS_COUNT) { + lastCountUpdate = JacksonUtil.treeToValue(update, UnreadNotificationsCountUpdate.class); + } super.onMessage(s); } diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java index 8ff88b7e3c..6738b37a70 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.notification.targets.SingleUserNotific import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.controller.TbTestWebSocketClient; import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsCountUpdate; import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate; import java.net.URISyntaxException; @@ -41,6 +42,41 @@ public class NotificationsWsApiTest extends AbstractControllerTest { loginTenantAdmin(); } + @Test + public void testSubscribingToUnreadNotificationsCount() { + NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId); + String notificationText1 = "Notification 1"; + submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText1); + String notificationText2 = "Notification 2"; + submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText2); + + getWsClient().subscribeForUnreadNotificationsCount(); + getWsClient().waitForReply(); + + UnreadNotificationsCountUpdate update = getWsClient().getLastCountUpdate(); + assertThat(update.getTotalUnreadCount()).isEqualTo(2); + } + + @Test + public void testReceivingCountUpdates_multipleSessions() { + getWsClient().subscribeForUnreadNotificationsCount(); + getAnotherWsClient().subscribeForUnreadNotificationsCount(); + getWsClient().waitForReply(); + getAnotherWsClient().waitForReply(); + assertThat(getWsClient().getLastCountUpdate().getTotalUnreadCount()).isZero(); + + getWsClient().registerWaitForUpdate(); + getAnotherWsClient().registerWaitForUpdate(); + NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId); + String notificationText = "Notification"; + submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText); + getWsClient().waitForUpdate(); + getAnotherWsClient().waitForUpdate(); + + assertThat(getWsClient().getLastCountUpdate().getTotalUnreadCount()).isOne(); + assertThat(getAnotherWsClient().getLastCountUpdate().getTotalUnreadCount()).isOne(); + } + @Test public void testSubscribingToUnreadNotifications_multipleSessions() throws Exception { NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId); @@ -54,8 +90,8 @@ public class NotificationsWsApiTest extends AbstractControllerTest { getWsClient().waitForReply(); getAnotherWsClient().waitForReply(); - checkFullNotificationsUpdate(getWsClient().getLastUpdate(), notificationText1, notificationText2); - checkFullNotificationsUpdate(getAnotherWsClient().getLastUpdate(), notificationText1, notificationText2); + checkFullNotificationsUpdate(getWsClient().getLastDataUpdate(), notificationText1, notificationText2); + checkFullNotificationsUpdate(getAnotherWsClient().getLastDataUpdate(), notificationText1, notificationText2); } @Test @@ -64,7 +100,7 @@ public class NotificationsWsApiTest extends AbstractControllerTest { getAnotherWsClient().subscribeForUnreadNotifications(10); getWsClient().waitForReply(); getAnotherWsClient().waitForReply(); - UnreadNotificationsUpdate notificationsUpdate = getWsClient().getLastUpdate(); + UnreadNotificationsUpdate notificationsUpdate = getWsClient().getLastDataUpdate(); assertThat(notificationsUpdate.getTotalUnreadCount()).isZero(); getWsClient().registerWaitForUpdate(); @@ -75,8 +111,8 @@ public class NotificationsWsApiTest extends AbstractControllerTest { getWsClient().waitForUpdate(); getAnotherWsClient().waitForUpdate(); - checkPartialNotificationsUpdate(getWsClient().getLastUpdate(), notificationText, 1); - checkPartialNotificationsUpdate(getAnotherWsClient().getLastUpdate(), notificationText, 1); + checkPartialNotificationsUpdate(getWsClient().getLastDataUpdate(), notificationText, 1); + checkPartialNotificationsUpdate(getAnotherWsClient().getLastDataUpdate(), notificationText, 1); } @Test @@ -85,28 +121,37 @@ public class NotificationsWsApiTest extends AbstractControllerTest { getAnotherWsClient().subscribeForUnreadNotifications(10); getWsClient().waitForReply(); getAnotherWsClient().waitForReply(); + getAnotherWsClient().subscribeForUnreadNotificationsCount(); + getAnotherWsClient().waitForReply(); NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId); getWsClient().registerWaitForUpdate(); + getAnotherWsClient().registerWaitForUpdate(2); String notificationText1 = "Notification 1"; submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText1); getWsClient().waitForUpdate(); - Notification notification1 = getWsClient().getLastUpdate().getUpdate(); + getAnotherWsClient().waitForUpdate(); + Notification notification1 = getWsClient().getLastDataUpdate().getUpdate(); getWsClient().registerWaitForUpdate(); + getAnotherWsClient().registerWaitForUpdate(2); String notificationText2 = "Notification 2"; submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText2); getWsClient().waitForUpdate(); - assertThat(getWsClient().getLastUpdate().getTotalUnreadCount()).isEqualTo(2); + getAnotherWsClient().waitForUpdate(); + assertThat(getWsClient().getLastDataUpdate().getTotalUnreadCount()).isEqualTo(2); + assertThat(getAnotherWsClient().getLastDataUpdate().getTotalUnreadCount()).isEqualTo(2); + assertThat(getAnotherWsClient().getLastCountUpdate().getTotalUnreadCount()).isEqualTo(2); getWsClient().registerWaitForUpdate(); - getAnotherWsClient().registerWaitForUpdate(); + getAnotherWsClient().registerWaitForUpdate(2); getWsClient().markNotificationAsRead(notification1.getUuidId()); getWsClient().waitForUpdate(); getAnotherWsClient().waitForUpdate(); - checkFullNotificationsUpdate(getWsClient().getLastUpdate(), notificationText2); - checkFullNotificationsUpdate(getAnotherWsClient().getLastUpdate(), notificationText2); + checkFullNotificationsUpdate(getWsClient().getLastDataUpdate(), notificationText2); + checkFullNotificationsUpdate(getAnotherWsClient().getLastDataUpdate(), notificationText2); + assertThat(getAnotherWsClient().getLastCountUpdate().getTotalUnreadCount()).isOne(); } public void testReceivingUpdatesWhenSubscriptionAtAnotherInstance() {} @@ -127,7 +172,7 @@ public class NotificationsWsApiTest extends AbstractControllerTest { notificationTarget.setTenantId(tenantId); notificationTarget.setName("User " + userId); SingleUserNotificationTargetConfig config = new SingleUserNotificationTargetConfig(); - config.setUserId(userId); + config.setUserId(userId.getId()); notificationTarget.setConfiguration(config); return doPost("/api/notification/target", notificationTarget, NotificationTarget.class); } diff --git a/common/cluster-api/src/main/proto/queue.proto b/common/cluster-api/src/main/proto/queue.proto index 7fb764579d..c1a6dd342e 100644 --- a/common/cluster-api/src/main/proto/queue.proto +++ b/common/cluster-api/src/main/proto/queue.proto @@ -552,6 +552,10 @@ message NotificationsSubscriptionProto { int32 limit = 2; } +message NotificationsCountSubscriptionProto { + TbSubscriptionProto sub = 1; +} + message TbSubscriptionUpdateProto { string sessionId = 1; int32 subscriptionId = 2; @@ -573,6 +577,7 @@ message NotificationsSubscriptionUpdateProto { string sessionId = 1; int32 subscriptionId = 2; string notification = 3; + bool isNewNotification = 4; } message NotificationUpdateProto { @@ -581,6 +586,7 @@ message NotificationUpdateProto { int64 recipientIdMSB = 3; int64 recipientIdLSB = 4; string notification = 5; + bool isNew = 6; } message NotificationRequestDeleteProto { @@ -692,8 +698,9 @@ message SubscriptionMgrMsgProto { TbAlarmDeleteProto alarmDelete = 9; TbTimeSeriesDeleteProto tsDelete = 10; NotificationsSubscriptionProto notificationsSub = 11; - NotificationUpdateProto notificationUpdate = 12; - NotificationRequestDeleteProto notificationRequestDelete = 13; + NotificationsCountSubscriptionProto notificationsCountSub = 12; + NotificationUpdateProto notificationUpdate = 13; + NotificationRequestDeleteProto notificationRequestDelete = 14; } message LocalSubscriptionServiceMsgProto { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java index c8e1a7ea08..a4bebfd135 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java @@ -38,10 +38,14 @@ public interface NotificationService { Notification createNotification(TenantId tenantId, Notification notification); - Notification updateNotificationStatus(TenantId tenantId, NotificationId notificationId, NotificationStatus status); + Notification findNotificationById(TenantId tenantId, NotificationId notificationId); + + boolean updateNotificationStatus(TenantId tenantId, UserId userId, NotificationId notificationId, NotificationStatus status); PageData findNotificationsByUserIdAndReadStatusAndPageLink(TenantId tenantId, UserId userId, boolean unreadOnly, PageLink pageLink); PageData findLatestUnreadNotificationsByUserId(TenantId tenantId, UserId userId, int limit); + int countUnreadNotificationsByUserId(TenantId tenantId, UserId userId); + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java index f6cdf780e5..10c7155d97 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java @@ -15,15 +15,14 @@ */ package org.thingsboard.server.dao.notification; +import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import java.util.List; - public interface NotificationTargetService { NotificationTarget saveNotificationTarget(TenantId tenantId, NotificationTarget notificationTarget); @@ -32,7 +31,9 @@ public interface NotificationTargetService { PageData findNotificationTargetsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); - List findRecipientsForNotificationTarget(TenantId tenantId, NotificationTargetId notificationTargetId); + PageData findRecipientsForNotificationTarget(TenantId tenantId, NotificationTargetId notificationTargetId, PageLink pageLink); + + PageData findRecipientsForNotificationTargetConfig(TenantId tenantId, NotificationTargetConfig targetConfig, PageLink pageLink); void deleteNotificationTarget(TenantId tenantId, NotificationTargetId notificationTargetId); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java index 93da1ecd6c..41864b327d 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java @@ -57,6 +57,8 @@ public interface UserService { PageData findTenantAdmins(TenantId tenantId, PageLink pageLink); + PageData findUsers(TenantId tenantId, PageLink pageLink); + void deleteTenantAdmins(TenantId tenantId); PageData findCustomerUsers(TenantId tenantId, CustomerId customerId, PageLink pageLink); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java index 42cff008d0..dd800ef3fc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java @@ -21,11 +21,11 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.validation.NoXss; import javax.validation.Valid; @@ -37,7 +37,7 @@ import javax.validation.constraints.NotNull; @NoArgsConstructor @AllArgsConstructor @Builder -public class NotificationRequest extends BaseData implements HasTenantId { +public class NotificationRequest extends BaseData implements HasTenantId, HasName { private TenantId tenantId; @NotNull(message = "Target is not specified") @@ -55,4 +55,9 @@ public class NotificationRequest extends BaseData impleme public static final String GENERAL_NOTIFICATION_REASON = "General"; public static final String ALARM_NOTIFICATION_REASON = "Alarm"; + @Override + public String getName() { + return notificationReason; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/AllUsersNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/AllUsersNotificationTargetConfig.java new file mode 100644 index 0000000000..8e380cafa5 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/AllUsersNotificationTargetConfig.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.targets; + +import lombok.Data; + +@Data +public class AllUsersNotificationTargetConfig implements NotificationTargetConfig { + + @Override + public NotificationTargetConfigType getType() { + return NotificationTargetConfigType.ALL_USERS; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/CustomerUsersNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/CustomerUsersNotificationTargetConfig.java new file mode 100644 index 0000000000..90e913c17e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/CustomerUsersNotificationTargetConfig.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.targets; + +import lombok.Data; + +import java.util.UUID; + +@Data +public class CustomerUsersNotificationTargetConfig implements NotificationTargetConfig { + + private UUID customerId; + + @Override + public NotificationTargetConfigType getType() { + return NotificationTargetConfigType.CUSTOMER_USERS; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java index 0623bf07b9..e3fb02bf4a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java @@ -24,7 +24,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ @Type(value = SingleUserNotificationTargetConfig.class, name = "SINGLE_USER"), - @Type(value = UserListNotificationTargetConfig.class, name = "USER_LIST") + @Type(value = UserListNotificationTargetConfig.class, name = "USER_LIST"), + @Type(value = CustomerUsersNotificationTargetConfig.class, name = "CUSTOMER_USERS"), + @Type(value = AllUsersNotificationTargetConfig.class, name = "ALL_USERS") }) public interface NotificationTargetConfig { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java index 2413eb9326..b7694c9de8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java @@ -19,6 +19,9 @@ public enum NotificationTargetConfigType { SINGLE_USER, USER_LIST, + CUSTOMER_USERS, + ALL_USERS + // USER_GROUP, // USERS_WITH_ROLE, // QUERY // ? diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/SingleUserNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/SingleUserNotificationTargetConfig.java index 9fbb6ca600..12dc7c59ce 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/SingleUserNotificationTargetConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/SingleUserNotificationTargetConfig.java @@ -16,15 +16,15 @@ package org.thingsboard.server.common.data.notification.targets; import lombok.Data; -import org.thingsboard.server.common.data.id.UserId; import javax.validation.constraints.NotNull; +import java.util.UUID; @Data public class SingleUserNotificationTargetConfig implements NotificationTargetConfig { @NotNull - private UserId userId; + private UUID userId; @Override public NotificationTargetConfigType getType() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/UserListNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/UserListNotificationTargetConfig.java index 22bc26703d..ea05948ff6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/UserListNotificationTargetConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/UserListNotificationTargetConfig.java @@ -18,12 +18,15 @@ package org.thingsboard.server.common.data.notification.targets; import lombok.Data; import org.thingsboard.server.common.data.id.UserId; +import javax.validation.constraints.NotEmpty; import java.util.List; +import java.util.UUID; @Data public class UserListNotificationTargetConfig implements NotificationTargetConfig { - private List usersIds; + @NotEmpty + private List usersIds; @Override public NotificationTargetConfigType getType() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java index 4be3f8e6ac..4e82583ba2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java @@ -110,13 +110,17 @@ public abstract class DaoUtil { } public static void processInBatches(Function> finder, int batchSize, Consumer processor) { + processBatches(finder, batchSize, batch -> batch.forEach(processor)); + } + + public static void processBatches(Function> finder, int batchSize, Consumer> processor) { PageLink pageLink = new PageLink(batchSize); PageData batch; boolean hasNextBatch; do { batch = finder.apply(pageLink); - batch.getData().forEach(processor); + processor.accept(batch.getData()); hasNextBatch = batch.hasNext(); pageLink = pageLink.nextPageLink(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java index e6e69fed22..c15480c747 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java @@ -81,13 +81,17 @@ public class DefaultNotificationService implements NotificationService { return notificationDao.save(tenantId, notification); } - @Transactional @Override - public Notification updateNotificationStatus(TenantId tenantId, NotificationId notificationId, NotificationStatus status) { - notificationDao.updateStatus(tenantId, notificationId, status); + public Notification findNotificationById(TenantId tenantId, NotificationId notificationId) { return notificationDao.findById(tenantId, notificationId.getId()); } + @Transactional + @Override + public boolean updateNotificationStatus(TenantId tenantId, UserId userId, NotificationId notificationId, NotificationStatus status) { + return notificationDao.updateStatusByIdAndUserId(tenantId, userId, notificationId, status); + } + @Override public PageData findNotificationsByUserIdAndReadStatusAndPageLink(TenantId tenantId, UserId userId, boolean unreadOnly, PageLink pageLink) { if (unreadOnly) { @@ -104,6 +108,11 @@ public class DefaultNotificationService implements NotificationService { return findNotificationsByUserIdAndReadStatusAndPageLink(tenantId, userId, true, pageLink); } + @Override + public int countUnreadNotificationsByUserId(TenantId tenantId, UserId userId) { + return notificationDao.countUnreadByUserId(tenantId, userId); + } + private static class NotificationRequestValidator extends DataValidator { @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java index 52f9f8b61b..00892576e4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java @@ -18,9 +18,12 @@ package org.thingsboard.server.dao.notification; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.targets.CustomerUsersNotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.SingleUserNotificationTargetConfig; @@ -28,9 +31,10 @@ import org.thingsboard.server.common.data.notification.targets.UserListNotificat import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.user.UserService; -import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @Service @Slf4j @@ -38,11 +42,11 @@ import java.util.List; public class DefaultNotificationTargetService implements NotificationTargetService { private final NotificationTargetDao notificationTargetDao; + private final UserService userService; private final NotificationTargetValidator validator = new NotificationTargetValidator(); @Override public NotificationTarget saveNotificationTarget(TenantId tenantId, NotificationTarget notificationTarget) { - notificationTarget.setTenantId(tenantId); validator.validate(notificationTarget, NotificationTarget::getTenantId); return notificationTargetDao.save(tenantId, notificationTarget); } @@ -58,21 +62,42 @@ public class DefaultNotificationTargetService implements NotificationTargetServi } @Override - public List findRecipientsForNotificationTarget(TenantId tenantId, NotificationTargetId notificationTargetId) { + public PageData findRecipientsForNotificationTarget(TenantId tenantId, NotificationTargetId notificationTargetId, PageLink pageLink) { NotificationTarget notificationTarget = findNotificationTargetById(tenantId, notificationTargetId); NotificationTargetConfig configuration = notificationTarget.getConfiguration(); - List recipients = new ArrayList<>(); - switch (configuration.getType()) { - case SINGLE_USER: - SingleUserNotificationTargetConfig singleUserNotificationTargetConfig = (SingleUserNotificationTargetConfig) configuration; - recipients.add(singleUserNotificationTargetConfig.getUserId()); - break; - case USER_LIST: - UserListNotificationTargetConfig userListNotificationTargetConfig = (UserListNotificationTargetConfig) configuration; - recipients.addAll(userListNotificationTargetConfig.getUsersIds()); - break; + return findRecipientsForNotificationTargetConfig(tenantId, configuration, pageLink); + } + + @Override + public PageData findRecipientsForNotificationTargetConfig(TenantId tenantId, NotificationTargetConfig targetConfig, PageLink pageLink) { + switch (targetConfig.getType()) { + case SINGLE_USER: { + UserId userId = new UserId(((SingleUserNotificationTargetConfig) targetConfig).getUserId()); + User user = userService.findUserById(tenantId, userId); + return new PageData<>(List.of(user), 1, 1, false); + } + case USER_LIST: { + List users = ((UserListNotificationTargetConfig) targetConfig).getUsersIds().stream() + .map(UserId::new).map(userId -> userService.findUserById(tenantId, userId)) + .collect(Collectors.toList()); + return new PageData<>(users, 1, users.size(), false); + } + case CUSTOMER_USERS: { + if (tenantId.equals(TenantId.SYS_TENANT_ID)) { + throw new IllegalArgumentException("Customer users target is not supported for system administrator"); + } + CustomerId customerId = new CustomerId(((CustomerUsersNotificationTargetConfig) targetConfig).getCustomerId()); + return userService.findCustomerUsers(tenantId, customerId, pageLink); + } + case ALL_USERS: { + if (!tenantId.equals(TenantId.SYS_TENANT_ID)) { + return userService.findUsersByTenantId(tenantId, pageLink); + } else { + return userService.findUsers(TenantId.SYS_TENANT_ID, pageLink); + } + } } - return recipients; + return new PageData<>(); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java index b088d75c35..c16df8819a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java @@ -30,6 +30,8 @@ public interface NotificationDao extends Dao { PageData findByUserIdAndPageLink(TenantId tenantId, UserId userId, PageLink pageLink); - void updateStatus(TenantId tenantId, NotificationId notificationId, NotificationStatus status); + boolean updateStatusByIdAndUserId(TenantId tenantId, UserId userId, NotificationId notificationId, NotificationStatus status); + + int countUnreadByUserId(TenantId tenantId, UserId userId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java index bb9c30a71b..2611c32c90 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java @@ -73,8 +73,13 @@ public class JpaNotificationDao extends JpaAbstractDao :status") + int updateStatusByIdAndRecipientId(@Param("id") UUID id, + @Param("recipientId") UUID recipientId, + @Param("status") NotificationStatus status); + + int countByRecipientIdAndStatusNot(UUID recipientId, NotificationStatus status); void deleteByRequestId(UUID requestId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java index ad8cac6a28..b566395eb8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java @@ -95,6 +95,11 @@ public class JpaUserDao extends JpaAbstractSearchTextDao imple } + @Override + public PageData findAll(TenantId tenantId, PageLink pageLink) { + return DaoUtil.toPageData(userRepository.findAll(DaoUtil.toPageable(pageLink))); + } + @Override public Long countByTenantId(TenantId tenantId) { return userRepository.countByTenantId(tenantId.getId()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java index b906b3278d..081e30b525 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java @@ -69,4 +69,7 @@ public interface UserDao extends Dao, TenantEntityDao { * @return the list of user entities */ PageData findCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink); + + PageData findAll(TenantId tenantId, PageLink pageLink); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index fc0de8a4fa..dce9f772bf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -230,6 +230,11 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic return userDao.findTenantAdmins(tenantId.getId(), pageLink); } + @Override + public PageData findUsers(TenantId tenantId, PageLink pageLink) { + return userDao.findAll(tenantId, pageLink); + } + @Override public void deleteTenantAdmins(TenantId tenantId) { log.trace("Executing deleteTenantAdmins, tenantId [{}]", tenantId); diff --git a/dao/src/main/resources/sql/schema-entities-idx.sql b/dao/src/main/resources/sql/schema-entities-idx.sql index 7bd4d69f5a..8f43b1ac4e 100644 --- a/dao/src/main/resources/sql/schema-entities-idx.sql +++ b/dao/src/main/resources/sql/schema-entities-idx.sql @@ -78,6 +78,8 @@ CREATE INDEX IF NOT EXISTS idx_notification_target_tenant_id_and_created_time ON CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id_and_created_time ON notification_request(tenant_id, created_time DESC); +CREATE INDEX IF NOT EXISTS idx_notification_id ON notification(id); + CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_and_created_time ON notification(recipient_id, created_time DESC); CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_and_status_and_created_time ON notification(recipient_id, status, created_time DESC); From 12ae902cfc7b982ca442e7667d88ff7d3fd5856c Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Sun, 6 Nov 2022 18:13:02 +0200 Subject: [PATCH 018/488] Notification rules initial implementation --- .../main/data/upgrade/3.4.2/schema_update.sql | 11 +- .../server/actors/ActorSystemContext.java | 7 +- .../actors/ruleChain/DefaultTbContext.java | 6 +- .../controller/NotificationController.java | 17 ++- ...aultNotificationRuleProcessingService.java | 138 ++++++++++++++++++ ...faultNotificationSubscriptionService.java} | 82 +++++++---- .../NotificationRuleProcessingService.java | 28 ++++ .../NotificationSubscriptionService.java | 6 +- .../queue/DefaultTbCoreConsumerService.java | 44 +++--- .../DefaultSubscriptionManagerService.java | 23 ++- .../SubscriptionManagerService.java | 8 +- .../subscription/TbSubscriptionUtils.java | 31 ++-- .../AbstractSubscriptionService.java | 6 +- .../DefaultAlarmSubscriptionService.java | 8 +- .../DefaultTelemetrySubscriptionService.java | 8 +- .../DefaultNotificationCommandsHandler.java | 100 +++++++++---- .../sub/NotificationRequestUpdate.java | 33 +++++ .../notification/sub/NotificationUpdate.java | 31 ++++ .../sub/NotificationsSubscriptionUpdate.java | 19 ++- common/cluster-api/src/main/proto/queue.proto | 14 +- .../notification/NotificationRuleService.java | 26 ++++ .../dao/notification/NotificationService.java | 22 ++- .../server/common/data/alarm/Alarm.java | 3 + .../device/profile/DeviceProfileAlarm.java | 3 + .../common/data/id/NotificationRuleId.java | 36 +++++ .../data/notification/NotificationInfo.java | 21 ++- .../notification/NotificationRequest.java | 11 ++ .../NotificationRequestConfig.java | 2 +- .../NotificationRequestStatus.java | 21 +++ .../NonConfirmedNotificationEscalation.java | 15 +- .../notification/rule/NotificationRule.java | 31 ++-- .../server/dao/model/ModelConstants.java | 7 + .../dao/model/sql/AbstractAlarmEntity.java | 8 + .../model/sql/NotificationRequestEntity.java | 19 +++ .../dao/model/sql/NotificationRuleEntity.java | 65 +++++++++ .../DefaultNotificationRuleService.java | 35 +++++ .../DefaultNotificationService.java | 40 ++--- .../dao/notification/NotificationDao.java | 6 + .../notification/NotificationRequestDao.java | 6 + .../dao/notification/NotificationRuleDao.java | 22 +++ .../sql/notification/JpaNotificationDao.java | 13 ++ .../JpaNotificationRequestDao.java | 8 + .../notification/JpaNotificationRuleDao.java | 46 ++++++ .../notification/NotificationRepository.java | 8 +- .../NotificationRequestRepository.java | 3 + .../NotificationRuleRepository.java | 26 ++++ .../resources/sql/schema-entities-idx.sql | 4 +- .../main/resources/sql/schema-entities.sql | 5 +- .../api/RuleEngineNotificationService.java | 25 ++++ .../rule/engine/api/TbContext.java | 3 +- .../notification/TbNotificationNode.java | 2 +- .../rule/engine/profile/AlarmState.java | 1 + .../rule/engine/profile/DeviceState.java | 1 + 53 files changed, 964 insertions(+), 200 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java rename application/src/main/java/org/thingsboard/server/service/notification/{DefaultNotificationProcessingService.java => DefaultNotificationSubscriptionService.java} (61%) create mode 100644 application/src/main/java/org/thingsboard/server/service/notification/NotificationRuleProcessingService.java rename common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationProcessingService.java => application/src/main/java/org/thingsboard/server/service/notification/NotificationSubscriptionService.java (86%) create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationRequestUpdate.java create mode 100644 application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationUpdate.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRuleId.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStatus.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java create mode 100644 rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineNotificationService.java diff --git a/application/src/main/data/upgrade/3.4.2/schema_update.sql b/application/src/main/data/upgrade/3.4.2/schema_update.sql index 77f078e67f..7294916445 100644 --- a/application/src/main/data/upgrade/3.4.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.4.2/schema_update.sql @@ -24,6 +24,10 @@ CREATE TABLE IF NOT EXISTS notification_target ( ); CREATE INDEX IF NOT EXISTS idx_notification_target_tenant_id_and_created_time ON notification_target(tenant_id, created_time DESC); +CREATE TABLE IF NOT EXISTS notification_rule ( + id UUID NOT NULL CONSTRAINT notification_rule_pkey PRIMARY KEY +); + CREATE TABLE IF NOT EXISTS notification_request ( id UUID NOT NULL CONSTRAINT notification_request_pkey PRIMARY KEY, created_time BIGINT NOT NULL, @@ -33,7 +37,10 @@ CREATE TABLE IF NOT EXISTS notification_request ( text_template VARCHAR NOT NULL, notification_info VARCHAR(1000), notification_severity VARCHAR(32), - additional_config VARCHAR(1000) + additional_config VARCHAR(1000), + status VARCHAR(32), + rule_id UUID NULL CONSTRAINT fk_notification_request_rule_id REFERENCES notification_rule(id), + alarm_id UUID ); CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id_and_created_time ON notification_request(tenant_id, created_time DESC); @@ -50,5 +57,5 @@ CREATE TABLE IF NOT EXISTS notification ( status VARCHAR(32) ) PARTITION BY RANGE (created_time); CREATE INDEX IF NOT EXISTS idx_notification_id ON notification(id); +CREATE INDEX IF NOT EXISTS idx_notification_notification_request_id ON notification(request_id); CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_and_created_time ON notification(recipient_id, created_time DESC); -CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_and_status_and_created_time ON notification(recipient_id, status, created_time DESC); diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index 2a574f5788..27d3d401ff 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -15,9 +15,7 @@ */ package org.thingsboard.server.actors; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -32,6 +30,7 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.thingsboard.rule.engine.api.MailService; +import org.thingsboard.rule.engine.api.RuleEngineNotificationService; import org.thingsboard.rule.engine.api.SmsService; import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; import org.thingsboard.server.actors.service.ActorService; @@ -64,7 +63,6 @@ import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.nosql.CassandraBufferedRateReadExecutor; import org.thingsboard.server.dao.nosql.CassandraBufferedRateWriteExecutor; -import org.thingsboard.server.dao.notification.NotificationProcessingService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; @@ -106,7 +104,6 @@ import javax.annotation.PostConstruct; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; -import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ScheduledExecutorService; @@ -307,7 +304,7 @@ public class ActorSystemContext { @Autowired @Getter - private NotificationProcessingService notificationProcessingService; + private RuleEngineNotificationService notificationService; @Lazy @Autowired(required = false) diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index c0197a0f89..82010dc984 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -27,6 +27,7 @@ import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.RuleEngineAlarmService; import org.thingsboard.rule.engine.api.RuleEngineAssetProfileCache; import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache; +import org.thingsboard.rule.engine.api.RuleEngineNotificationService; import org.thingsboard.rule.engine.api.RuleEngineRpcService; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.ScriptEngine; @@ -79,7 +80,6 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.nosql.CassandraStatementTask; import org.thingsboard.server.dao.nosql.TbResultSetFuture; -import org.thingsboard.server.dao.notification.NotificationProcessingService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; @@ -632,8 +632,8 @@ class DefaultTbContext implements TbContext { } @Override - public NotificationProcessingService getNotificationProcessingService() { - return mainCtx.getNotificationProcessingService(); + public RuleEngineNotificationService getNotificationService() { + return mainCtx.getNotificationService(); } @Override diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index 1bf771218a..a4f44b589d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -39,7 +39,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.dao.notification.NotificationProcessingService; +import org.thingsboard.server.service.notification.NotificationSubscriptionService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; @@ -54,7 +54,7 @@ import java.util.UUID; public class NotificationController extends BaseController { private final NotificationService notificationService; - private final NotificationProcessingService notificationProcessingService; + private final NotificationSubscriptionService notificationSubscriptionService; @GetMapping("/notifications") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -66,7 +66,7 @@ public class NotificationController extends BaseController { @RequestParam(defaultValue = "false") boolean unreadOnly, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return notificationService.findNotificationsByUserIdAndReadStatusAndPageLink(user.getTenantId(), user.getId(), unreadOnly, pageLink); + return notificationService.findNotificationsByUserIdAndReadStatus(user.getTenantId(), user.getId(), unreadOnly, pageLink); } @PutMapping("/notification/{id}/read") // or maybe to NotificationUpdateRequest for the future @@ -74,7 +74,7 @@ public class NotificationController extends BaseController { public void markNotificationAsRead(@PathVariable UUID id, @AuthenticationPrincipal SecurityUser user) { NotificationId notificationId = new NotificationId(id); - notificationProcessingService.markNotificationAsRead(user.getTenantId(), user.getId(), notificationId); + notificationSubscriptionService.markNotificationAsRead(user.getTenantId(), user.getId(), notificationId); } // delete notification? @@ -84,8 +84,11 @@ public class NotificationController extends BaseController { public NotificationRequest createNotificationRequest(@RequestBody NotificationRequest notificationRequest, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { accessControlService.checkPermission(user, Resource.NOTIFICATION_REQUEST, Operation.CREATE, null, notificationRequest); + if (notificationRequest.getId() != null) { + throw new IllegalArgumentException("Notification request cannot be changed. You can delete it and create a new one"); + } try { - NotificationRequest savedNotificationRequest = notificationProcessingService.processNotificationRequest(user.getTenantId(), notificationRequest); + NotificationRequest savedNotificationRequest = notificationSubscriptionService.processNotificationRequest(user.getTenantId(), notificationRequest); logEntityAction(user, EntityType.NOTIFICATION_REQUEST, savedNotificationRequest, ActionType.ADDED); return savedNotificationRequest; } catch (Exception e) { @@ -111,7 +114,7 @@ public class NotificationController extends BaseController { @RequestParam(required = false) String sortOrder, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return notificationService.findNotificationRequestsByTenantIdAndPageLink(user.getTenantId(), pageLink); + return notificationService.findNotificationRequestsByTenantId(user.getTenantId(), pageLink); } @DeleteMapping("/notification/request/{id}") @@ -121,7 +124,7 @@ public class NotificationController extends BaseController { NotificationRequest notificationRequest = notificationService.findNotificationRequestById(user.getTenantId(), notificationRequestId); accessControlService.checkPermission(user, Resource.NOTIFICATION_REQUEST, Operation.DELETE, notificationRequestId, notificationRequest); try { - notificationProcessingService.deleteNotificationRequest(user.getTenantId(), notificationRequestId); + notificationSubscriptionService.deleteNotificationRequest(user.getTenantId(), notificationRequestId); logEntityAction(user, EntityType.NOTIFICATION_REQUEST, notificationRequest, ActionType.DELETED); } catch (Exception e) { logEntityAction(user, EntityType.NOTIFICATION_REQUEST, notificationRequest, notificationRequest, ActionType.DELETED, e); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java new file mode 100644 index 0000000000..7c459b2e80 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java @@ -0,0 +1,138 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.notification; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.id.NotificationRuleId; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.NotificationInfo; +import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationRequestConfig; +import org.thingsboard.server.common.data.notification.NotificationRequestStatus; +import org.thingsboard.server.common.data.notification.NotificationSeverity; +import org.thingsboard.server.common.data.notification.rule.NonConfirmedNotificationEscalation; +import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.dao.notification.NotificationRuleService; +import org.thingsboard.server.dao.notification.NotificationService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.executors.DbCallbackExecutorService; + +import java.util.List; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class DefaultNotificationRuleProcessingService implements NotificationRuleProcessingService { + + private final NotificationRuleService notificationRuleService; + private final NotificationService notificationService; + private final NotificationSubscriptionService notificationSubscriptionService; + private final DbCallbackExecutorService dbCallbackExecutorService; + + @Override + public ListenableFuture onAlarmCreatedOrUpdated(TenantId tenantId, Alarm alarm) { + return processAlarmUpdate(tenantId, alarm); + } + + @Override + public ListenableFuture onAlarmAcknowledged(TenantId tenantId, Alarm alarm) { + return processAlarmUpdate(tenantId, alarm); + } + + private ListenableFuture processAlarmUpdate(TenantId tenantId, Alarm alarm) { + if (alarm.getNotificationRuleId() == null) return Futures.immediateFuture(null); + return dbCallbackExecutorService.submit(() -> { + onAlarmUpdate(tenantId, alarm.getNotificationRuleId(), alarm); + }); + } + + private void onAlarmUpdate(TenantId tenantId, NotificationRuleId notificationRuleId, Alarm alarm) { + List notificationRequests = notificationService.findNotificationRequestsByRuleIdAndAlarmId(tenantId, notificationRuleId, alarm.getId()); + NotificationRule notificationRule = notificationRuleService.findNotificationRuleById(tenantId, notificationRuleId); + + if (notificationRequests.isEmpty()) { // in case it is first notification for alarm, or it was previously acked and now we need to send notifications again + NotificationTargetId initialNotificationTargetId = notificationRule.getInitialNotificationTargetId(); + submitNotificationRequest(tenantId, initialNotificationTargetId, notificationRule, alarm, 0); + + for (NonConfirmedNotificationEscalation escalation : notificationRule.getEscalations()) { + submitNotificationRequest(tenantId, escalation.getNotificationTargetId(), notificationRule, alarm, escalation.getDelayInMinutes()); + } + } else { + if (alarmAcknowledged(alarm)) { + for (NotificationRequest notificationRequest : notificationRequests) { + if (notificationRequest.getStatus() == NotificationRequestStatus.SCHEDULED) { + // using regular service due to no need to send an update to subscription manager + notificationService.deleteNotificationRequestById(tenantId, notificationRequest.getId()); + } else { + notificationSubscriptionService.deleteNotificationRequest(tenantId, notificationRequest.getId()); + // todo: or should we mark already sent notifications as read? + } + } + } else { + NotificationInfo newNotificationInfo = constructNotificationInfo(alarm, notificationRule); + for (NotificationRequest notificationRequest : notificationRequests) { + NotificationInfo previousNotificationInfo = notificationRequest.getNotificationInfo(); + if (!previousNotificationInfo.equals(newNotificationInfo)) { + notificationRequest.setNotificationInfo(newNotificationInfo); + notificationSubscriptionService.updateNotificationRequest(tenantId, notificationRequest); + } + // fixme: no need to send an update event for scheduled requests, only for sent + } + } + } + } + + private boolean alarmAcknowledged(Alarm alarm) { // todo: decide when to consider the alarm processed by notification target (not to escalate then) + return alarm.getStatus().isAck() && alarm.getStatus().isCleared(); + } + + private void submitNotificationRequest(TenantId tenantId, NotificationTargetId targetId, NotificationRule notificationRule, Alarm alarm, int delayInMinutes) { + NotificationRequestConfig config = new NotificationRequestConfig(); + if (delayInMinutes > 0) { + config.setSendingDelayInMinutes(delayInMinutes); + } + NotificationInfo notificationInfo = constructNotificationInfo(alarm, notificationRule); + + NotificationRequest notificationRequest = NotificationRequest.builder() + .tenantId(tenantId) + .targetId(targetId) + .notificationReason("Alarm") + .textTemplate(notificationRule.getNotificationTextTemplate()) // todo: format with alarm vars + .notificationInfo(notificationInfo) + .notificationSeverity(NotificationSeverity.NORMAL) // todo: from alarm severity + .additionalConfig(config) + .ruleId(notificationRule.getId()) + .alarmId(alarm.getId()) + .build(); + notificationSubscriptionService.processNotificationRequest(tenantId, notificationRequest); + } + + private NotificationInfo constructNotificationInfo(Alarm alarm, NotificationRule notificationRule) { + return NotificationInfo.builder() + .alarmId(alarm.getId()) + .alarmType(alarm.getType()) + .alarmOriginator(alarm.getOriginator()) + .alarmSeverity(alarm.getSeverity()) + .alarmStatus(alarm.getStatus()) + .build(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSubscriptionService.java similarity index 61% rename from application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java rename to application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSubscriptionService.java index 99ede1fba9..8709f76179 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSubscriptionService.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.notification; import com.google.common.base.Strings; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.thingsboard.rule.engine.api.RuleEngineNotificationService; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.User; @@ -28,12 +29,12 @@ import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequestConfig; +import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.notification.NotificationProcessingService; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.gen.transport.TransportProtos; @@ -42,6 +43,8 @@ import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import org.thingsboard.server.service.telemetry.AbstractSubscriptionService; +import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; +import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; import java.util.HashSet; import java.util.Map; @@ -50,18 +53,18 @@ import java.util.UUID; @Service @Slf4j -public class DefaultNotificationProcessingService extends AbstractSubscriptionService implements NotificationProcessingService { +public class DefaultNotificationSubscriptionService extends AbstractSubscriptionService implements NotificationSubscriptionService, RuleEngineNotificationService { private final NotificationTargetService notificationTargetService; private final NotificationService notificationService; private final DbCallbackExecutorService dbCallbackExecutorService; private final NotificationsTopicService notificationsTopicService; - public DefaultNotificationProcessingService(TbClusterService clusterService, PartitionService partitionService, - NotificationTargetService notificationTargetService, - NotificationService notificationService, - DbCallbackExecutorService dbCallbackExecutorService, - NotificationsTopicService notificationsTopicService) { + public DefaultNotificationSubscriptionService(TbClusterService clusterService, PartitionService partitionService, + NotificationTargetService notificationTargetService, + NotificationService notificationService, + DbCallbackExecutorService dbCallbackExecutorService, + NotificationsTopicService notificationsTopicService) { super(clusterService, partitionService); this.notificationTargetService = notificationTargetService; this.notificationService = notificationService; @@ -72,13 +75,20 @@ public class DefaultNotificationProcessingService extends AbstractSubscriptionSe @Override public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { notificationRequest.setTenantId(tenantId); - NotificationRequest savedNotificationRequest = notificationService.createNotificationRequest(tenantId, notificationRequest); - if (notificationRequest.getAdditionalConfig() != null) { NotificationRequestConfig config = notificationRequest.getAdditionalConfig(); - // todo: delayed sending; check all delayed notification requests on start up, schedule send + if (config.getSendingDelayInMinutes() > 0) { + notificationRequest.setStatus(NotificationRequestStatus.SCHEDULED); + + // todo: delayed sending; check all delayed notification requests on start up, schedule send + } + } + if (notificationRequest.getStatus() == null) { + notificationRequest.setStatus(NotificationRequestStatus.PROCESSED); } + NotificationRequest savedNotificationRequest = notificationService.saveNotificationRequest(tenantId, notificationRequest); + DaoUtil.processBatches(pageLink -> { return notificationTargetService.findRecipientsForNotificationTarget(tenantId, notificationRequest.getTargetId(), pageLink); }, 100, recipients -> { @@ -99,7 +109,7 @@ public class DefaultNotificationProcessingService extends AbstractSubscriptionSe @Override public void markNotificationAsRead(TenantId tenantId, UserId recipientId, NotificationId notificationId) { - boolean updated = notificationService.updateNotificationStatus(tenantId, recipientId, notificationId, NotificationStatus.READ); + boolean updated = notificationService.markNotificationAsRead(tenantId, recipientId, notificationId); if (updated) { Notification notification = notificationService.findNotificationById(tenantId, notificationId); onNotificationUpdate(tenantId, recipientId, notification, false); @@ -108,8 +118,22 @@ public class DefaultNotificationProcessingService extends AbstractSubscriptionSe @Override public void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId) { - notificationService.deleteNotificationRequest(tenantId, notificationRequestId); - onNotificationRequestDeleted(tenantId, notificationRequestId); + notificationService.deleteNotificationRequestById(tenantId, notificationRequestId); + onNotificationRequestUpdate(tenantId, NotificationRequestUpdate.builder() + .notificationRequestId(notificationRequestId) + .deleted(true) + .build()); + } + + @Override + public void updateNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { + notificationService.saveNotificationRequest(tenantId, notificationRequest); + notificationService.updateNotificationsInfosByRequestId(tenantId, notificationRequest.getId(), notificationRequest.getNotificationInfo()); + onNotificationRequestUpdate(tenantId, NotificationRequestUpdate.builder() + .notificationRequestId(notificationRequest.getId()) + .notificationInfo(notificationRequest.getNotificationInfo()) + .deleted(false) + .build()); } private Notification createNotification(User recipient, NotificationRequest notificationRequest) { @@ -122,7 +146,7 @@ public class DefaultNotificationProcessingService extends AbstractSubscriptionSe .severity(notificationRequest.getNotificationSeverity()) .status(NotificationStatus.SENT) .build(); - return notificationService.createNotification(recipient.getTenantId(), notification); + return notificationService.saveNotification(recipient.getTenantId(), notification); } private String formatNotificationText(String template, User recipient) { @@ -135,20 +159,28 @@ public class DefaultNotificationProcessingService extends AbstractSubscriptionSe } private void onNotificationUpdate(TenantId tenantId, UserId recipientId, Notification notification, boolean isNew) { - forwardToSubscriptionManagerServiceOrSendToCore(tenantId, recipientId, subscriptionManagerService -> { - subscriptionManagerService.onNotificationUpdate(tenantId, recipientId, notification, isNew, TbCallback.EMPTY); - }, () -> { - return TbSubscriptionUtils.notificationUpdateToProto(tenantId, recipientId, notification, isNew); + NotificationUpdate notificationUpdate = NotificationUpdate.builder() + .notification(notification) + .isNew(isNew) + .build(); + wsCallBackExecutor.submit(() -> { + forwardToSubscriptionManagerService(tenantId, recipientId, subscriptionManagerService -> { + subscriptionManagerService.onNotificationUpdate(tenantId, recipientId, notificationUpdate, TbCallback.EMPTY); + }, () -> { + return TbSubscriptionUtils.notificationUpdateToProto(tenantId, recipientId, notificationUpdate); + }); }); } - public void onNotificationRequestDeleted(TenantId tenantId, NotificationRequestId notificationRequestId) { - TransportProtos.ToCoreMsg notificationRequestDeletedProto = TbSubscriptionUtils.notificationRequestDeletedToProto(tenantId, notificationRequestId); - Set coreServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_CORE)); - for (String serviceId : coreServices) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); - clusterService.pushMsgToCore(tpi, UUID.randomUUID(), notificationRequestDeletedProto, null); - } + private void onNotificationRequestUpdate(TenantId tenantId, NotificationRequestUpdate update) { + wsCallBackExecutor.submit(() -> { + TransportProtos.ToCoreMsg notificationRequestDeletedProto = TbSubscriptionUtils.notificationRequestUpdateToProto(tenantId, update); + Set coreServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_CORE)); + for (String serviceId : coreServices) { + TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); + clusterService.pushMsgToCore(tpi, UUID.randomUUID(), notificationRequestDeletedProto, null); + } + }); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/notification/NotificationRuleProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/NotificationRuleProcessingService.java new file mode 100644 index 0000000000..4f3d0f7d9e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/notification/NotificationRuleProcessingService.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.notification; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.id.TenantId; + +public interface NotificationRuleProcessingService { + + ListenableFuture onAlarmCreatedOrUpdated(TenantId tenantId, Alarm alarm); + + ListenableFuture onAlarmAcknowledged(TenantId tenantId, Alarm alarm); + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/NotificationSubscriptionService.java similarity index 86% rename from common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationProcessingService.java rename to application/src/main/java/org/thingsboard/server/service/notification/NotificationSubscriptionService.java index b25d4943b7..902ad39470 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/NotificationSubscriptionService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.notification; +package org.thingsboard.server.service.notification; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; @@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.NotificationRequest; -public interface NotificationProcessingService { +public interface NotificationSubscriptionService { NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest); @@ -29,4 +29,6 @@ public interface NotificationProcessingService { void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId); + void updateNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest); + } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index 1ee7c3cd16..cf93ea7f1d 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.queue; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.scheduling.annotation.Scheduled; @@ -31,6 +32,7 @@ import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationInfo; import org.thingsboard.server.common.data.rpc.RpcError; import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.TbActorMsg; @@ -80,6 +82,8 @@ import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import org.thingsboard.server.service.sync.vc.GitVersionControlQueueService; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; +import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; +import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; import javax.annotation.PostConstruct; @@ -461,13 +465,17 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService> subscriptionsByEntityId = new ConcurrentHashMap<>(); private final Map> subscriptionsByWsSessionId = new ConcurrentHashMap<>(); private final ConcurrentMap> partitionedSubscriptions = new ConcurrentHashMap<>(); @@ -309,6 +313,7 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene s -> alarm, false ); + notificationRuleProcessingService.onAlarmCreatedOrUpdated(tenantId, alarm); callback.onSuccess(); } @@ -330,13 +335,10 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene } @Override - public void onNotificationUpdate(TenantId tenantId, UserId recipientId, Notification notification, boolean isNew, TbCallback callback) { + public void onNotificationUpdate(TenantId tenantId, UserId recipientId, NotificationUpdate notificationUpdate, TbCallback callback) { Set subscriptions = subscriptionsByEntityId.get(recipientId); if (subscriptions != null) { - NotificationsSubscriptionUpdate subscriptionUpdate = NotificationsSubscriptionUpdate.builder() - .notification(notification) - .isNewNotification(isNew) - .build(); + NotificationsSubscriptionUpdate subscriptionUpdate = new NotificationsSubscriptionUpdate(notificationUpdate); subscriptions.stream() .filter(subscription -> subscription.getType() == TbSubscriptionType.NOTIFICATIONS || subscription.getType() == TbSubscriptionType.NOTIFICATIONS_COUNT) @@ -356,11 +358,8 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene } @Override - public void onNotificationRequestDeleted(TenantId tenantId, NotificationRequestId notificationRequestId, TbCallback callback) { - NotificationsSubscriptionUpdate subscriptionUpdate = NotificationsSubscriptionUpdate.builder() - .notificationRequestDeleted(true) - .notificationRequestId(notificationRequestId) - .build(); + public void onNotificationRequestUpdate(TenantId tenantId, NotificationRequestUpdate notificationRequestUpdate, TbCallback callback) { + NotificationsSubscriptionUpdate subscriptionUpdate = new NotificationsSubscriptionUpdate(notificationRequestUpdate); subscriptionsByEntityId.entrySet().stream() .filter(subEntry -> subEntry.getKey().getEntityType() == EntityType.USER) .flatMap(subEntry -> subEntry.getValue().stream() diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java index 239f9c94f9..8079bdbcce 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java @@ -18,14 +18,14 @@ package org.thingsboard.server.service.subscription; import org.springframework.context.ApplicationListener; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; +import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; +import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; import java.util.List; @@ -49,8 +49,8 @@ public interface SubscriptionManagerService extends ApplicationListener toSubscriptionManagerService, - Supplier toCore) { + protected void forwardToSubscriptionManagerService(TenantId tenantId, EntityId entityId, + Consumer toSubscriptionManagerService, + Supplier toCore) { TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); if (currentPartitions.contains(tpi)) { if (subscriptionManagerService.isPresent()) { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java index 386904918f..0e455bf9d5 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java @@ -37,12 +37,9 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmDataQuery; -import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.alarm.AlarmOperationResult; import org.thingsboard.server.dao.alarm.AlarmService; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.usagestats.TbApiUsageClient; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; @@ -164,13 +161,14 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService Alarm alarm = result.getAlarm(); TenantId tenantId = result.getAlarm().getTenantId(); for (EntityId entityId : result.getPropagatedEntitiesList()) { - forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> { + forwardToSubscriptionManagerService(tenantId, entityId, subscriptionManagerService -> { subscriptionManagerService.onAlarmUpdate(tenantId, entityId, alarm, TbCallback.EMPTY); }, () -> { return TbSubscriptionUtils.toAlarmUpdateProto(tenantId, entityId, alarm); }); } }); + // todo: handle notification rule } private void onAlarmDeleted(AlarmOperationResult result) { @@ -178,7 +176,7 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService Alarm alarm = result.getAlarm(); TenantId tenantId = result.getAlarm().getTenantId(); for (EntityId entityId : result.getPropagatedEntitiesList()) { - forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> { + forwardToSubscriptionManagerService(tenantId, entityId, subscriptionManagerService -> { subscriptionManagerService.onAlarmDeleted(tenantId, entityId, alarm, TbCallback.EMPTY); }, () -> { return TbSubscriptionUtils.toAlarmDeletedProto(tenantId, entityId, alarm); diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index 567f5cb9ff..9521805337 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -366,7 +366,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } private void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List attributes, boolean notifyDevice) { - forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> { + forwardToSubscriptionManagerService(tenantId, entityId, subscriptionManagerService -> { subscriptionManagerService.onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice, TbCallback.EMPTY); }, () -> { return TbSubscriptionUtils.toAttributesUpdateProto(tenantId, entityId, scope, attributes); @@ -374,7 +374,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } private void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List keys) { - forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> { + forwardToSubscriptionManagerService(tenantId, entityId, subscriptionManagerService -> { subscriptionManagerService.onAttributesDelete(tenantId, entityId, scope, keys, TbCallback.EMPTY); }, () -> { return TbSubscriptionUtils.toAttributesDeleteProto(tenantId, entityId, scope, keys); @@ -382,7 +382,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } private void onTimeSeriesUpdate(TenantId tenantId, EntityId entityId, List ts) { - forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> { + forwardToSubscriptionManagerService(tenantId, entityId, subscriptionManagerService -> { subscriptionManagerService.onTimeSeriesUpdate(tenantId, entityId, ts, TbCallback.EMPTY); }, () -> { return TbSubscriptionUtils.toTimeseriesUpdateProto(tenantId, entityId, ts); @@ -390,7 +390,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } private void onTimeSeriesDelete(TenantId tenantId, EntityId entityId, List keys, List ts) { - forwardToSubscriptionManagerServiceOrSendToCore(tenantId, entityId, subscriptionManagerService -> { + forwardToSubscriptionManagerService(tenantId, entityId, subscriptionManagerService -> { List updated = new ArrayList<>(); List deleted = new ArrayList<>(); diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java index ee0afd3c79..6e4ff69a88 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java @@ -21,16 +21,20 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.id.IdBased; import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationInfo; import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.dao.notification.NotificationProcessingService; +import org.thingsboard.server.service.notification.NotificationSubscriptionService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.ws.notification.cmd.NotificationsCountSubCmd; +import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; +import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscription; import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; import org.thingsboard.server.service.ws.WebSocketSessionRef; @@ -53,7 +57,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH private final NotificationService notificationService; private final TbLocalSubscriptionService localSubscriptionService; - private final NotificationProcessingService notificationProcessingService; + private final NotificationSubscriptionService notificationSubscriptionService; private final TbServiceInfoProvider serviceInfoProvider; @Autowired @Lazy private WebSocketService wsService; @@ -106,47 +110,83 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH subscription.getUnreadCounter().set(unreadCount); } + + /* Notifications subscription update handling */ private void handleNotificationsSubscriptionUpdate(NotificationsSubscription subscription, NotificationsSubscriptionUpdate subscriptionUpdate) { - if (subscriptionUpdate.getNotification() != null) { - Notification notification = subscriptionUpdate.getNotification(); - if (notification.getStatus() == NotificationStatus.READ) { - fetchUnreadNotifications(subscription); - sendUpdate(subscription.getSessionId(), subscription.createFullUpdate()); - } else { - subscription.getUnreadNotifications().put(notification.getUuidId(), notification); - if (subscriptionUpdate.isNewNotification()) { - subscription.getTotalUnreadCounter().incrementAndGet(); - Set beyondLimit = subscription.getUnreadNotifications().keySet().stream() - .skip(subscription.getLimit()) - .collect(Collectors.toSet()); - beyondLimit.forEach(notificationId -> subscription.getUnreadNotifications().remove(notificationId)); - } - sendUpdate(subscription.getSessionId(), subscription.createPartialUpdate(notification)); + if (subscriptionUpdate.getNotificationUpdate() != null) { + handleNotificationUpdate(subscription, subscriptionUpdate.getNotificationUpdate()); + } else if (subscriptionUpdate.getNotificationRequestUpdate() != null) { + handleNotificationRequestUpdate(subscription, subscriptionUpdate.getNotificationRequestUpdate()); + } + } + + private void handleNotificationUpdate(NotificationsSubscription subscription, NotificationUpdate update) { + Notification notification = update.getNotification(); + if (notification.getStatus() == NotificationStatus.READ) { + fetchUnreadNotifications(subscription); + sendUpdate(subscription.getSessionId(), subscription.createFullUpdate()); + } else { + subscription.getUnreadNotifications().put(notification.getUuidId(), notification); + if (update.isNew()) { + subscription.getTotalUnreadCounter().incrementAndGet(); + Set beyondLimit = subscription.getUnreadNotifications().keySet().stream() + .skip(subscription.getLimit()) + .collect(Collectors.toSet()); + beyondLimit.forEach(notificationId -> subscription.getUnreadNotifications().remove(notificationId)); } - } else if (subscriptionUpdate.isNotificationRequestDeleted()) { + sendUpdate(subscription.getSessionId(), subscription.createPartialUpdate(notification)); + } + } + + private void handleNotificationRequestUpdate(NotificationsSubscription subscription, NotificationRequestUpdate update) { + NotificationRequestId notificationRequestId = update.getNotificationRequestId(); + if (update.isDeleted()) { if (subscription.getUnreadNotifications().values().stream() - .anyMatch(notification -> notification.getRequestId().equals(subscriptionUpdate.getNotificationRequestId()))) { + .anyMatch(notification -> notification.getRequestId().equals(notificationRequestId))) { fetchUnreadNotifications(subscription); sendUpdate(subscription.getSessionId(), subscription.createFullUpdate()); } + } else { + NotificationInfo notificationInfo = update.getNotificationInfo(); + subscription.getUnreadNotifications().values().stream() + .filter(notification -> notification.getRequestId().equals(notificationRequestId)) + .forEach(notification -> { + notification.setInfo(notificationInfo); + sendUpdate(subscription.getSessionId(), subscription.createPartialUpdate(notification)); + }); } } + + /* Notifications count subscription update handling */ private void handleNotificationsCountSubscriptionUpdate(NotificationsCountSubscription subscription, NotificationsSubscriptionUpdate subscriptionUpdate) { - if (subscriptionUpdate.getNotification() != null) { - Notification notification = subscriptionUpdate.getNotification(); - if (subscriptionUpdate.isNewNotification()) { - subscription.getUnreadCounter().incrementAndGet(); - } else if (notification.getStatus() == NotificationStatus.READ) { - // for now this can only happen when user marks notification as read - subscription.getUnreadCounter().decrementAndGet(); - } - } else if (subscriptionUpdate.isNotificationRequestDeleted()) { - fetchUnreadNotificationsCount(subscription); + if (subscriptionUpdate.getNotificationUpdate() != null) { + handleNotificationUpdate(subscription, subscriptionUpdate.getNotificationUpdate()); + } else if (subscriptionUpdate.getNotificationRequestUpdate() != null) { + handleNotificationRequestUpdate(subscription, subscriptionUpdate.getNotificationRequestUpdate()); + } + sendUpdate(subscription.getSessionId(), subscription.createUpdate()); + } + + private void handleNotificationUpdate(NotificationsCountSubscription subscription, NotificationUpdate update) { + Notification notification = update.getNotification(); + if (update.isNew()) { + subscription.getUnreadCounter().incrementAndGet(); + } else if (notification.getStatus() == NotificationStatus.READ) { + // for now this can only happen when user marks notification as read + subscription.getUnreadCounter().decrementAndGet(); } sendUpdate(subscription.getSessionId(), subscription.createUpdate()); } + private void handleNotificationRequestUpdate(NotificationsCountSubscription subscription, NotificationRequestUpdate update) { + if (update.isDeleted()) { + fetchUnreadNotificationsCount(subscription); + sendUpdate(subscription.getSessionId(), subscription.createUpdate()); + } + } + + private void sendUpdate(String sessionId, CmdUpdate update) { wsService.sendWsMsg(sessionId, update); } @@ -155,7 +195,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH @Override public void handleMarkAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationAsReadCmd cmd) { NotificationId notificationId = new NotificationId(cmd.getNotificationId()); - notificationProcessingService.markNotificationAsRead(sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), notificationId); + notificationSubscriptionService.markNotificationAsRead(sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), notificationId); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationRequestUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationRequestUpdate.java new file mode 100644 index 0000000000..36c3b2a441 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationRequestUpdate.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws.notification.sub; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.notification.NotificationInfo; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class NotificationRequestUpdate { + private NotificationRequestId notificationRequestId; + private NotificationInfo notificationInfo; + private boolean deleted; +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationUpdate.java new file mode 100644 index 0000000000..0e7db3e8cf --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationUpdate.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws.notification.sub; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.notification.Notification; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class NotificationUpdate { + private Notification notification; + private boolean isNew; +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionUpdate.java index 4ed7ca7341..cedd6041f7 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscriptionUpdate.java @@ -15,19 +15,22 @@ */ package org.thingsboard.server.service.ws.notification.sub; -import lombok.Builder; import lombok.Data; -import org.thingsboard.server.common.data.id.NotificationRequestId; -import org.thingsboard.server.common.data.notification.Notification; @Data -@Builder public class NotificationsSubscriptionUpdate { - private final Notification notification; - private final boolean isNewNotification; + private final NotificationUpdate notificationUpdate; + private final NotificationRequestUpdate notificationRequestUpdate; - private final boolean notificationRequestDeleted; - private final NotificationRequestId notificationRequestId; + public NotificationsSubscriptionUpdate(NotificationUpdate notificationUpdate) { + this.notificationUpdate = notificationUpdate; + this.notificationRequestUpdate = null; + } + + public NotificationsSubscriptionUpdate(NotificationRequestUpdate notificationRequestUpdate) { + this.notificationUpdate = null; + this.notificationRequestUpdate = notificationRequestUpdate; + } } diff --git a/common/cluster-api/src/main/proto/queue.proto b/common/cluster-api/src/main/proto/queue.proto index c1a6dd342e..74223de8c7 100644 --- a/common/cluster-api/src/main/proto/queue.proto +++ b/common/cluster-api/src/main/proto/queue.proto @@ -576,8 +576,8 @@ message TbAlarmSubscriptionUpdateProto { message NotificationsSubscriptionUpdateProto { string sessionId = 1; int32 subscriptionId = 2; - string notification = 3; - bool isNewNotification = 4; + string notificationUpdate = 3; + string notificationRequestUpdate = 4; } message NotificationUpdateProto { @@ -585,15 +585,13 @@ message NotificationUpdateProto { int64 tenantIdLSB = 2; int64 recipientIdMSB = 3; int64 recipientIdLSB = 4; - string notification = 5; - bool isNew = 6; + string update = 5; } -message NotificationRequestDeleteProto { +message NotificationRequestUpdateProto { int64 tenantIdMSB = 1; int64 tenantIdLSB = 2; - int64 notificationRequestIdMSB = 3; - int64 notificationRequestIdLSB = 4; + string update = 6; } message TbAttributeUpdateProto { @@ -700,7 +698,7 @@ message SubscriptionMgrMsgProto { NotificationsSubscriptionProto notificationsSub = 11; NotificationsCountSubscriptionProto notificationsCountSub = 12; NotificationUpdateProto notificationUpdate = 13; - NotificationRequestDeleteProto notificationRequestDelete = 14; + NotificationRequestUpdateProto notificationRequestUpdate = 14; } message LocalSubscriptionServiceMsgProto { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java new file mode 100644 index 0000000000..291688a422 --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.notification; + +import org.thingsboard.server.common.data.id.NotificationRuleId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.NotificationRule; + +public interface NotificationRuleService { + + NotificationRule findNotificationRuleById(TenantId tenantId, NotificationRuleId notificationRuleId); + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java index a4bebfd135..6b7f6267db 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java @@ -15,37 +15,45 @@ */ package org.thingsboard.server.dao.notification; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationInfo; import org.thingsboard.server.common.data.notification.NotificationRequest; -import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import java.util.List; + public interface NotificationService { - NotificationRequest createNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest); + NotificationRequest saveNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest); NotificationRequest findNotificationRequestById(TenantId tenantId, NotificationRequestId id); - PageData findNotificationRequestsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + PageData findNotificationRequestsByTenantId(TenantId tenantId, PageLink pageLink); + + List findNotificationRequestsByRuleIdAndAlarmId(TenantId tenantId, NotificationRuleId ruleId, AlarmId alarmId); - void deleteNotificationRequest(TenantId tenantId, NotificationRequestId id); + void deleteNotificationRequestById(TenantId tenantId, NotificationRequestId id); - Notification createNotification(TenantId tenantId, Notification notification); + Notification saveNotification(TenantId tenantId, Notification notification); Notification findNotificationById(TenantId tenantId, NotificationId notificationId); - boolean updateNotificationStatus(TenantId tenantId, UserId userId, NotificationId notificationId, NotificationStatus status); + boolean markNotificationAsRead(TenantId tenantId, UserId userId, NotificationId notificationId); - PageData findNotificationsByUserIdAndReadStatusAndPageLink(TenantId tenantId, UserId userId, boolean unreadOnly, PageLink pageLink); + PageData findNotificationsByUserIdAndReadStatus(TenantId tenantId, UserId userId, boolean unreadOnly, PageLink pageLink); PageData findLatestUnreadNotificationsByUserId(TenantId tenantId, UserId userId, int limit); int countUnreadNotificationsByUserId(TenantId tenantId, UserId userId); + int updateNotificationsInfosByRequestId(TenantId tenantId, NotificationRequestId notificationRequestId, NotificationInfo notificationInfo); + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java index f13c84bc82..ba7d30fb85 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.Length; @@ -78,6 +79,7 @@ public class Alarm extends BaseData implements HasName, HasTenantId, Ha "By default, 'propagateRelationTypes' array is empty which means that the alarm will be propagated based on any relation type to parent entities. " + "This parameter should be used only in case when 'propagate' parameter is set to true, otherwise, 'propagateRelationTypes' array will be ignored.") private List propagateRelationTypes; + private NotificationRuleId notificationRuleId; public Alarm() { super(); @@ -105,6 +107,7 @@ public class Alarm extends BaseData implements HasName, HasTenantId, Ha this.propagateToOwner = alarm.isPropagateToOwner(); this.propagateToTenant = alarm.isPropagateToTenant(); this.propagateRelationTypes = alarm.getPropagateRelationTypes(); + this.notificationRuleId = alarm.getNotificationRuleId(); } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java index 71e7b5f415..33e5cf1391 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java @@ -19,6 +19,7 @@ import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; @@ -59,4 +60,6 @@ public class DeviceProfileAlarm implements Serializable { "This parameter should be used only in case when 'propagate' parameter is set to true, otherwise, 'propagateRelationTypes' array will be ignored.") private List propagateRelationTypes; + private NotificationRuleId notificationRuleId; + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRuleId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRuleId.java new file mode 100644 index 0000000000..2b963c5b88 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRuleId.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.EntityType; + +import java.util.UUID; + +public class NotificationRuleId extends UUIDBased { + + @JsonCreator + public NotificationRuleId(@JsonProperty("id") UUID id) { + super(id); + } + +// @Override +// public EntityType getEntityType() { +// return EntityType.NOTIFICATION_TARGET; +// } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationInfo.java index 4831e056cd..298998f8d7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationInfo.java @@ -15,14 +15,22 @@ */ package org.thingsboard.server.common.data.notification; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.alarm.AlarmStatus; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.validation.NoXss; @Data +@AllArgsConstructor +@NoArgsConstructor +@Builder //@JsonIgnoreProperties(ignoreUnknown = true) //@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "notificationType", visible = true, defaultImpl = NotificationInfo.class) //@JsonSubTypes({ @@ -31,7 +39,12 @@ import org.thingsboard.server.common.data.validation.NoXss; public class NotificationInfo { @NoXss private String description; - - private ObjectNode alarmDetails; // move to child class private DashboardId dashboardId; + + private AlarmId alarmId; + private String alarmType; + private EntityId alarmOriginator; + private AlarmSeverity alarmSeverity; + private AlarmStatus alarmStatus; + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java index dd800ef3fc..af90eab7ee 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.notification; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -23,7 +25,9 @@ import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.NoXss; @@ -51,6 +55,13 @@ public class NotificationRequest extends BaseData impleme private NotificationInfo notificationInfo; private NotificationSeverity notificationSeverity; private NotificationRequestConfig additionalConfig; + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private NotificationRequestStatus status; + + @JsonIgnore + private NotificationRuleId ruleId; // maybe move to child class + @JsonIgnore + private AlarmId alarmId; public static final String GENERAL_NOTIFICATION_REASON = "General"; public static final String ALARM_NOTIFICATION_REASON = "Alarm"; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java index 8f65427f21..fd01c91255 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java @@ -19,5 +19,5 @@ import lombok.Data; @Data public class NotificationRequestConfig { - private Long sendingDelayMs; + private int sendingDelayInMinutes; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStatus.java new file mode 100644 index 0000000000..1c3eede15e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStatus.java @@ -0,0 +1,21 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification; + +public enum NotificationRequestStatus { + PROCESSED, + SCHEDULED +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NonConfirmedNotificationEscalation.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NonConfirmedNotificationEscalation.java index befc666281..1174cedb06 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NonConfirmedNotificationEscalation.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NonConfirmedNotificationEscalation.java @@ -15,9 +15,18 @@ */ package org.thingsboard.server.common.data.notification.rule; -import java.util.UUID; +import lombok.Data; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +@Data public class NonConfirmedNotificationEscalation { - private long delayMs; // delay since initial notification request // if no one from previous escalation item has read the notification, send notifications after this time to other recipients - private UUID notificationTargetId; + + @Min(1) + private int delayInMinutes; // delay since initial notification request // if no one from previous escalation item has read the notification, send notifications after this time to other recipients + @NotNull + private NotificationTargetId notificationTargetId; + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java index 340a49c54d..4d2a8ed184 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java @@ -16,23 +16,36 @@ package org.thingsboard.server.common.data.notification.rule; import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.id.NotificationRuleId; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.TenantId; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; import java.util.List; import java.util.Map; +import java.util.TreeMap; import java.util.UUID; @Data -public class NotificationRule { +@EqualsAndHashCode(callSuper = true) +public class NotificationRule extends BaseData implements HasTenantId, HasName { - // we may choose it in the alarm rule config, or maybe it's better to configure evrth in the triggers? - - private UUID id; // NotificationRuleId id; + @NotNull + private TenantId tenantId; + @NotBlank private String name; - private Map triggers; // or maybe bad idea - // Map - concrete alarmRule or alarm rule search (e.g. alarm rule of device profiles of particular transport type with certain severity) - // triggerConfiguration (??) - alarm filter: severity, specific device profile, alarm rule - - private UUID initialNotificationTargetId; + @NotBlank + private String notificationTextTemplate; + @NotNull + private NotificationTargetId initialNotificationTargetId; + @NotNull + @Valid private List escalations; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 2ae524f1e2..27b17164ea 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -295,6 +295,7 @@ public class ModelConstants { public static final String ALARM_PROPAGATE_TO_OWNER_PROPERTY = "propagate_to_owner"; public static final String ALARM_PROPAGATE_TO_TENANT_PROPERTY = "propagate_to_tenant"; public static final String ALARM_PROPAGATE_RELATION_TYPES = "propagate_relation_types"; + public static final String ALARM_NOTIFICATION_RULE_ID = "notification_rule_id"; public static final String ALARM_BY_ID_VIEW_NAME = "alarm_by_id"; @@ -668,6 +669,12 @@ public class ModelConstants { public static final String NOTIFICATION_REQUEST_NOTIFICATION_INFO_PROPERTY = "notification_info"; public static final String NOTIFICATION_REQUEST_NOTIFICATION_SEVERITY_PROPERTY = "notification_severity"; public static final String NOTIFICATION_REQUEST_ADDITIONAL_CONFIG_PROPERTY = "additional_config"; + public static final String NOTIFICATION_REQUEST_STATUS_PROPERTY = "status"; + public static final String NOTIFICATION_REQUEST_RULE_ID_PROPERTY = "rule_id"; + public static final String NOTIFICATION_REQUEST_ALARM_ID_PROPERTY = "alarm_id"; + + public static final String NOTIFICATION_RULE_TABLE_NAME = "notification_rule"; + // ... protected static final String[] NONE_AGGREGATION_COLUMNS = new String[]{LONG_VALUE_COLUMN, DOUBLE_VALUE_COLUMN, BOOLEAN_VALUE_COLUMN, STRING_VALUE_COLUMN, JSON_VALUE_COLUMN, KEY_COLUMN, TS_COLUMN}; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java index 56adfae6de..8bdb90d1b4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseSqlEntity; @@ -47,6 +48,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ACK_TS_PROPE import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CLEAR_TS_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CUSTOMER_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_END_TS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_NOTIFICATION_RULE_ID; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_PROPERTY; @@ -116,6 +118,9 @@ public abstract class AbstractAlarmEntity extends BaseSqlEntity @Column(name = ALARM_PROPAGATE_RELATION_TYPES) private String propagateRelationTypes; + @Column(name = ALARM_NOTIFICATION_RULE_ID) + private UUID notificationRuleId; + public AbstractAlarmEntity() { super(); } @@ -150,6 +155,7 @@ public abstract class AbstractAlarmEntity extends BaseSqlEntity } else { this.propagateRelationTypes = null; } + this.notificationRuleId = getUuid(alarm.getNotificationRuleId()); } public AbstractAlarmEntity(AlarmEntity alarmEntity) { @@ -172,6 +178,7 @@ public abstract class AbstractAlarmEntity extends BaseSqlEntity this.clearTs = alarmEntity.getClearTs(); this.details = alarmEntity.getDetails(); this.propagateRelationTypes = alarmEntity.getPropagateRelationTypes(); + this.notificationRuleId = alarmEntity.getNotificationRuleId(); } protected Alarm toAlarm() { @@ -200,6 +207,7 @@ public abstract class AbstractAlarmEntity extends BaseSqlEntity } else { alarm.setPropagateRelationTypes(Collections.emptyList()); } + alarm.setNotificationRuleId(createId(notificationRuleId, NotificationRuleId::new)); return alarm; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java index 65e1fe8214..cd0bbd435a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java @@ -21,13 +21,16 @@ import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.NotificationInfo; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequestConfig; +import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.notification.NotificationSeverity; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; @@ -71,6 +74,16 @@ public class NotificationRequestEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.TENANT_ID_PROPERTY, nullable = false) + private UUID tenantId; + + @Column(name = ModelConstants.NAME_PROPERTY, nullable = false) + private String name; + + + public NotificationRuleEntity() {} + + public NotificationRuleEntity(NotificationRule notificationRule) { + setId(notificationRule.getUuidId()); + setCreatedTime(notificationRule.getCreatedTime()); + setTenantId(getUuid(notificationRule.getTenantId())); + setName(notificationRule.getName()); + } + + @Override + public NotificationRule toData() { + NotificationRule notificationRule = new NotificationRule(); + notificationRule.setId(new NotificationRuleId(id)); + notificationRule.setCreatedTime(createdTime); + notificationRule.setTenantId(createId(tenantId, TenantId::fromUUID)); + notificationRule.setName(name); + return notificationRule; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java new file mode 100644 index 0000000000..7c9e404b3b --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.notification; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.id.NotificationRuleId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.NotificationRule; + +@Service +@RequiredArgsConstructor +public class DefaultNotificationRuleService implements NotificationRuleService { + + private final NotificationRuleDao notificationRuleDao; + + @Override + public NotificationRule findNotificationRuleById(TenantId tenantId, NotificationRuleId notificationRuleId) { + return notificationRuleDao.findById(tenantId, notificationRuleId.getId()); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java index c15480c747..a3ef857233 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java @@ -19,22 +19,25 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationInfo; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationSeverity; import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.sql.query.EntityKeyMapping; +import java.util.List; + @Service @Slf4j @RequiredArgsConstructor @@ -46,7 +49,7 @@ public class DefaultNotificationService implements NotificationService { private final NotificationRequestValidator notificationRequestValidator = new NotificationRequestValidator(); @Override - public NotificationRequest createNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { + public NotificationRequest saveNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { if (StringUtils.isBlank(notificationRequest.getNotificationReason())) { notificationRequest.setNotificationReason(NotificationRequest.GENERAL_NOTIFICATION_REASON); } @@ -63,21 +66,23 @@ public class DefaultNotificationService implements NotificationService { } @Override - public PageData findNotificationRequestsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { + public PageData findNotificationRequestsByTenantId(TenantId tenantId, PageLink pageLink) { return notificationRequestDao.findByTenantIdAndPageLink(tenantId, pageLink); } + @Override + public List findNotificationRequestsByRuleIdAndAlarmId(TenantId tenantId, NotificationRuleId ruleId, AlarmId alarmId) { + return notificationRequestDao.findByRuleIdAndAlarmId(tenantId, ruleId, alarmId); + } + // ON DELETE CASCADE is used: notifications for request are deleted as well @Override - public void deleteNotificationRequest(TenantId tenantId, NotificationRequestId id) { + public void deleteNotificationRequestById(TenantId tenantId, NotificationRequestId id) { notificationRequestDao.removeById(tenantId, id.getId()); } @Override - public Notification createNotification(TenantId tenantId, Notification notification) { - if (notification.getId() != null) { - throw new DataValidationException("Notification cannot be updated"); // tmp ? - } + public Notification saveNotification(TenantId tenantId, Notification notification) { return notificationDao.save(tenantId, notification); } @@ -86,14 +91,13 @@ public class DefaultNotificationService implements NotificationService { return notificationDao.findById(tenantId, notificationId.getId()); } - @Transactional @Override - public boolean updateNotificationStatus(TenantId tenantId, UserId userId, NotificationId notificationId, NotificationStatus status) { - return notificationDao.updateStatusByIdAndUserId(tenantId, userId, notificationId, status); + public boolean markNotificationAsRead(TenantId tenantId, UserId userId, NotificationId notificationId) { + return notificationDao.updateStatusByIdAndUserId(tenantId, userId, notificationId, NotificationStatus.READ); } @Override - public PageData findNotificationsByUserIdAndReadStatusAndPageLink(TenantId tenantId, UserId userId, boolean unreadOnly, PageLink pageLink) { + public PageData findNotificationsByUserIdAndReadStatus(TenantId tenantId, UserId userId, boolean unreadOnly, PageLink pageLink) { if (unreadOnly) { return notificationDao.findUnreadByUserIdAndPageLink(tenantId, userId, pageLink); } else { @@ -105,7 +109,7 @@ public class DefaultNotificationService implements NotificationService { public PageData findLatestUnreadNotificationsByUserId(TenantId tenantId, UserId userId, int limit) { SortOrder sortOrder = new SortOrder(EntityKeyMapping.CREATED_TIME, SortOrder.Direction.DESC); PageLink pageLink = new PageLink(limit, 0, null, sortOrder); - return findNotificationsByUserIdAndReadStatusAndPageLink(tenantId, userId, true, pageLink); + return findNotificationsByUserIdAndReadStatus(tenantId, userId, true, pageLink); } @Override @@ -113,13 +117,15 @@ public class DefaultNotificationService implements NotificationService { return notificationDao.countUnreadByUserId(tenantId, userId); } + @Override + public int updateNotificationsInfosByRequestId(TenantId tenantId, NotificationRequestId notificationRequestId, NotificationInfo notificationInfo) { + return notificationDao.updateInfosByRequestId(tenantId, notificationRequestId, notificationInfo); + } + private static class NotificationRequestValidator extends DataValidator { @Override protected void validateDataImpl(TenantId tenantId, NotificationRequest notificationRequest) { - if (notificationRequest.getId() != null) { - throw new DataValidationException("Notification request cannot be changed once created"); - } } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java index c16df8819a..dcfba4d704 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java @@ -16,9 +16,11 @@ package org.thingsboard.server.dao.notification; import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationInfo; import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -34,4 +36,8 @@ public interface NotificationDao extends Dao { int countUnreadByUserId(TenantId tenantId, UserId userId); + PageData findByRequestId(TenantId tenantId, NotificationRequestId notificationRequestId, PageLink pageLink); + + int updateInfosByRequestId(TenantId tenantId, NotificationRequestId notificationRequestId, NotificationInfo notificationInfo); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java index d518a4e245..1848e826e0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java @@ -15,14 +15,20 @@ */ package org.thingsboard.server.dao.notification; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import java.util.List; + public interface NotificationRequestDao extends Dao { PageData findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + List findByRuleIdAndAlarmId(TenantId tenantId, NotificationRuleId ruleId, AlarmId alarmId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java new file mode 100644 index 0000000000..3a7cbb777b --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.notification; + +import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.dao.Dao; + +public interface NotificationRuleDao extends Dao { +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java index 2611c32c90..cd98941011 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java @@ -20,10 +20,13 @@ import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.NotificationId; +import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationInfo; import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -82,6 +85,16 @@ public class JpaNotificationDao extends JpaAbstractDao findByRequestId(TenantId tenantId, NotificationRequestId notificationRequestId, PageLink pageLink) { + return DaoUtil.toPageData(notificationRepository.findByRequestId(notificationRequestId.getId(), DaoUtil.toPageable(pageLink))); + } + + @Override + public int updateInfosByRequestId(TenantId tenantId, NotificationRequestId notificationRequestId, NotificationInfo notificationInfo) { + return notificationRepository.updateInfosByRequestId(notificationRequestId.getId(), JacksonUtil.valueToTree(notificationInfo)); + } + @Override protected Class getEntityClass() { return NotificationEntity.class; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java index 099218abcc..5e1ca5d3f7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java @@ -19,6 +19,8 @@ import com.google.common.base.Strings; import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.page.PageData; @@ -29,6 +31,7 @@ import org.thingsboard.server.dao.notification.NotificationRequestDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; +import java.util.List; import java.util.UUID; @Component @@ -44,6 +47,11 @@ public class JpaNotificationRequestDao extends JpaAbstractDao findByRuleIdAndAlarmId(TenantId tenantId, NotificationRuleId ruleId, AlarmId alarmId) { + return DaoUtil.convertDataList(notificationRequestRepository.findAllByRuleIdAndAlarmId(ruleId.getId(), alarmId.getId())); + } + @Override protected Class getEntityClass() { return NotificationRequestEntity.class; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java new file mode 100644 index 0000000000..365a439bd5 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.notification; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.dao.model.sql.NotificationRuleEntity; +import org.thingsboard.server.dao.notification.NotificationRuleDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.UUID; + +@Component +@SqlDao +@RequiredArgsConstructor +public class JpaNotificationRuleDao extends JpaAbstractDao implements NotificationRuleDao { + + private final NotificationRuleRepository notificationRuleRepository; + + @Override + protected Class getEntityClass() { + return NotificationRuleEntity.class; + } + + @Override + protected JpaRepository getRepository() { + return notificationRuleRepository; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java index e264689ce7..3f46a3dfa9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.sql.notification; +import com.fasterxml.jackson.databind.JsonNode; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; @@ -45,6 +46,11 @@ public interface NotificationRepository extends JpaRepository findByRequestId(UUID requestId, Pageable pageable); + + @Modifying + @Transactional + @Query("UPDATE NotificationEntity n SET n.info = :info WHERE n.requestId = :requestId") + int updateInfosByRequestId(@Param("requestId") UUID requestId, @Param("info") JsonNode info); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java index 1303c99034..4ff69f8797 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java @@ -23,6 +23,7 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.thingsboard.server.dao.model.sql.NotificationRequestEntity; +import java.util.List; import java.util.UUID; @Repository @@ -34,4 +35,6 @@ public interface NotificationRequestRepository extends JpaRepository findByTenantIdAndSearchText(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); + List findAllByRuleIdAndAlarmId(UUID ruleId, UUID alarmId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java new file mode 100644 index 0000000000..a991407ab2 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.notification; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.thingsboard.server.dao.model.sql.NotificationRuleEntity; + +import java.util.UUID; + +@Repository +public interface NotificationRuleRepository extends JpaRepository { +} diff --git a/dao/src/main/resources/sql/schema-entities-idx.sql b/dao/src/main/resources/sql/schema-entities-idx.sql index 8f43b1ac4e..019a8b1771 100644 --- a/dao/src/main/resources/sql/schema-entities-idx.sql +++ b/dao/src/main/resources/sql/schema-entities-idx.sql @@ -80,6 +80,6 @@ CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id_and_created_time O CREATE INDEX IF NOT EXISTS idx_notification_id ON notification(id); -CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_and_created_time ON notification(recipient_id, created_time DESC); +CREATE INDEX IF NOT EXISTS idx_notification_notification_request_id ON notification(request_id); -CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_and_status_and_created_time ON notification(recipient_id, status, created_time DESC); +CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_and_created_time ON notification(recipient_id, created_time DESC); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index cae28a049a..1018abe42a 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -795,7 +795,10 @@ CREATE TABLE IF NOT EXISTS notification_request ( text_template VARCHAR NOT NULL, notification_info VARCHAR(1000), notification_severity VARCHAR(32), - additional_config VARCHAR(1000) + additional_config VARCHAR(1000), + status VARCHAR(32), + rule_id UUID NULL CONSTRAINT fk_notification_request_rule_id REFERENCES notification_rule(id), + alarm_id UUID ); CREATE TABLE IF NOT EXISTS notification ( diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineNotificationService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineNotificationService.java new file mode 100644 index 0000000000..e7b87cf25d --- /dev/null +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineNotificationService.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.api; + +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.NotificationRequest; + +public interface RuleEngineNotificationService { + + NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest); + +} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index 6f6ebcd233..0978296ba5 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -53,7 +53,6 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.nosql.CassandraStatementTask; import org.thingsboard.server.dao.nosql.TbResultSetFuture; -import org.thingsboard.server.dao.notification.NotificationProcessingService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; @@ -267,7 +266,7 @@ public interface TbContext { SmsSenderFactory getSmsSenderFactory(); - NotificationProcessingService getNotificationProcessingService(); + RuleEngineNotificationService getNotificationService(); ScriptEngine createJsScriptEngine(String script, String... argNames); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java index 73ec3383fe..06fc0b110d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java @@ -60,7 +60,7 @@ public class TbNotificationNode implements TbNode { .notificationSeverity(config.getNotificationSeverity()) .build(); withCallback(ctx.getDbCallbackExecutor().executeAsync(() -> { - return ctx.getNotificationProcessingService().processNotificationRequest(ctx.getTenantId(), notificationRequest); + return ctx.getNotificationService().processNotificationRequest(ctx.getTenantId(), notificationRequest); }), r -> { TbMsgMetaData msgMetaData = msg.getMetaData().copy(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java index 1f90b4e6c1..29bd7d6177 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java @@ -272,6 +272,7 @@ class AlarmState { if (alarmDefinition.getPropagateRelationTypes() != null) { currentAlarm.setPropagateRelationTypes(alarmDefinition.getPropagateRelationTypes()); } + currentAlarm.setNotificationRuleId(alarmDefinition.getNotificationRuleId()); currentAlarm = ctx.getAlarmService().createOrUpdateAlarm(currentAlarm); boolean updated = currentAlarm.getStartTs() != currentAlarm.getEndTs(); return new TbAlarmResult(!updated, updated, false, false, currentAlarm); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java index 5ab5adc7fc..1ef06547ba 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java @@ -191,6 +191,7 @@ class DeviceState { AlarmState alarmState = alarmStates.computeIfAbsent(alarm.getId(), a -> new AlarmState(this.deviceProfile, deviceId, alarm, getOrInitPersistedAlarmState(alarm), dynamicPredicateValueCtx)); alarmState.processAckAlarm(alarmNf); + // todo: process notification rule } ctx.tellSuccess(msg); } From a545ace4ecc4bbb98af41670c40af9911b96f628 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Sun, 6 Nov 2022 18:51:49 +0200 Subject: [PATCH 019/488] Add logging for notification services --- .../DefaultNotificationSubscriptionService.java | 14 +++++++++++--- .../DefaultNotificationCommandsHandler.java | 12 +++++++++++- .../cmd/UnreadNotificationsCountUpdate.java | 2 ++ .../cmd/UnreadNotificationsUpdate.java | 3 ++- dao/src/main/resources/sql/schema-entities.sql | 4 ++++ 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSubscriptionService.java index 8709f76179..74fb400fb2 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSubscriptionService.java @@ -74,6 +74,7 @@ public class DefaultNotificationSubscriptionService extends AbstractSubscription @Override public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { + log.info("Processing notification request (tenant id: {}, notification target id: {})", tenantId, notificationRequest.getTargetId()); notificationRequest.setTenantId(tenantId); if (notificationRequest.getAdditionalConfig() != null) { NotificationRequestConfig config = notificationRequest.getAdditionalConfig(); @@ -93,6 +94,7 @@ public class DefaultNotificationSubscriptionService extends AbstractSubscription return notificationTargetService.findRecipientsForNotificationTarget(tenantId, notificationRequest.getTargetId(), pageLink); }, 100, recipients -> { dbCallbackExecutorService.submit(() -> { + log.debug("Sending notifications for request {} to recipients batch", savedNotificationRequest.getId()); for (User recipient : recipients) { try { Notification notification = createNotification(recipient, savedNotificationRequest); @@ -111,6 +113,7 @@ public class DefaultNotificationSubscriptionService extends AbstractSubscription public void markNotificationAsRead(TenantId tenantId, UserId recipientId, NotificationId notificationId) { boolean updated = notificationService.markNotificationAsRead(tenantId, recipientId, notificationId); if (updated) { + log.debug("Marking notification {} as read (recipient id: {}, tenant id: {})", notificationId, recipientId, tenantId); Notification notification = notificationService.findNotificationById(tenantId, notificationId); onNotificationUpdate(tenantId, recipientId, notification, false); } @@ -118,6 +121,7 @@ public class DefaultNotificationSubscriptionService extends AbstractSubscription @Override public void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId) { + log.debug("Deleting notification request {}", notificationRequestId); notificationService.deleteNotificationRequestById(tenantId, notificationRequestId); onNotificationRequestUpdate(tenantId, NotificationRequestUpdate.builder() .notificationRequestId(notificationRequestId) @@ -127,6 +131,7 @@ public class DefaultNotificationSubscriptionService extends AbstractSubscription @Override public void updateNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { + log.debug("Updating notification request {}", notificationRequest.getId()); notificationService.saveNotificationRequest(tenantId, notificationRequest); notificationService.updateNotificationsInfosByRequestId(tenantId, notificationRequest.getId(), notificationRequest.getNotificationInfo()); onNotificationRequestUpdate(tenantId, NotificationRequestUpdate.builder() @@ -137,6 +142,7 @@ public class DefaultNotificationSubscriptionService extends AbstractSubscription } private Notification createNotification(User recipient, NotificationRequest notificationRequest) { + log.trace("Creating notification for recipient {} (notification request id: {})", recipient.getId(), notificationRequest.getId()); Notification notification = Notification.builder() .requestId(notificationRequest.getId()) .recipientId(recipient.getId()) @@ -159,20 +165,22 @@ public class DefaultNotificationSubscriptionService extends AbstractSubscription } private void onNotificationUpdate(TenantId tenantId, UserId recipientId, Notification notification, boolean isNew) { - NotificationUpdate notificationUpdate = NotificationUpdate.builder() + NotificationUpdate update = NotificationUpdate.builder() .notification(notification) .isNew(isNew) .build(); + log.trace("Submitting notification update for recipient {}: {}", recipientId, update); wsCallBackExecutor.submit(() -> { forwardToSubscriptionManagerService(tenantId, recipientId, subscriptionManagerService -> { - subscriptionManagerService.onNotificationUpdate(tenantId, recipientId, notificationUpdate, TbCallback.EMPTY); + subscriptionManagerService.onNotificationUpdate(tenantId, recipientId, update, TbCallback.EMPTY); }, () -> { - return TbSubscriptionUtils.notificationUpdateToProto(tenantId, recipientId, notificationUpdate); + return TbSubscriptionUtils.notificationUpdateToProto(tenantId, recipientId, update); }); }); } private void onNotificationRequestUpdate(TenantId tenantId, NotificationRequestUpdate update) { + log.trace("Submitting notification request update: {}", update); wsCallBackExecutor.submit(() -> { TransportProtos.ToCoreMsg notificationRequestDeletedProto = TbSubscriptionUtils.notificationRequestUpdateToProto(tenantId, update); Set coreServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_CORE)); diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java index 6e4ff69a88..6af03caeb5 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java @@ -16,6 +16,7 @@ package org.thingsboard.server.service.ws.notification; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -53,6 +54,7 @@ import java.util.stream.Collectors; @Service @TbCoreComponent @RequiredArgsConstructor +@Slf4j public class DefaultNotificationCommandsHandler implements NotificationCommandsHandler { private final NotificationService notificationService; @@ -64,6 +66,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH @Override public void handleUnreadNotificationsSubCmd(WebSocketSessionRef sessionRef, NotificationsSubCmd cmd) { + log.debug("[{}] Handling unread notifications subscription cmd (cmdId: {})", sessionRef.getSessionId(), cmd.getCmdId()); SecurityUser user = sessionRef.getSecurityCtx(); NotificationsSubscription subscription = NotificationsSubscription.builder() .serviceId(serviceInfoProvider.getServiceId()) @@ -82,6 +85,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH @Override public void handleUnreadNotificationsCountSubCmd(WebSocketSessionRef sessionRef, NotificationsCountSubCmd cmd) { + log.debug("[{}] Handling unread notifications count subscription cmd (cmdId: {})", sessionRef.getSessionId(), cmd.getCmdId()); SecurityUser user = sessionRef.getSecurityCtx(); NotificationsCountSubscription subscription = NotificationsCountSubscription.builder() .serviceId(serviceInfoProvider.getServiceId()) @@ -98,6 +102,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH } private void fetchUnreadNotifications(NotificationsSubscription subscription) { + log.trace("[{}, subId: {}] Fetching unread notifications from DB", subscription.getSessionId(), subscription.getSubscriptionId()); PageData notifications = notificationService.findLatestUnreadNotificationsByUserId(subscription.getTenantId(), (UserId) subscription.getEntityId(), subscription.getLimit()); subscription.getUnreadNotifications().clear(); @@ -106,6 +111,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH } private void fetchUnreadNotificationsCount(NotificationsCountSubscription subscription) { + log.trace("[{}, subId: {}] Fetching unread notifications count from DB", subscription.getSessionId(), subscription.getSubscriptionId()); int unreadCount = notificationService.countUnreadNotificationsByUserId(subscription.getTenantId(), (UserId) subscription.getEntityId()); subscription.getUnreadCounter().set(unreadCount); } @@ -121,6 +127,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH } private void handleNotificationUpdate(NotificationsSubscription subscription, NotificationUpdate update) { + log.trace("[{}, subId: {}] Handling notification update: {}", subscription.getSessionId(), subscription.getSubscriptionId(), update); Notification notification = update.getNotification(); if (notification.getStatus() == NotificationStatus.READ) { fetchUnreadNotifications(subscription); @@ -139,6 +146,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH } private void handleNotificationRequestUpdate(NotificationsSubscription subscription, NotificationRequestUpdate update) { + log.trace("[{}, subId: {}] Handling notification request update: {}", subscription.getSessionId(), subscription.getSubscriptionId(), update); NotificationRequestId notificationRequestId = update.getNotificationRequestId(); if (update.isDeleted()) { if (subscription.getUnreadNotifications().values().stream() @@ -165,10 +173,10 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH } else if (subscriptionUpdate.getNotificationRequestUpdate() != null) { handleNotificationRequestUpdate(subscription, subscriptionUpdate.getNotificationRequestUpdate()); } - sendUpdate(subscription.getSessionId(), subscription.createUpdate()); } private void handleNotificationUpdate(NotificationsCountSubscription subscription, NotificationUpdate update) { + log.trace("[{}, subId: {}] Handling notification update for count sub: {}", subscription.getSessionId(), subscription.getSubscriptionId(), update); Notification notification = update.getNotification(); if (update.isNew()) { subscription.getUnreadCounter().incrementAndGet(); @@ -180,6 +188,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH } private void handleNotificationRequestUpdate(NotificationsCountSubscription subscription, NotificationRequestUpdate update) { + log.trace("[{}, subId: {}] Handling notification request update for count sub: {}", subscription.getSessionId(), subscription.getSubscriptionId(), update); if (update.isDeleted()) { fetchUnreadNotificationsCount(subscription); sendUpdate(subscription.getSessionId(), subscription.createUpdate()); @@ -188,6 +197,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH private void sendUpdate(String sessionId, CmdUpdate update) { + log.trace("[{}] Sending WS update: {}", sessionId, update); wsService.sendWsMsg(sessionId, update); } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsCountUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsCountUpdate.java index c839b36e8d..4f99414be7 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsCountUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsCountUpdate.java @@ -19,10 +19,12 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Builder; import lombok.Getter; +import lombok.ToString; import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdateType; @Getter +@ToString public class UnreadNotificationsCountUpdate extends CmdUpdate { private final int totalUnreadCount; diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsUpdate.java index 0597612569..d6da5f9e7e 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsUpdate.java @@ -19,14 +19,15 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Builder; import lombok.Getter; +import lombok.ToString; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdateType; import java.util.Collection; -import java.util.List; @Getter +@ToString(exclude = "notifications") public class UnreadNotificationsUpdate extends CmdUpdate { private final Collection notifications; diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 1018abe42a..1ef732b8b6 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -786,6 +786,10 @@ CREATE TABLE IF NOT EXISTS notification_target ( configuration varchar(1000) NOT NULL ); +CREATE TABLE IF NOT EXISTS notification_rule ( + id UUID NOT NULL CONSTRAINT notification_rule_pkey PRIMARY KEY +); + CREATE TABLE IF NOT EXISTS notification_request ( id UUID NOT NULL CONSTRAINT notification_request_pkey PRIMARY KEY, created_time BIGINT NOT NULL, From c4d8354fa77ab0731dee14b3eb8d086be68c61a1 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 7 Nov 2022 11:42:58 +0200 Subject: [PATCH 020/488] Notification request scheduling --- .../controller/NotificationController.java | 8 +- ...aultNotificationRuleProcessingService.java | 8 +- .../DefaultNotificationSchedulerService.java | 129 ++++++++++++++++++ ...efaultNotificationSubscriptionService.java | 37 +++-- .../NotificationSchedulerService.java | 27 ++++ .../AbstractPartitionBasedService.java | 5 +- .../queue/DefaultTbCoreConsumerService.java | 28 +++- common/cluster-api/src/main/proto/queue.proto | 10 +- .../NotificationRequestService.java | 42 ++++++ .../dao/notification/NotificationService.java | 16 --- .../server/dao/model/BaseSqlEntity.java | 4 +- .../model/sql/NotificationRequestEntity.java | 4 +- .../DefaultNotificationRequestService.java | 87 ++++++++++++ .../DefaultNotificationService.java | 45 +----- .../notification/NotificationRequestDao.java | 3 + .../JpaNotificationRequestDao.java | 6 + .../NotificationRequestRepository.java | 3 + 17 files changed, 377 insertions(+), 85 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/notification/NotificationSchedulerService.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index a4f44b589d..5c621477a1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.notification.NotificationSubscriptionService; @@ -54,6 +55,7 @@ import java.util.UUID; public class NotificationController extends BaseController { private final NotificationService notificationService; + private final NotificationRequestService notificationRequestService; private final NotificationSubscriptionService notificationSubscriptionService; @GetMapping("/notifications") @@ -102,7 +104,7 @@ public class NotificationController extends BaseController { public NotificationRequest getNotificationRequestById(@PathVariable UUID id, @AuthenticationPrincipal SecurityUser user) { NotificationRequestId notificationRequestId = new NotificationRequestId(id); - return notificationService.findNotificationRequestById(user.getTenantId(), notificationRequestId); + return notificationRequestService.findNotificationRequestById(user.getTenantId(), notificationRequestId); } @GetMapping("/notification/requests") @@ -114,14 +116,14 @@ public class NotificationController extends BaseController { @RequestParam(required = false) String sortOrder, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return notificationService.findNotificationRequestsByTenantId(user.getTenantId(), pageLink); + return notificationRequestService.findNotificationRequestsByTenantId(user.getTenantId(), pageLink); } @DeleteMapping("/notification/request/{id}") public void deleteNotificationRequest(@PathVariable UUID id, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { NotificationRequestId notificationRequestId = new NotificationRequestId(id); - NotificationRequest notificationRequest = notificationService.findNotificationRequestById(user.getTenantId(), notificationRequestId); + NotificationRequest notificationRequest = notificationRequestService.findNotificationRequestById(user.getTenantId(), notificationRequestId); accessControlService.checkPermission(user, Resource.NOTIFICATION_REQUEST, Operation.DELETE, notificationRequestId, notificationRequest); try { notificationSubscriptionService.deleteNotificationRequest(user.getTenantId(), notificationRequestId); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java index 7c459b2e80..a36b5cc8c2 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java @@ -30,8 +30,8 @@ import org.thingsboard.server.common.data.notification.NotificationRequestStatus import org.thingsboard.server.common.data.notification.NotificationSeverity; import org.thingsboard.server.common.data.notification.rule.NonConfirmedNotificationEscalation; import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.notification.NotificationRuleService; -import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.executors.DbCallbackExecutorService; @@ -43,7 +43,7 @@ import java.util.List; public class DefaultNotificationRuleProcessingService implements NotificationRuleProcessingService { private final NotificationRuleService notificationRuleService; - private final NotificationService notificationService; + private final NotificationRequestService notificationRequestService; private final NotificationSubscriptionService notificationSubscriptionService; private final DbCallbackExecutorService dbCallbackExecutorService; @@ -65,7 +65,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul } private void onAlarmUpdate(TenantId tenantId, NotificationRuleId notificationRuleId, Alarm alarm) { - List notificationRequests = notificationService.findNotificationRequestsByRuleIdAndAlarmId(tenantId, notificationRuleId, alarm.getId()); + List notificationRequests = notificationRequestService.findNotificationRequestsByRuleIdAndAlarmId(tenantId, notificationRuleId, alarm.getId()); NotificationRule notificationRule = notificationRuleService.findNotificationRuleById(tenantId, notificationRuleId); if (notificationRequests.isEmpty()) { // in case it is first notification for alarm, or it was previously acked and now we need to send notifications again @@ -80,7 +80,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul for (NotificationRequest notificationRequest : notificationRequests) { if (notificationRequest.getStatus() == NotificationRequestStatus.SCHEDULED) { // using regular service due to no need to send an update to subscription manager - notificationService.deleteNotificationRequestById(tenantId, notificationRequest.getId()); + notificationRequestService.deleteNotificationRequestById(tenantId, notificationRequest.getId()); } else { notificationSubscriptionService.deleteNotificationRequest(tenantId, notificationRequest.getId()); // todo: or should we mark already sent notifications as read? diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java new file mode 100644 index 0000000000..903444529b --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java @@ -0,0 +1,129 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.notification; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListenableScheduledFuture; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationRequestConfig; +import org.thingsboard.server.common.data.page.PageDataIterable; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.dao.notification.NotificationRequestService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.partition.AbstractPartitionBasedService; + +import javax.annotation.PostConstruct; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +@TbCoreComponent +@Service +@RequiredArgsConstructor +@Slf4j +@SuppressWarnings("UnstableApiUsage") +public class DefaultNotificationSchedulerService extends AbstractPartitionBasedService implements NotificationSchedulerService { + + private final NotificationSubscriptionService notificationSubscriptionService; + private final NotificationRequestService notificationRequestService; + + private final Map> scheduledNotificationRequests = new ConcurrentHashMap<>(); + + @PostConstruct + public void init() { + super.init(); + } + + @Override + protected Map>> onAddedPartitions(Set addedPartitions) { + PageDataIterable notificationRequests = new PageDataIterable<>(pageLink -> { + return notificationRequestService.findScheduledNotificationRequests(pageLink); + }, 1000); + for (NotificationRequest notificationRequest : notificationRequests) { + TopicPartitionInfo requestPartition = partitionService.resolve(ServiceType.TB_CORE, notificationRequest.getTenantId(), notificationRequest.getId()); + if (addedPartitions.contains(requestPartition)) { + partitionedEntities.computeIfAbsent(requestPartition, k -> ConcurrentHashMap.newKeySet()).add(notificationRequest.getId()); + if (!scheduledNotificationRequests.containsKey(notificationRequest.getId())) { + scheduleNotificationRequest(notificationRequest.getTenantId(), notificationRequest, notificationRequest.getCreatedTime()); + } + } + } + return Collections.emptyMap(); + } + + @Override + public void scheduleNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId, long requestTs) { + NotificationRequest notificationRequest = notificationRequestService.findNotificationRequestById(tenantId, notificationRequestId); + scheduleNotificationRequest(tenantId, notificationRequest, requestTs); + } + + private void scheduleNotificationRequest(TenantId tenantId, NotificationRequest request, long requestTs) { + int delayInMinutes = Optional.ofNullable(request) + .map(NotificationRequest::getAdditionalConfig) + .map(NotificationRequestConfig::getSendingDelayInMinutes) + .orElse(0); + if (delayInMinutes <= 0) return; // todo: think about: if server was down for some time and delayMs will be negative - need to send these requests as well (but when the value is within some range) + long delayMs = TimeUnit.MINUTES.toMillis(delayInMinutes) - (System.currentTimeMillis() - requestTs); + + ListenableScheduledFuture scheduledTask = scheduledExecutor.schedule(() -> { + NotificationRequest notificationRequest = notificationRequestService.findNotificationRequestById(tenantId, request.getId()); + if (notificationRequest == null) return; + + notificationSubscriptionService.processNotificationRequest(tenantId, notificationRequest); + scheduledNotificationRequests.remove(notificationRequest.getId()); + }, delayMs, TimeUnit.MILLISECONDS); + scheduledNotificationRequests.put(request.getId(), scheduledTask); + } + + @Override + public void onNotificationRequestDeleted(TenantId tenantId, NotificationRequestId notificationRequestId) { + removeAndCancel(notificationRequestId); + } + + @Override + protected void cleanupEntityOnPartitionRemoval(NotificationRequestId notificationRequestId) { + removeAndCancel(notificationRequestId); + } + + private void removeAndCancel(NotificationRequestId notificationRequestId) { + ScheduledFuture scheduledTask = scheduledNotificationRequests.remove(notificationRequestId); + if (scheduledTask != null) { + scheduledTask.cancel(false); + } + } + + @Override + protected String getServiceName() { + return "Notifications scheduler"; + } + + @Override + protected String getSchedulerExecutorName() { + return "notifications-scheduler"; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSubscriptionService.java index 74fb400fb2..8c786f33c9 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSubscriptionService.java @@ -35,6 +35,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.gen.transport.TransportProtos; @@ -56,17 +57,20 @@ import java.util.UUID; public class DefaultNotificationSubscriptionService extends AbstractSubscriptionService implements NotificationSubscriptionService, RuleEngineNotificationService { private final NotificationTargetService notificationTargetService; + private final NotificationRequestService notificationRequestService; private final NotificationService notificationService; private final DbCallbackExecutorService dbCallbackExecutorService; private final NotificationsTopicService notificationsTopicService; public DefaultNotificationSubscriptionService(TbClusterService clusterService, PartitionService partitionService, NotificationTargetService notificationTargetService, + NotificationRequestService notificationRequestService, NotificationService notificationService, DbCallbackExecutorService dbCallbackExecutorService, NotificationsTopicService notificationsTopicService) { super(clusterService, partitionService); this.notificationTargetService = notificationTargetService; + this.notificationRequestService = notificationRequestService; this.notificationService = notificationService; this.dbCallbackExecutorService = dbCallbackExecutorService; this.notificationsTopicService = notificationsTopicService; @@ -77,18 +81,18 @@ public class DefaultNotificationSubscriptionService extends AbstractSubscription log.info("Processing notification request (tenant id: {}, notification target id: {})", tenantId, notificationRequest.getTargetId()); notificationRequest.setTenantId(tenantId); if (notificationRequest.getAdditionalConfig() != null) { + // TODO: think about notification request update NotificationRequestConfig config = notificationRequest.getAdditionalConfig(); - if (config.getSendingDelayInMinutes() > 0) { + if (config.getSendingDelayInMinutes() > 0 && notificationRequest.getId() == null) { notificationRequest.setStatus(NotificationRequestStatus.SCHEDULED); - - // todo: delayed sending; check all delayed notification requests on start up, schedule send + NotificationRequest savedNotificationRequest = notificationRequestService.saveNotificationRequest(tenantId, notificationRequest); + forwardToNotificationSchedulerService(tenantId, savedNotificationRequest.getId(), false); + return savedNotificationRequest; } } - if (notificationRequest.getStatus() == null) { - notificationRequest.setStatus(NotificationRequestStatus.PROCESSED); - } - NotificationRequest savedNotificationRequest = notificationService.saveNotificationRequest(tenantId, notificationRequest); + notificationRequest.setStatus(NotificationRequestStatus.PROCESSED); + NotificationRequest savedNotificationRequest = notificationRequestService.saveNotificationRequest(tenantId, notificationRequest); DaoUtil.processBatches(pageLink -> { return notificationTargetService.findRecipientsForNotificationTarget(tenantId, notificationRequest.getTargetId(), pageLink); @@ -109,6 +113,20 @@ public class DefaultNotificationSubscriptionService extends AbstractSubscription return savedNotificationRequest; } + private void forwardToNotificationSchedulerService(TenantId tenantId, NotificationRequestId notificationRequestId, boolean deleted) { + TransportProtos.NotificationSchedulerServiceMsg.Builder msg = TransportProtos.NotificationSchedulerServiceMsg.newBuilder() + .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) + .setRequestIdMSB(notificationRequestId.getId().getMostSignificantBits()) + .setRequestIdLSB(notificationRequestId.getId().getLeastSignificantBits()) + .setTs(System.currentTimeMillis()) + .setDeleted(deleted); + TransportProtos.ToCoreMsg toCoreMsg = TransportProtos.ToCoreMsg.newBuilder() + .setNotificationSchedulerServiceMsg(msg) + .build(); + clusterService.pushMsgToCore(tenantId, notificationRequestId, toCoreMsg, null); + } + @Override public void markNotificationAsRead(TenantId tenantId, UserId recipientId, NotificationId notificationId) { boolean updated = notificationService.markNotificationAsRead(tenantId, recipientId, notificationId); @@ -122,17 +140,18 @@ public class DefaultNotificationSubscriptionService extends AbstractSubscription @Override public void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId) { log.debug("Deleting notification request {}", notificationRequestId); - notificationService.deleteNotificationRequestById(tenantId, notificationRequestId); + notificationRequestService.deleteNotificationRequestById(tenantId, notificationRequestId); onNotificationRequestUpdate(tenantId, NotificationRequestUpdate.builder() .notificationRequestId(notificationRequestId) .deleted(true) .build()); + forwardToNotificationSchedulerService(tenantId, notificationRequestId, true); } @Override public void updateNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { log.debug("Updating notification request {}", notificationRequest.getId()); - notificationService.saveNotificationRequest(tenantId, notificationRequest); + notificationRequestService.saveNotificationRequest(tenantId, notificationRequest); notificationService.updateNotificationsInfosByRequestId(tenantId, notificationRequest.getId(), notificationRequest.getNotificationInfo()); onNotificationRequestUpdate(tenantId, NotificationRequestUpdate.builder() .notificationRequestId(notificationRequest.getId()) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/NotificationSchedulerService.java b/application/src/main/java/org/thingsboard/server/service/notification/NotificationSchedulerService.java new file mode 100644 index 0000000000..ce018f171c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/notification/NotificationSchedulerService.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.notification; + +import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.TenantId; + +public interface NotificationSchedulerService { + + void scheduleNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId, long requestTs); + + void onNotificationRequestDeleted(TenantId tenantId, NotificationRequestId notificationRequestId); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/partition/AbstractPartitionBasedService.java b/application/src/main/java/org/thingsboard/server/service/partition/AbstractPartitionBasedService.java index 305696662b..489b1dcb48 100644 --- a/application/src/main/java/org/thingsboard/server/service/partition/AbstractPartitionBasedService.java +++ b/application/src/main/java/org/thingsboard/server/service/partition/AbstractPartitionBasedService.java @@ -20,16 +20,17 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningScheduledExecutorService; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbApplicationEventListener; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import java.util.ArrayList; -import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -48,6 +49,8 @@ public abstract class AbstractPartitionBasedService extends protected final ConcurrentMap>> partitionedFetchTasks = new ConcurrentHashMap<>(); final Queue> subscribeQueue = new ConcurrentLinkedQueue<>(); + @Autowired + protected PartitionService partitionService; protected ListeningScheduledExecutorService scheduledExecutor; abstract protected String getServiceName(); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index cf93ea7f1d..60a9567488 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -31,8 +31,6 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; -import org.thingsboard.server.common.data.notification.Notification; -import org.thingsboard.server.common.data.notification.NotificationInfo; import org.thingsboard.server.common.data.rpc.RpcError; import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.TbActorMsg; @@ -69,6 +67,7 @@ import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.edge.EdgeNotificationService; +import org.thingsboard.server.service.notification.NotificationSchedulerService; import org.thingsboard.server.service.ota.OtaPackageStateService; import org.thingsboard.server.service.profile.TbAssetProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache; @@ -127,6 +126,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService> usageStatsConsumer; private final TbQueueConsumer> firmwareStatesConsumer; @@ -151,7 +151,8 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService findNotificationRequestsByTenantId(TenantId tenantId, PageLink pageLink); + + List findNotificationRequestsByRuleIdAndAlarmId(TenantId tenantId, NotificationRuleId ruleId, AlarmId alarmId); + + void deleteNotificationRequestById(TenantId tenantId, NotificationRequestId id); + + PageData findScheduledNotificationRequests(PageLink pageLink); + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java index 6b7f6267db..498e3dfa3f 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java @@ -15,33 +15,17 @@ */ package org.thingsboard.server.dao.notification; -import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; -import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationInfo; -import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import java.util.List; - public interface NotificationService { - NotificationRequest saveNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest); - - NotificationRequest findNotificationRequestById(TenantId tenantId, NotificationRequestId id); - - PageData findNotificationRequestsByTenantId(TenantId tenantId, PageLink pageLink); - - List findNotificationRequestsByRuleIdAndAlarmId(TenantId tenantId, NotificationRuleId ruleId, AlarmId alarmId); - - void deleteNotificationRequestById(TenantId tenantId, NotificationRequestId id); - - Notification saveNotification(TenantId tenantId, Notification notification); Notification findNotificationById(TenantId tenantId, NotificationId notificationId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java index 5256eee7db..d639192917 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java @@ -86,8 +86,8 @@ public abstract class BaseSqlEntity implements BaseEntity { } } - protected T fromJson(JsonNode json) { - return JacksonUtil.convertValue(json, new TypeReference() {}); + protected T fromJson(JsonNode json, Class type) { + return JacksonUtil.convertValue(json, type); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java index cd0bbd435a..3c7b525628 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java @@ -110,9 +110,9 @@ public class NotificationRequestEntity extends BaseSqlEntity findNotificationRequestsByTenantId(TenantId tenantId, PageLink pageLink) { + return notificationRequestDao.findByTenantIdAndPageLink(tenantId, pageLink); + } + + @Override + public List findNotificationRequestsByRuleIdAndAlarmId(TenantId tenantId, NotificationRuleId ruleId, AlarmId alarmId) { + return notificationRequestDao.findByRuleIdAndAlarmId(tenantId, ruleId, alarmId); + } + + // ON DELETE CASCADE is used: notifications for request are deleted as well + @Override + public void deleteNotificationRequestById(TenantId tenantId, NotificationRequestId id) { + notificationRequestDao.removeById(tenantId, id.getId()); + } + + @Override + public PageData findScheduledNotificationRequests(PageLink pageLink) { + return notificationRequestDao.findAllByStatus(NotificationRequestStatus.SCHEDULED, pageLink); + } + + + private static class NotificationRequestValidator extends DataValidator { + + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java index a3ef857233..37a45a3a72 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationInfo; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.notification.NotificationSeverity; import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.data.page.PageData; @@ -43,44 +44,8 @@ import java.util.List; @RequiredArgsConstructor public class DefaultNotificationService implements NotificationService { - private final NotificationRequestDao notificationRequestDao; private final NotificationDao notificationDao; - private final NotificationRequestValidator notificationRequestValidator = new NotificationRequestValidator(); - - @Override - public NotificationRequest saveNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { - if (StringUtils.isBlank(notificationRequest.getNotificationReason())) { - notificationRequest.setNotificationReason(NotificationRequest.GENERAL_NOTIFICATION_REASON); - } - if (notificationRequest.getNotificationSeverity() == null) { - notificationRequest.setNotificationSeverity(NotificationSeverity.NORMAL); - } - notificationRequestValidator.validate(notificationRequest, NotificationRequest::getTenantId); - return notificationRequestDao.save(tenantId, notificationRequest); - } - - @Override - public NotificationRequest findNotificationRequestById(TenantId tenantId, NotificationRequestId id) { - return notificationRequestDao.findById(tenantId, id.getId()); - } - - @Override - public PageData findNotificationRequestsByTenantId(TenantId tenantId, PageLink pageLink) { - return notificationRequestDao.findByTenantIdAndPageLink(tenantId, pageLink); - } - - @Override - public List findNotificationRequestsByRuleIdAndAlarmId(TenantId tenantId, NotificationRuleId ruleId, AlarmId alarmId) { - return notificationRequestDao.findByRuleIdAndAlarmId(tenantId, ruleId, alarmId); - } - - // ON DELETE CASCADE is used: notifications for request are deleted as well - @Override - public void deleteNotificationRequestById(TenantId tenantId, NotificationRequestId id) { - notificationRequestDao.removeById(tenantId, id.getId()); - } - @Override public Notification saveNotification(TenantId tenantId, Notification notification) { return notificationDao.save(tenantId, notification); @@ -122,12 +87,4 @@ public class DefaultNotificationService implements NotificationService { return notificationDao.updateInfosByRequestId(tenantId, notificationRequestId, notificationInfo); } - private static class NotificationRequestValidator extends DataValidator { - - @Override - protected void validateDataImpl(TenantId tenantId, NotificationRequest notificationRequest) { - } - - } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java index 1848e826e0..76778c64b5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java @@ -19,6 +19,7 @@ import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; @@ -31,4 +32,6 @@ public interface NotificationRequestDao extends Dao { List findByRuleIdAndAlarmId(TenantId tenantId, NotificationRuleId ruleId, AlarmId alarmId); + PageData findAllByStatus(NotificationRequestStatus status, PageLink pageLink); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java index 5e1ca5d3f7..7684ed5865 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.DaoUtil; @@ -52,6 +53,11 @@ public class JpaNotificationRequestDao extends JpaAbstractDao findAllByStatus(NotificationRequestStatus status, PageLink pageLink) { + return DaoUtil.toPageData(notificationRequestRepository.findAllByStatus(status, DaoUtil.toPageable(pageLink))); + } + @Override protected Class getEntityClass() { return NotificationRequestEntity.class; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java index 4ff69f8797..f892767a83 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java @@ -21,6 +21,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.dao.model.sql.NotificationRequestEntity; import java.util.List; @@ -37,4 +38,6 @@ public interface NotificationRequestRepository extends JpaRepository findAllByRuleIdAndAlarmId(UUID ruleId, UUID alarmId); + Page findAllByStatus(NotificationRequestStatus status, Pageable pageable); + } From 48023495a850801516345721db467220975fc217 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 7 Nov 2022 13:50:16 +0200 Subject: [PATCH 021/488] Notification api refactoring --- .../main/data/upgrade/3.4.2/schema_update.sql | 18 +++--- .../server/actors/ActorSystemContext.java | 4 +- .../actors/ruleChain/DefaultTbContext.java | 6 +- .../controller/NotificationController.java | 29 ++++++++-- .../controller/plugin/TbWebSocketHandler.java | 4 +- ...e.java => DefaultNotificationManager.java} | 18 +++--- ...aultNotificationRuleProcessingService.java | 20 ++++--- .../DefaultNotificationSchedulerService.java | 5 +- ...efaultTbEntityDataSubscriptionService.java | 2 +- .../subscription/TbAbstractDataSubCtx.java | 2 +- .../subscription/TbAbstractSubCtx.java | 2 +- .../subscription/TbAlarmDataSubCtx.java | 2 +- .../subscription/TbEntityCountSubCtx.java | 2 +- .../subscription/TbEntityDataSubCtx.java | 2 +- .../DefaultWebSocketService.java | 55 ++++++++++--------- .../ws/{telemetry => }/WebSocketService.java | 2 +- .../DefaultNotificationCommandsHandler.java | 31 ++++++----- .../NotificationCommandsHandler.java | 4 +- ...d.java => MarkNotificationsAsReadCmd.java} | 6 +- .../cmd/NotificationCmdsWrapper.java | 3 +- .../cmd/NotificationsCountSubCmd.java | 2 - .../notification/cmd/NotificationsSubCmd.java | 2 - .../cmd/NotificationsUnsubCmd.java | 1 - .../ws/{ => notification/cmd}/WsCmd.java | 2 +- ...sApiTest.java => NotificationApiTest.java} | 14 ++--- ...ient.java => NotificationApiWsClient.java} | 11 ++-- .../NotificationRequestService.java | 4 +- .../AlarmOriginatedNotificationInfo.java | 46 ++++++++++++++++ .../data/notification/Notification.java | 2 +- .../data/notification/NotificationInfo.java | 27 +++++---- .../NotificationOriginatorType.java | 13 ++--- .../notification/NotificationRequest.java | 22 ++++---- .../server/dao/model/ModelConstants.java | 5 +- .../dao/model/sql/NotificationEntity.java | 7 +++ .../model/sql/NotificationRequestEntity.java | 38 +++++++++---- .../DefaultNotificationRequestService.java | 14 +---- .../notification/NotificationRequestDao.java | 4 +- .../JpaNotificationRequestDao.java | 6 +- .../NotificationRequestRepository.java | 3 +- .../dao/sql/query/AlarmDataAdapter.java | 4 ++ .../query/DefaultAlarmQueryRepository.java | 1 + .../resources/sql/schema-entities-idx.sql | 8 +-- .../main/resources/sql/schema-entities.sql | 12 ++-- .../rule/engine/api/NotificationManager.java | 4 +- .../rule/engine/api/TbContext.java | 2 +- .../notification/TbNotificationNode.java | 5 +- 46 files changed, 291 insertions(+), 185 deletions(-) rename application/src/main/java/org/thingsboard/server/service/notification/{DefaultNotificationSubscriptionService.java => DefaultNotificationManager.java} (92%) rename application/src/main/java/org/thingsboard/server/service/{telemetry => ws}/DefaultWebSocketService.java (95%) rename application/src/main/java/org/thingsboard/server/service/ws/{telemetry => }/WebSocketService.java (96%) rename application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/{MarkNotificationAsReadCmd.java => MarkNotificationsAsReadCmd.java} (86%) rename application/src/main/java/org/thingsboard/server/service/ws/{ => notification/cmd}/WsCmd.java (91%) rename application/src/test/java/org/thingsboard/server/service/notification/{NotificationsWsApiTest.java => NotificationApiTest.java} (94%) rename application/src/test/java/org/thingsboard/server/service/notification/{NotificationsWebSocketClient.java => NotificationApiWsClient.java} (89%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/AlarmOriginatedNotificationInfo.java rename rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineNotificationService.java => common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationOriginatorType.java (64%) rename application/src/main/java/org/thingsboard/server/service/notification/NotificationSubscriptionService.java => rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationManager.java (92%) diff --git a/application/src/main/data/upgrade/3.4.2/schema_update.sql b/application/src/main/data/upgrade/3.4.2/schema_update.sql index 7294916445..01113f2324 100644 --- a/application/src/main/data/upgrade/3.4.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.4.2/schema_update.sql @@ -14,7 +14,6 @@ -- limitations under the License. -- - CREATE TABLE IF NOT EXISTS notification_target ( id UUID NOT NULL CONSTRAINT notification_target_pkey PRIMARY KEY, created_time BIGINT NOT NULL, @@ -22,12 +21,14 @@ CREATE TABLE IF NOT EXISTS notification_target ( name VARCHAR(255) NOT NULL, configuration varchar(1000) NOT NULL ); -CREATE INDEX IF NOT EXISTS idx_notification_target_tenant_id_and_created_time ON notification_target(tenant_id, created_time DESC); +CREATE INDEX IF NOT EXISTS idx_notification_target_tenant_id_created_time ON notification_target(tenant_id, created_time DESC); CREATE TABLE IF NOT EXISTS notification_rule ( id UUID NOT NULL CONSTRAINT notification_rule_pkey PRIMARY KEY ); +ALTER TABLE alarm ADD COLUMN IF NOT EXISTS notification_rule_id UUID; + CREATE TABLE IF NOT EXISTS notification_request ( id UUID NOT NULL CONSTRAINT notification_request_pkey PRIMARY KEY, created_time BIGINT NOT NULL, @@ -37,12 +38,14 @@ CREATE TABLE IF NOT EXISTS notification_request ( text_template VARCHAR NOT NULL, notification_info VARCHAR(1000), notification_severity VARCHAR(32), - additional_config VARCHAR(1000), - status VARCHAR(32), + originator_type VARCHAR(32) NOT NULL, + originator_entity_id UUID, + originator_entity_type VARCHAR(32), rule_id UUID NULL CONSTRAINT fk_notification_request_rule_id REFERENCES notification_rule(id), - alarm_id UUID + additional_config VARCHAR(1000), + status VARCHAR(32) ); -CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id_and_created_time ON notification_request(tenant_id, created_time DESC); +CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id_originator_type_created_time ON notification_request(tenant_id, originator_type, created_time DESC); CREATE TABLE IF NOT EXISTS notification ( id UUID NOT NULL, @@ -54,8 +57,9 @@ CREATE TABLE IF NOT EXISTS notification ( text VARCHAR NOT NULL, info VARCHAR(1000), severity VARCHAR(32), + originator_type VARCHAR(32) NOT NULL, status VARCHAR(32) ) PARTITION BY RANGE (created_time); CREATE INDEX IF NOT EXISTS idx_notification_id ON notification(id); +CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_created_time ON notification(recipient_id, created_time DESC); CREATE INDEX IF NOT EXISTS idx_notification_notification_request_id ON notification(request_id); -CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_and_created_time ON notification(recipient_id, created_time DESC); diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index 1bfb515811..52cd33a885 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -30,7 +30,7 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.thingsboard.rule.engine.api.MailService; -import org.thingsboard.rule.engine.api.RuleEngineNotificationService; +import org.thingsboard.rule.engine.api.NotificationManager; import org.thingsboard.rule.engine.api.SmsService; import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; import org.thingsboard.script.api.js.JsInvokeService; @@ -309,7 +309,7 @@ public class ActorSystemContext { @Autowired @Getter - private RuleEngineNotificationService notificationService; + private NotificationManager notificationManager; @Lazy @Autowired(required = false) diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index f623a9ad4c..bc43e69284 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -25,10 +25,10 @@ import org.bouncycastle.util.Arrays; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ListeningExecutor; import org.thingsboard.rule.engine.api.MailService; +import org.thingsboard.rule.engine.api.NotificationManager; import org.thingsboard.rule.engine.api.RuleEngineAlarmService; import org.thingsboard.rule.engine.api.RuleEngineAssetProfileCache; import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache; -import org.thingsboard.rule.engine.api.RuleEngineNotificationService; import org.thingsboard.rule.engine.api.RuleEngineRpcService; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.ScriptEngine; @@ -665,8 +665,8 @@ class DefaultTbContext implements TbContext { } @Override - public RuleEngineNotificationService getNotificationService() { - return mainCtx.getNotificationService(); + public NotificationManager getNotificationManager() { + return mainCtx.getNotificationManager(); } @Override diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index 5c621477a1..a392e6ec74 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -17,6 +17,7 @@ package org.thingsboard.server.controller; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; @@ -34,13 +35,15 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationOriginatorType; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationSeverity; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.notification.NotificationSubscriptionService; +import org.thingsboard.rule.engine.api.NotificationManager; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; @@ -56,7 +59,7 @@ public class NotificationController extends BaseController { private final NotificationService notificationService; private final NotificationRequestService notificationRequestService; - private final NotificationSubscriptionService notificationSubscriptionService; + private final NotificationManager notificationManager; @GetMapping("/notifications") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -76,7 +79,7 @@ public class NotificationController extends BaseController { public void markNotificationAsRead(@PathVariable UUID id, @AuthenticationPrincipal SecurityUser user) { NotificationId notificationId = new NotificationId(id); - notificationSubscriptionService.markNotificationAsRead(user.getTenantId(), user.getId(), notificationId); + notificationManager.markNotificationAsRead(user.getTenantId(), user.getId(), notificationId); } // delete notification? @@ -86,11 +89,27 @@ public class NotificationController extends BaseController { public NotificationRequest createNotificationRequest(@RequestBody NotificationRequest notificationRequest, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { accessControlService.checkPermission(user, Resource.NOTIFICATION_REQUEST, Operation.CREATE, null, notificationRequest); + // todo: check permission for notification target if (notificationRequest.getId() != null) { + // TODO: think about notification request update throw new IllegalArgumentException("Notification request cannot be changed. You can delete it and create a new one"); } + notificationRequest.setOriginatorType(NotificationOriginatorType.USER); + notificationRequest.setOriginatorEntityId(user.getId()); + if (StringUtils.isBlank(notificationRequest.getNotificationReason())) { + notificationRequest.setNotificationReason(NotificationRequest.GENERAL_NOTIFICATION_REASON); + } + if (notificationRequest.getNotificationSeverity() == null) { + notificationRequest.setNotificationSeverity(NotificationSeverity.NORMAL); + } + if (notificationRequest.getNotificationInfo() != null && notificationRequest.getNotificationInfo().getOriginatorType() != null) { + throw new IllegalArgumentException("Unsupported notification info type"); + } + notificationRequest.setRuleId(null); + notificationRequest.setStatus(null); + try { - NotificationRequest savedNotificationRequest = notificationSubscriptionService.processNotificationRequest(user.getTenantId(), notificationRequest); + NotificationRequest savedNotificationRequest = notificationManager.processNotificationRequest(user.getTenantId(), notificationRequest); logEntityAction(user, EntityType.NOTIFICATION_REQUEST, savedNotificationRequest, ActionType.ADDED); return savedNotificationRequest; } catch (Exception e) { @@ -126,7 +145,7 @@ public class NotificationController extends BaseController { NotificationRequest notificationRequest = notificationRequestService.findNotificationRequestById(user.getTenantId(), notificationRequestId); accessControlService.checkPermission(user, Resource.NOTIFICATION_REQUEST, Operation.DELETE, notificationRequestId, notificationRequest); try { - notificationSubscriptionService.deleteNotificationRequest(user.getTenantId(), notificationRequestId); + notificationManager.deleteNotificationRequest(user.getTenantId(), notificationRequestId); logEntityAction(user, EntityType.NOTIFICATION_REQUEST, notificationRequest, ActionType.DELETED); } catch (Exception e) { logEntityAction(user, EntityType.NOTIFICATION_REQUEST, notificationRequest, notificationRequest, ActionType.DELETED, e); diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index 7cb0a8258e..edd6843c2f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -42,7 +42,7 @@ import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.ws.SessionEvent; import org.thingsboard.server.service.ws.WebSocketMsgEndpoint; import org.thingsboard.server.service.ws.WebSocketSessionType; -import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.WebSocketService; import org.thingsboard.server.service.ws.WebSocketSessionRef; import javax.websocket.RemoteEndpoint; @@ -59,7 +59,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.LinkedBlockingQueue; -import static org.thingsboard.server.service.telemetry.DefaultWebSocketService.NUMBER_OF_PING_ATTEMPTS; +import static org.thingsboard.server.service.ws.DefaultWebSocketService.NUMBER_OF_PING_ATTEMPTS; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationManager.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSubscriptionService.java rename to application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationManager.java index 8c786f33c9..4bb8afc61d 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationManager.java @@ -18,7 +18,7 @@ package org.thingsboard.server.service.notification; import com.google.common.base.Strings; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import org.thingsboard.rule.engine.api.RuleEngineNotificationService; +import org.thingsboard.rule.engine.api.NotificationManager; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.User; @@ -54,7 +54,7 @@ import java.util.UUID; @Service @Slf4j -public class DefaultNotificationSubscriptionService extends AbstractSubscriptionService implements NotificationSubscriptionService, RuleEngineNotificationService { +public class DefaultNotificationManager extends AbstractSubscriptionService implements NotificationManager { private final NotificationTargetService notificationTargetService; private final NotificationRequestService notificationRequestService; @@ -62,12 +62,12 @@ public class DefaultNotificationSubscriptionService extends AbstractSubscription private final DbCallbackExecutorService dbCallbackExecutorService; private final NotificationsTopicService notificationsTopicService; - public DefaultNotificationSubscriptionService(TbClusterService clusterService, PartitionService partitionService, - NotificationTargetService notificationTargetService, - NotificationRequestService notificationRequestService, - NotificationService notificationService, - DbCallbackExecutorService dbCallbackExecutorService, - NotificationsTopicService notificationsTopicService) { + public DefaultNotificationManager(TbClusterService clusterService, PartitionService partitionService, + NotificationTargetService notificationTargetService, + NotificationRequestService notificationRequestService, + NotificationService notificationService, + DbCallbackExecutorService dbCallbackExecutorService, + NotificationsTopicService notificationsTopicService) { super(clusterService, partitionService); this.notificationTargetService = notificationTargetService; this.notificationRequestService = notificationRequestService; @@ -81,7 +81,6 @@ public class DefaultNotificationSubscriptionService extends AbstractSubscription log.info("Processing notification request (tenant id: {}, notification target id: {})", tenantId, notificationRequest.getTargetId()); notificationRequest.setTenantId(tenantId); if (notificationRequest.getAdditionalConfig() != null) { - // TODO: think about notification request update NotificationRequestConfig config = notificationRequest.getAdditionalConfig(); if (config.getSendingDelayInMinutes() > 0 && notificationRequest.getId() == null) { notificationRequest.setStatus(NotificationRequestStatus.SCHEDULED); @@ -169,6 +168,7 @@ public class DefaultNotificationSubscriptionService extends AbstractSubscription .text(formatNotificationText(notificationRequest.getTextTemplate(), recipient)) .info(notificationRequest.getNotificationInfo()) .severity(notificationRequest.getNotificationSeverity()) + .originatorType(notificationRequest.getOriginatorType()) .status(NotificationStatus.SENT) .build(); return notificationService.saveNotification(recipient.getTenantId(), notification); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java index a36b5cc8c2..ac7e875e03 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java @@ -19,11 +19,14 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.thingsboard.rule.engine.api.NotificationManager; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.AlarmOriginatedNotificationInfo; import org.thingsboard.server.common.data.notification.NotificationInfo; +import org.thingsboard.server.common.data.notification.NotificationOriginatorType; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequestConfig; import org.thingsboard.server.common.data.notification.NotificationRequestStatus; @@ -44,7 +47,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul private final NotificationRuleService notificationRuleService; private final NotificationRequestService notificationRequestService; - private final NotificationSubscriptionService notificationSubscriptionService; + private final NotificationManager notificationManager; private final DbCallbackExecutorService dbCallbackExecutorService; @Override @@ -65,7 +68,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul } private void onAlarmUpdate(TenantId tenantId, NotificationRuleId notificationRuleId, Alarm alarm) { - List notificationRequests = notificationRequestService.findNotificationRequestsByRuleIdAndAlarmId(tenantId, notificationRuleId, alarm.getId()); + List notificationRequests = notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityId(tenantId, notificationRuleId, alarm.getId()); NotificationRule notificationRule = notificationRuleService.findNotificationRuleById(tenantId, notificationRuleId); if (notificationRequests.isEmpty()) { // in case it is first notification for alarm, or it was previously acked and now we need to send notifications again @@ -82,7 +85,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul // using regular service due to no need to send an update to subscription manager notificationRequestService.deleteNotificationRequestById(tenantId, notificationRequest.getId()); } else { - notificationSubscriptionService.deleteNotificationRequest(tenantId, notificationRequest.getId()); + notificationManager.deleteNotificationRequest(tenantId, notificationRequest.getId()); // todo: or should we mark already sent notifications as read? } } @@ -92,7 +95,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul NotificationInfo previousNotificationInfo = notificationRequest.getNotificationInfo(); if (!previousNotificationInfo.equals(newNotificationInfo)) { notificationRequest.setNotificationInfo(newNotificationInfo); - notificationSubscriptionService.updateNotificationRequest(tenantId, notificationRequest); + notificationManager.updateNotificationRequest(tenantId, notificationRequest); } // fixme: no need to send an update event for scheduled requests, only for sent } @@ -118,15 +121,16 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul .textTemplate(notificationRule.getNotificationTextTemplate()) // todo: format with alarm vars .notificationInfo(notificationInfo) .notificationSeverity(NotificationSeverity.NORMAL) // todo: from alarm severity - .additionalConfig(config) + .originatorType(NotificationOriginatorType.ALARM) + .originatorEntityId(alarm.getId()) .ruleId(notificationRule.getId()) - .alarmId(alarm.getId()) + .additionalConfig(config) .build(); - notificationSubscriptionService.processNotificationRequest(tenantId, notificationRequest); + notificationManager.processNotificationRequest(tenantId, notificationRequest); } private NotificationInfo constructNotificationInfo(Alarm alarm, NotificationRule notificationRule) { - return NotificationInfo.builder() + return AlarmOriginatedNotificationInfo.builder() .alarmId(alarm.getId()) .alarmType(alarm.getType()) .alarmOriginator(alarm.getOriginator()) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java index 903444529b..36faeeee87 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java @@ -20,6 +20,7 @@ import com.google.common.util.concurrent.ListenableScheduledFuture; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.thingsboard.rule.engine.api.NotificationManager; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationRequest; @@ -48,7 +49,7 @@ import java.util.concurrent.TimeUnit; @SuppressWarnings("UnstableApiUsage") public class DefaultNotificationSchedulerService extends AbstractPartitionBasedService implements NotificationSchedulerService { - private final NotificationSubscriptionService notificationSubscriptionService; + private final NotificationManager notificationManager; private final NotificationRequestService notificationRequestService; private final Map> scheduledNotificationRequests = new ConcurrentHashMap<>(); @@ -93,7 +94,7 @@ public class DefaultNotificationSchedulerService extends AbstractPartitionBasedS NotificationRequest notificationRequest = notificationRequestService.findNotificationRequestById(tenantId, request.getId()); if (notificationRequest == null) return; - notificationSubscriptionService.processNotificationRequest(tenantId, notificationRequest); + notificationManager.processNotificationRequest(tenantId, notificationRequest); scheduledNotificationRequests.remove(notificationRequest.getId()); }, delayMs, TimeUnit.MILLISECONDS); scheduledNotificationRequests.put(request.getId(), scheduledTask); diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java index eed12acf3e..4661730f9c 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java @@ -48,7 +48,7 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.executors.DbCallbackExecutorService; -import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.WebSocketService; import org.thingsboard.server.service.ws.WebSocketSessionRef; import org.thingsboard.server.service.ws.telemetry.cmd.v2.AggHistoryCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.AggKey; diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java index 7ff8bf931e..11fa6b4c35 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java @@ -29,7 +29,7 @@ import org.thingsboard.server.common.data.query.TsValue; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.service.ws.WebSocketSessionRef; -import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.WebSocketService; import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; import java.util.ArrayList; diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java index d7fde23b31..8dd90da5dc 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java @@ -39,7 +39,7 @@ import org.thingsboard.server.common.data.query.TsValue; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.service.ws.WebSocketSessionRef; -import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.WebSocketService; import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate; import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java index c66cdc4828..a952a01ee9 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java @@ -38,7 +38,7 @@ import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.WebSocketService; import org.thingsboard.server.service.ws.WebSocketSessionRef; import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataUpdate; import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate; diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityCountSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityCountSubCtx.java index f0cc2260b6..573016c20c 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityCountSubCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityCountSubCtx.java @@ -19,7 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.entity.EntityService; -import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.WebSocketService; import org.thingsboard.server.service.ws.WebSocketSessionRef; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUpdate; diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java index 975ecea1c2..f5c701916d 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java @@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.query.EntityKeyType; import org.thingsboard.server.common.data.query.TsValue; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.entity.EntityService; -import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.WebSocketService; import org.thingsboard.server.service.ws.WebSocketSessionRef; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java similarity index 95% rename from application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java rename to application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java index e03e0fc3fb..2598429672 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.telemetry; +package org.thingsboard.server.service.ws; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -62,14 +62,9 @@ import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope; import org.thingsboard.server.service.subscription.TbEntityDataSubscriptionService; import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; import org.thingsboard.server.service.subscription.TbTimeseriesSubscription; -import org.thingsboard.server.service.ws.SessionEvent; -import org.thingsboard.server.service.ws.WebSocketMsgEndpoint; -import org.thingsboard.server.service.ws.WebSocketSessionRef; -import org.thingsboard.server.service.ws.WsCmd; -import org.thingsboard.server.service.ws.WsSessionMetaData; import org.thingsboard.server.service.ws.notification.NotificationCommandsHandler; import org.thingsboard.server.service.ws.notification.cmd.NotificationCmdsWrapper; -import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.notification.cmd.WsCmd; import org.thingsboard.server.service.ws.telemetry.cmd.TelemetryPluginCmdsWrapper; import org.thingsboard.server.service.ws.telemetry.cmd.v1.AttributesSubscriptionCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v1.GetHistoryCmd; @@ -163,22 +158,22 @@ public class DefaultWebSocketService implements WebSocketService { pingExecutor.scheduleWithFixedDelay(this::sendPing, pingTimeout / NUMBER_OF_PING_ATTEMPTS, pingTimeout / NUMBER_OF_PING_ATTEMPTS, TimeUnit.MILLISECONDS); telemetryCmdsHandlers = List.of( - WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getAttrSubCmds, this::handleWsAttributesSubscriptionCmd), - WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getTsSubCmds, this::handleWsTimeseriesSubscriptionCmd), - WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getHistoryCmds, this::handleWsHistoryCmd), - WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getEntityDataCmds, this::handleWsEntityDataCmd), - WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getAlarmDataCmds, this::handleWsAlarmDataCmd), - WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getEntityCountCmds, this::handleWsEntityCountCmd), - WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getEntityDataUnsubscribeCmds, this::handleWsDataUnsubscribeCmd), - WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getAlarmDataUnsubscribeCmds, this::handleWsDataUnsubscribeCmd), - WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getAlarmDataUnsubscribeCmds, this::handleWsDataUnsubscribeCmd), - WsCmdListHandler.of(TelemetryPluginCmdsWrapper::getEntityCountUnsubscribeCmds, this::handleWsDataUnsubscribeCmd) + newCmdsHandler(TelemetryPluginCmdsWrapper::getAttrSubCmds, this::handleWsAttributesSubscriptionCmd), + newCmdsHandler(TelemetryPluginCmdsWrapper::getTsSubCmds, this::handleWsTimeseriesSubscriptionCmd), + newCmdsHandler(TelemetryPluginCmdsWrapper::getHistoryCmds, this::handleWsHistoryCmd), + newCmdsHandler(TelemetryPluginCmdsWrapper::getEntityDataCmds, this::handleWsEntityDataCmd), + newCmdsHandler(TelemetryPluginCmdsWrapper::getAlarmDataCmds, this::handleWsAlarmDataCmd), + newCmdsHandler(TelemetryPluginCmdsWrapper::getEntityCountCmds, this::handleWsEntityCountCmd), + newCmdsHandler(TelemetryPluginCmdsWrapper::getEntityDataUnsubscribeCmds, this::handleWsDataUnsubscribeCmd), + newCmdsHandler(TelemetryPluginCmdsWrapper::getAlarmDataUnsubscribeCmds, this::handleWsDataUnsubscribeCmd), + newCmdsHandler(TelemetryPluginCmdsWrapper::getAlarmDataUnsubscribeCmds, this::handleWsDataUnsubscribeCmd), + newCmdsHandler(TelemetryPluginCmdsWrapper::getEntityCountUnsubscribeCmds, this::handleWsDataUnsubscribeCmd) ); notificationCmdsHandlers = List.of( - WsCmdHandler.of(NotificationCmdsWrapper::getUnreadSubCmd, notificationCmdsHandler::handleUnreadNotificationsSubCmd), - WsCmdHandler.of(NotificationCmdsWrapper::getUnreadCountSubCmd, notificationCmdsHandler::handleUnreadNotificationsCountSubCmd), - WsCmdHandler.of(NotificationCmdsWrapper::getMarkAsReadCmd, notificationCmdsHandler::handleMarkAsReadCmd), - WsCmdHandler.of(NotificationCmdsWrapper::getUnsubCmd, notificationCmdsHandler::handleUnsubCmd) + newCmdHandler(NotificationCmdsWrapper::getUnreadSubCmd, notificationCmdsHandler::handleUnreadNotificationsSubCmd), + newCmdHandler(NotificationCmdsWrapper::getUnreadCountSubCmd, notificationCmdsHandler::handleUnreadNotificationsCountSubCmd), + newCmdHandler(NotificationCmdsWrapper::getMarkAsReadCmd, notificationCmdsHandler::handleMarkAsReadCmd), + newCmdHandler(NotificationCmdsWrapper::getUnsubCmd, notificationCmdsHandler::handleUnsubCmd) ); } @@ -955,7 +950,17 @@ public class DefaultWebSocketService implements WebSocketService { return limit == 0 ? DEFAULT_LIMIT : limit; } - @RequiredArgsConstructor(staticName = "of") + public static WsCmdHandler newCmdHandler(java.util.function.Function cmdExtractor, + BiConsumer handler) { + return new WsCmdHandler<>(cmdExtractor, handler); + } + + public static WsCmdListHandler newCmdsHandler(java.util.function.Function> cmdsExtractor, + BiConsumer handler) { + return new WsCmdListHandler<>(cmdsExtractor, handler); + } + + @RequiredArgsConstructor public static class WsCmdHandler { private final java.util.function.Function cmdExtractor; private final BiConsumer handler; @@ -970,13 +975,13 @@ public class DefaultWebSocketService implements WebSocketService { } } - @RequiredArgsConstructor(staticName = "of") + @RequiredArgsConstructor public static class WsCmdListHandler { - private final java.util.function.Function> cmdExtractor; + private final java.util.function.Function> cmdsExtractor; private final BiConsumer handler; public List extractCmds(W cmdsWrapper) { - return cmdExtractor.apply(cmdsWrapper); + return cmdsExtractor.apply(cmdsWrapper); } @SuppressWarnings("unchecked") diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/WebSocketService.java b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketService.java similarity index 96% rename from application/src/main/java/org/thingsboard/server/service/ws/telemetry/WebSocketService.java rename to application/src/main/java/org/thingsboard/server/service/ws/WebSocketService.java index d0d8dab59e..ddd08590b5 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/WebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.ws.telemetry; +package org.thingsboard.server.service.ws; import org.springframework.web.socket.CloseStatus; import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate; diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java index 6af03caeb5..5e31924e45 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java @@ -31,7 +31,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.notification.NotificationSubscriptionService; +import org.thingsboard.rule.engine.api.NotificationManager; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.ws.notification.cmd.NotificationsCountSubCmd; import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; @@ -39,11 +39,11 @@ import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscription; import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; import org.thingsboard.server.service.ws.WebSocketSessionRef; -import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd; +import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationsAsReadCmd; import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationsCountSubscription; -import org.thingsboard.server.service.ws.telemetry.WebSocketService; +import org.thingsboard.server.service.ws.WebSocketService; import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; @@ -59,7 +59,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH private final NotificationService notificationService; private final TbLocalSubscriptionService localSubscriptionService; - private final NotificationSubscriptionService notificationSubscriptionService; + private final NotificationManager notificationManager; private final TbServiceInfoProvider serviceInfoProvider; @Autowired @Lazy private WebSocketService wsService; @@ -67,13 +67,13 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH @Override public void handleUnreadNotificationsSubCmd(WebSocketSessionRef sessionRef, NotificationsSubCmd cmd) { log.debug("[{}] Handling unread notifications subscription cmd (cmdId: {})", sessionRef.getSessionId(), cmd.getCmdId()); - SecurityUser user = sessionRef.getSecurityCtx(); + SecurityUser securityCtx = sessionRef.getSecurityCtx(); NotificationsSubscription subscription = NotificationsSubscription.builder() .serviceId(serviceInfoProvider.getServiceId()) .sessionId(sessionRef.getSessionId()) .subscriptionId(cmd.getCmdId()) - .tenantId(user.getTenantId()) - .entityId(user.getId()) + .tenantId(securityCtx.getTenantId()) + .entityId(securityCtx.getId()) .updateProcessor(this::handleNotificationsSubscriptionUpdate) .limit(cmd.getLimit()) .build(); @@ -86,13 +86,13 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH @Override public void handleUnreadNotificationsCountSubCmd(WebSocketSessionRef sessionRef, NotificationsCountSubCmd cmd) { log.debug("[{}] Handling unread notifications count subscription cmd (cmdId: {})", sessionRef.getSessionId(), cmd.getCmdId()); - SecurityUser user = sessionRef.getSecurityCtx(); + SecurityUser securityCtx = sessionRef.getSecurityCtx(); NotificationsCountSubscription subscription = NotificationsCountSubscription.builder() .serviceId(serviceInfoProvider.getServiceId()) .sessionId(sessionRef.getSessionId()) .subscriptionId(cmd.getCmdId()) - .tenantId(user.getTenantId()) - .entityId(user.getId()) + .tenantId(securityCtx.getTenantId()) + .entityId(securityCtx.getId()) .updateProcessor(this::handleNotificationsCountSubscriptionUpdate) .build(); localSubscriptionService.addSubscription(subscription); @@ -203,9 +203,14 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH @Override - public void handleMarkAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationAsReadCmd cmd) { - NotificationId notificationId = new NotificationId(cmd.getNotificationId()); - notificationSubscriptionService.markNotificationAsRead(sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), notificationId); + public void handleMarkAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationsAsReadCmd cmd) { + SecurityUser securityCtx = sessionRef.getSecurityCtx(); + cmd.getNotifications().stream() + .map(NotificationId::new) + .forEach(notificationId -> { + notificationManager.markNotificationAsRead(securityCtx.getTenantId(), securityCtx.getId(), notificationId); + // fixme: should send bulk update event, not a separate event for each notification + }); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/NotificationCommandsHandler.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/NotificationCommandsHandler.java index 4cf1fb3809..0f57730601 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/NotificationCommandsHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/NotificationCommandsHandler.java @@ -16,7 +16,7 @@ package org.thingsboard.server.service.ws.notification; import org.thingsboard.server.service.ws.WebSocketSessionRef; -import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd; +import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationsAsReadCmd; import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; import org.thingsboard.server.service.ws.notification.cmd.NotificationsCountSubCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; @@ -27,7 +27,7 @@ public interface NotificationCommandsHandler { void handleUnreadNotificationsCountSubCmd(WebSocketSessionRef sessionRef, NotificationsCountSubCmd cmd); - void handleMarkAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationAsReadCmd cmd); + void handleMarkAsReadCmd(WebSocketSessionRef sessionRef, MarkNotificationsAsReadCmd cmd); void handleUnsubCmd(WebSocketSessionRef sessionRef, UnsubscribeCmd cmd); diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationAsReadCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationsAsReadCmd.java similarity index 86% rename from application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationAsReadCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationsAsReadCmd.java index cfc6e8e38b..6ed49fc4b6 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationAsReadCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationsAsReadCmd.java @@ -18,14 +18,14 @@ package org.thingsboard.server.service.ws.notification.cmd; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import org.thingsboard.server.service.ws.WsCmd; +import java.util.List; import java.util.UUID; @Data @NoArgsConstructor @AllArgsConstructor -public class MarkNotificationAsReadCmd implements WsCmd { +public class MarkNotificationsAsReadCmd implements WsCmd { private int cmdId; - private UUID notificationId; + private List notifications; } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java index 011e8cecca..1c56d49a6f 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java @@ -23,7 +23,8 @@ public class NotificationCmdsWrapper { private NotificationsCountSubCmd unreadCountSubCmd; private NotificationsSubCmd unreadSubCmd; - private MarkNotificationAsReadCmd markAsReadCmd; + + private MarkNotificationsAsReadCmd markAsReadCmd; private NotificationsUnsubCmd unsubCmd; diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsCountSubCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsCountSubCmd.java index 2722d6ae7d..985e0c9aa0 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsCountSubCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsCountSubCmd.java @@ -17,9 +17,7 @@ package org.thingsboard.server.service.ws.notification.cmd; import lombok.AllArgsConstructor; import lombok.Data; -import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import org.thingsboard.server.service.ws.WsCmd; @Data @NoArgsConstructor diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java index 087b250314..c772635176 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java @@ -17,9 +17,7 @@ package org.thingsboard.server.service.ws.notification.cmd; import lombok.AllArgsConstructor; import lombok.Data; -import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import org.thingsboard.server.service.ws.WsCmd; @Data @NoArgsConstructor diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java index b81e76e81d..d8e825e21d 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.ws.notification.cmd; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import org.thingsboard.server.service.ws.WsCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; @Data diff --git a/application/src/main/java/org/thingsboard/server/service/ws/WsCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/WsCmd.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/ws/WsCmd.java rename to application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/WsCmd.java index 096c5930a5..daa59abcba 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/WsCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/WsCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.ws; +package org.thingsboard.server.service.ws.notification.cmd; public interface WsCmd { int getCmdId(); diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java similarity index 94% rename from application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java rename to application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java index 6738b37a70..aca74f9a07 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWsApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java @@ -35,7 +35,7 @@ import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; @DaoSqlTest -public class NotificationsWsApiTest extends AbstractControllerTest { +public class NotificationApiTest extends AbstractControllerTest { @Before public void beforeEach() throws Exception { @@ -154,8 +154,6 @@ public class NotificationsWsApiTest extends AbstractControllerTest { assertThat(getAnotherWsClient().getLastCountUpdate().getTotalUnreadCount()).isOne(); } - public void testReceivingUpdatesWhenSubscriptionAtAnotherInstance() {} - private void checkFullNotificationsUpdate(UnreadNotificationsUpdate notificationsUpdate, String... expectedNotifications) { assertThat(notificationsUpdate.getNotifications()).extracting(Notification::getText).containsOnly(expectedNotifications); @@ -189,19 +187,19 @@ public class NotificationsWsApiTest extends AbstractControllerTest { @Override protected TbTestWebSocketClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException { - NotificationsWebSocketClient wsClient = new NotificationsWebSocketClient(WS_URL + wsPort, token); + NotificationApiWsClient wsClient = new NotificationApiWsClient(WS_URL + wsPort, token); assertThat(wsClient.connectBlocking(TIMEOUT, TimeUnit.SECONDS)).isTrue(); return wsClient; } @Override - public NotificationsWebSocketClient getWsClient() { - return (NotificationsWebSocketClient) super.getWsClient(); + public NotificationApiWsClient getWsClient() { + return (NotificationApiWsClient) super.getWsClient(); } @Override - public NotificationsWebSocketClient getAnotherWsClient() { - return (NotificationsWebSocketClient) super.getAnotherWsClient(); + public NotificationApiWsClient getAnotherWsClient() { + return (NotificationApiWsClient) super.getAnotherWsClient(); } } diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWebSocketClient.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiWsClient.java similarity index 89% rename from application/src/test/java/org/thingsboard/server/service/notification/NotificationsWebSocketClient.java rename to application/src/test/java/org/thingsboard/server/service/notification/NotificationApiWsClient.java index 5da35b91fc..e5b9d4b887 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationsWebSocketClient.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiWsClient.java @@ -21,7 +21,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.controller.TbTestWebSocketClient; -import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationAsReadCmd; +import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationsAsReadCmd; import org.thingsboard.server.service.ws.notification.cmd.NotificationCmdsWrapper; import org.thingsboard.server.service.ws.notification.cmd.NotificationsCountSubCmd; import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; @@ -31,17 +31,18 @@ import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdateType; import java.net.URI; import java.net.URISyntaxException; +import java.util.Arrays; import java.util.UUID; @Slf4j -public class NotificationsWebSocketClient extends TbTestWebSocketClient { +public class NotificationApiWsClient extends TbTestWebSocketClient { @Getter private UnreadNotificationsUpdate lastDataUpdate; @Getter private UnreadNotificationsCountUpdate lastCountUpdate; - public NotificationsWebSocketClient(String wsUrl, String token) throws URISyntaxException { + public NotificationApiWsClient(String wsUrl, String token) throws URISyntaxException { super(new URI(wsUrl + "/api/ws/plugins/notifications?token=" + token)); } @@ -57,9 +58,9 @@ public class NotificationsWebSocketClient extends TbTestWebSocketClient { sendCmd(cmdsWrapper); } - public void markNotificationAsRead(UUID notificationId) { + public void markNotificationAsRead(UUID... notifications) { NotificationCmdsWrapper cmdsWrapper = new NotificationCmdsWrapper(); - cmdsWrapper.setMarkAsReadCmd(new MarkNotificationAsReadCmd(newCmdId(), notificationId)); + cmdsWrapper.setMarkAsReadCmd(new MarkNotificationsAsReadCmd(newCmdId(), Arrays.asList(notifications))); sendCmd(cmdsWrapper); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java index 4ded8cbd9f..7ae96ada79 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java @@ -15,7 +15,7 @@ */ package org.thingsboard.server.dao.notification; -import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; @@ -33,7 +33,7 @@ public interface NotificationRequestService { PageData findNotificationRequestsByTenantId(TenantId tenantId, PageLink pageLink); - List findNotificationRequestsByRuleIdAndAlarmId(TenantId tenantId, NotificationRuleId ruleId, AlarmId alarmId); + List findNotificationRequestsByRuleIdAndOriginatorEntityId(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId); void deleteNotificationRequestById(TenantId tenantId, NotificationRequestId id); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/AlarmOriginatedNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/AlarmOriginatedNotificationInfo.java new file mode 100644 index 0000000000..19d3dc99ad --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/AlarmOriginatedNotificationInfo.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.alarm.AlarmStatus; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.EntityId; + +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AlarmOriginatedNotificationInfo extends NotificationInfo { + + private AlarmId alarmId; + private String alarmType; + private EntityId alarmOriginator; + private AlarmSeverity alarmSeverity; + private AlarmStatus alarmStatus; + + @Override + public NotificationOriginatorType getOriginatorType() { + return NotificationOriginatorType.ALARM; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java index 827bddc980..a39d858fcc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/Notification.java @@ -38,7 +38,7 @@ public class Notification extends BaseData { private String text; private NotificationInfo info; private NotificationSeverity severity; + private NotificationOriginatorType originatorType; private NotificationStatus status; -// private UserId senderId; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationInfo.java index 298998f8d7..e7b2873d99 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationInfo.java @@ -15,6 +15,10 @@ */ package org.thingsboard.server.common.data.notification; +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 lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -28,23 +32,18 @@ import org.thingsboard.server.common.data.validation.NoXss; @Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -//@JsonIgnoreProperties(ignoreUnknown = true) -//@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "notificationType", visible = true, defaultImpl = NotificationInfo.class) -//@JsonSubTypes({ -// @Type(name = "ALARM", value = DeviceExportData.class), -//}) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "originatorType", defaultImpl = NotificationInfo.class) +@JsonSubTypes({ + @Type(name = "ALARM", value = AlarmOriginatedNotificationInfo.class), +}) public class NotificationInfo { - @NoXss + private String description; private DashboardId dashboardId; - private AlarmId alarmId; - private String alarmType; - private EntityId alarmOriginator; - private AlarmSeverity alarmSeverity; - private AlarmStatus alarmStatus; + public NotificationOriginatorType getOriginatorType() { + return null; + } } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineNotificationService.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationOriginatorType.java similarity index 64% rename from rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineNotificationService.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationOriginatorType.java index e7b87cf25d..44aa879a00 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineNotificationService.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationOriginatorType.java @@ -13,13 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.rule.engine.api; - -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.notification.NotificationRequest; - -public interface RuleEngineNotificationService { - - NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest); +package org.thingsboard.server.common.data.notification; +public enum NotificationOriginatorType { + USER, + ALARM, + RULE_NODE } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java index af90eab7ee..f962db97e4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java @@ -16,7 +16,6 @@ package org.thingsboard.server.common.data.notification; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -25,7 +24,7 @@ import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; -import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.NotificationTargetId; @@ -46,26 +45,25 @@ public class NotificationRequest extends BaseData impleme private TenantId tenantId; @NotNull(message = "Target is not specified") private NotificationTargetId targetId; + @NoXss - private String notificationReason; // "Alarm", "Scheduled event". "General" by default - // @NoXss + private String notificationReason; @NotBlank(message = "Notification text template is missing") - private String textTemplate; + private String textTemplate; // fixme: xss @Valid private NotificationInfo notificationInfo; private NotificationSeverity notificationSeverity; - private NotificationRequestConfig additionalConfig; - @JsonProperty(access = JsonProperty.Access.READ_ONLY) - private NotificationRequestStatus status; - @JsonIgnore + private NotificationOriginatorType originatorType; + private EntityId originatorEntityId; // userId, alarmId or tenantId private NotificationRuleId ruleId; // maybe move to child class - @JsonIgnore - private AlarmId alarmId; + + private NotificationRequestConfig additionalConfig; + private NotificationRequestStatus status; public static final String GENERAL_NOTIFICATION_REASON = "General"; - public static final String ALARM_NOTIFICATION_REASON = "Alarm"; + @JsonIgnore @Override public String getName() { return notificationReason; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 27b17164ea..9d477059d6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -660,6 +660,7 @@ public class ModelConstants { public static final String NOTIFICATION_TEXT_PROPERTY = "text"; public static final String NOTIFICATION_INFO_PROPERTY = "info"; public static final String NOTIFICATION_SEVERITY_PROPERTY = "severity"; + public static final String NOTIFICATION_ORIGINATOR_TYPE_PROPERTY = "originator_type"; public static final String NOTIFICATION_STATUS_PROPERTY = "status"; public static final String NOTIFICATION_REQUEST_TABLE_NAME = "notification_request"; @@ -668,10 +669,12 @@ public class ModelConstants { public static final String NOTIFICATION_REQUEST_NOTIFICATION_REASON_PROPERTY = "notification_reason"; public static final String NOTIFICATION_REQUEST_NOTIFICATION_INFO_PROPERTY = "notification_info"; public static final String NOTIFICATION_REQUEST_NOTIFICATION_SEVERITY_PROPERTY = "notification_severity"; + public static final String NOTIFICATION_REQUEST_ORIGINATOR_TYPE_PROPERTY = "originator_type"; + public static final String NOTIFICATION_REQUEST_ORIGINATOR_ENTITY_ID_PROPERTY = "originator_entity_id"; + public static final String NOTIFICATION_REQUEST_ORIGINATOR_ENTITY_TYPE_PROPERTY = "originator_entity_type"; public static final String NOTIFICATION_REQUEST_ADDITIONAL_CONFIG_PROPERTY = "additional_config"; public static final String NOTIFICATION_REQUEST_STATUS_PROPERTY = "status"; public static final String NOTIFICATION_REQUEST_RULE_ID_PROPERTY = "rule_id"; - public static final String NOTIFICATION_REQUEST_ALARM_ID_PROPERTY = "alarm_id"; public static final String NOTIFICATION_RULE_TABLE_NAME = "notification_rule"; // ... diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java index c79ba6f30b..c94ec80016 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationEntity.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationInfo; +import org.thingsboard.server.common.data.notification.NotificationOriginatorType; import org.thingsboard.server.common.data.notification.NotificationSeverity; import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.dao.model.BaseSqlEntity; @@ -66,6 +67,10 @@ public class NotificationEntity extends BaseSqlEntity { @Column(name = ModelConstants.NOTIFICATION_SEVERITY_PROPERTY) private NotificationSeverity severity; + @Enumerated(EnumType.STRING) + @Column(name = ModelConstants.NOTIFICATION_ORIGINATOR_TYPE_PROPERTY) + private NotificationOriginatorType originatorType; + @Enumerated(EnumType.STRING) @Column(name = ModelConstants.NOTIFICATION_STATUS_PROPERTY) private NotificationStatus status; @@ -83,6 +88,7 @@ public class NotificationEntity extends BaseSqlEntity { setInfo(JacksonUtil.valueToTree(notification.getInfo())); } setSeverity(notification.getSeverity()); + setOriginatorType(notification.getOriginatorType()); setStatus(notification.getStatus()); } @@ -99,6 +105,7 @@ public class NotificationEntity extends BaseSqlEntity { notification.setInfo(JacksonUtil.treeToValue(info, NotificationInfo.class)); } notification.setSeverity(severity); + notification.setOriginatorType(originatorType); notification.setStatus(status); return notification; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java index 3c7b525628..52b5aee2b9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java @@ -21,13 +21,16 @@ import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.NotificationInfo; +import org.thingsboard.server.common.data.notification.NotificationOriginatorType; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequestConfig; import org.thingsboard.server.common.data.notification.NotificationRequestStatus; @@ -70,6 +73,20 @@ public class NotificationRequestEntity extends BaseSqlEntity findNotificationRequestsByRuleIdAndAlarmId(TenantId tenantId, NotificationRuleId ruleId, AlarmId alarmId) { - return notificationRequestDao.findByRuleIdAndAlarmId(tenantId, ruleId, alarmId); + public List findNotificationRequestsByRuleIdAndOriginatorEntityId(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId) { + return notificationRequestDao.findByRuleIdAndOriginatorEntityId(tenantId, ruleId, originatorEntityId); } // ON DELETE CASCADE is used: notifications for request are deleted as well diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java index 76778c64b5..2036105ffb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java @@ -15,7 +15,7 @@ */ package org.thingsboard.server.dao.notification; -import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationRequest; @@ -30,7 +30,7 @@ public interface NotificationRequestDao extends Dao { PageData findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); - List findByRuleIdAndAlarmId(TenantId tenantId, NotificationRuleId ruleId, AlarmId alarmId); + List findByRuleIdAndOriginatorEntityId(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId); PageData findAllByStatus(NotificationRequestStatus status, PageLink pageLink); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java index 7684ed5865..b58fc198a7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java @@ -19,7 +19,7 @@ import com.google.common.base.Strings; import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationRequest; @@ -49,8 +49,8 @@ public class JpaNotificationRequestDao extends JpaAbstractDao findByRuleIdAndAlarmId(TenantId tenantId, NotificationRuleId ruleId, AlarmId alarmId) { - return DaoUtil.convertDataList(notificationRequestRepository.findAllByRuleIdAndAlarmId(ruleId.getId(), alarmId.getId())); + public List findByRuleIdAndOriginatorEntityId(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId) { + return DaoUtil.convertDataList(notificationRequestRepository.findAllByRuleIdAndOriginatorEntityTypeAndOriginatorEntityId(ruleId.getId(), originatorEntityId.getEntityType(), originatorEntityId.getId())); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java index f892767a83..40ae696979 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java @@ -21,6 +21,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.dao.model.sql.NotificationRequestEntity; @@ -36,7 +37,7 @@ public interface NotificationRequestRepository extends JpaRepository findByTenantIdAndSearchText(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); - List findAllByRuleIdAndAlarmId(UUID ruleId, UUID alarmId); + List findAllByRuleIdAndOriginatorEntityTypeAndOriginatorEntityId(UUID ruleId, EntityType originatorEntityType, UUID originatorEntityId); Page findAllByStatus(NotificationRequestStatus status, Pageable pageable); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java index ff156eb51c..b825d36892 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.query.AlarmData; @@ -101,6 +102,9 @@ public class AlarmDataAdapter { } else { alarm.setPropagateRelationTypes(Collections.emptyList()); } + if (row.get(ModelConstants.ALARM_NOTIFICATION_RULE_ID) != null) { + alarm.setNotificationRuleId(new NotificationRuleId((UUID) row.get(ModelConstants.ALARM_NOTIFICATION_RULE_ID))); + } UUID entityUuid = (UUID) row.get(ModelConstants.ENTITY_ID_COLUMN); EntityId entityId = entityIdMap.get(entityUuid); Object originatorNameObj = row.get(ModelConstants.ALARM_ORIGINATOR_NAME_PROPERTY); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java index 69cb4a2d86..94eebc7f6b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java @@ -104,6 +104,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { " a.tenant_id as tenant_id, " + " a.customer_id as customer_id, " + " a.propagate_relation_types as propagate_relation_types, " + + " a.notification_rule_id as notification_rule_id, " + " a.type as type," + SELECT_ORIGINATOR_NAME + ", "; private static final String JOIN_ENTITY_ALARMS = "inner join entity_alarm ea on a.id = ea.alarm_id"; diff --git a/dao/src/main/resources/sql/schema-entities-idx.sql b/dao/src/main/resources/sql/schema-entities-idx.sql index 019a8b1771..0b95809b91 100644 --- a/dao/src/main/resources/sql/schema-entities-idx.sql +++ b/dao/src/main/resources/sql/schema-entities-idx.sql @@ -74,12 +74,12 @@ CREATE INDEX IF NOT EXISTS idx_rule_node_type ON rule_node(type); CREATE INDEX IF NOT EXISTS idx_api_usage_state_entity_id ON api_usage_state(entity_id); -CREATE INDEX IF NOT EXISTS idx_notification_target_tenant_id_and_created_time ON notification_target(tenant_id, created_time DESC); +CREATE INDEX IF NOT EXISTS idx_notification_target_tenant_id_created_time ON notification_target(tenant_id, created_time DESC); -CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id_and_created_time ON notification_request(tenant_id, created_time DESC); +CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id_originator_type_created_time ON notification_request(tenant_id, originator_type, created_time DESC); CREATE INDEX IF NOT EXISTS idx_notification_id ON notification(id); -CREATE INDEX IF NOT EXISTS idx_notification_notification_request_id ON notification(request_id); +CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_created_time ON notification(recipient_id, created_time DESC); -CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_and_created_time ON notification(recipient_id, created_time DESC); +CREATE INDEX IF NOT EXISTS idx_notification_notification_request_id ON notification(request_id); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 1ef732b8b6..885608c7f5 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -58,7 +58,8 @@ CREATE TABLE IF NOT EXISTS alarm ( propagate_relation_types varchar, type varchar(255), propagate_to_owner boolean, - propagate_to_tenant boolean + propagate_to_tenant boolean, + notification_rule_id uuid ); CREATE TABLE IF NOT EXISTS entity_alarm ( @@ -799,10 +800,12 @@ CREATE TABLE IF NOT EXISTS notification_request ( text_template VARCHAR NOT NULL, notification_info VARCHAR(1000), notification_severity VARCHAR(32), - additional_config VARCHAR(1000), - status VARCHAR(32), + originator_type VARCHAR(32) NOT NULL, + originator_entity_id UUID, + originator_entity_type VARCHAR(32), rule_id UUID NULL CONSTRAINT fk_notification_request_rule_id REFERENCES notification_rule(id), - alarm_id UUID + additional_config VARCHAR(1000), + status VARCHAR(32) ); CREATE TABLE IF NOT EXISTS notification ( @@ -815,5 +818,6 @@ CREATE TABLE IF NOT EXISTS notification ( text VARCHAR NOT NULL, info VARCHAR(1000), severity VARCHAR(32), + originator_type VARCHAR(32) NOT NULL, status VARCHAR(32) ) PARTITION BY RANGE (created_time); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/NotificationSubscriptionService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationManager.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/notification/NotificationSubscriptionService.java rename to rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationManager.java index 902ad39470..ac336e9fe4 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/NotificationSubscriptionService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationManager.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.notification; +package org.thingsboard.rule.engine.api; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; @@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.NotificationRequest; -public interface NotificationSubscriptionService { +public interface NotificationManager { NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index 7ca31da24e..ede2f716e5 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -267,7 +267,7 @@ public interface TbContext { SmsSenderFactory getSmsSenderFactory(); - RuleEngineNotificationService getNotificationService(); + NotificationManager getNotificationManager(); /** * Creates JS Script Engine diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java index 06fc0b110d..f87debd4dc 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java @@ -23,6 +23,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.notification.NotificationOriginatorType; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -58,9 +59,11 @@ public class TbNotificationNode implements TbNode { .notificationReason(config.getNotificationReason()) .textTemplate(TbNodeUtils.processPattern(config.getNotificationTextTemplate(), msg)) .notificationSeverity(config.getNotificationSeverity()) + .originatorType(NotificationOriginatorType.RULE_NODE) + .originatorEntityId(ctx.getTenantId()) .build(); withCallback(ctx.getDbCallbackExecutor().executeAsync(() -> { - return ctx.getNotificationService().processNotificationRequest(ctx.getTenantId(), notificationRequest); + return ctx.getNotificationManager().processNotificationRequest(ctx.getTenantId(), notificationRequest); }), r -> { TbMsgMetaData msgMetaData = msg.getMetaData().copy(); From 165e86c82b18f59001035cdbf4a8eafdaed9641f Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 8 Nov 2022 16:04:50 +0200 Subject: [PATCH 022/488] Notification rules processing; refactoring --- .../main/data/upgrade/3.4.2/schema_update.sql | 10 ++- .../NotificationRuleController.java | 81 +++++++++++++++++++ .../NotificationTargetController.java | 5 +- .../controller/plugin/TbWebSocketHandler.java | 14 ++-- .../DefaultNotificationManager.java | 13 ++- ...aultNotificationRuleProcessingService.java | 73 +++++++++-------- .../NotificationRuleProcessingService.java | 7 +- .../queue/DefaultTbCoreConsumerService.java | 2 + .../service/security/permission/Resource.java | 3 +- .../permission/TenantAdminPermissions.java | 1 + .../DefaultSubscriptionManagerService.java | 1 + .../subscription/TbSubscriptionUtils.java | 9 ++- .../DefaultAlarmSubscriptionService.java | 1 - .../DefaultNotificationCommandsHandler.java | 45 +++++++---- .../sub/NotificationsSubscription.java | 23 +++++- .../controller/TbTestWebSocketClient.java | 12 ++- .../notification/NotificationApiTest.java | 16 ++-- .../notification/NotificationApiWsClient.java | 33 +++++++- common/cluster-api/src/main/proto/queue.proto | 1 + .../notification/NotificationRuleService.java | 6 ++ .../NotificationTargetService.java | 2 +- .../server/common/data/EntityType.java | 2 +- .../common/data/id/EntityIdFactory.java | 2 + .../common/data/id/NotificationRuleId.java | 10 +-- .../notification/NotificationRequest.java | 2 +- .../rule/NotificationEscalationConfig.java | 31 +++++++ .../notification/rule/NotificationRule.java | 7 +- .../server/dao/model/ModelConstants.java | 4 +- .../dao/model/sql/NotificationRuleEntity.java | 19 +++++ .../DefaultNotificationRuleService.java | 19 +++++ .../DefaultNotificationTargetService.java | 2 +- .../dao/notification/NotificationRuleDao.java | 6 ++ .../notification/JpaNotificationRuleDao.java | 11 +++ .../NotificationRuleRepository.java | 5 ++ .../main/resources/sql/schema-entities.sql | 8 +- 35 files changed, 379 insertions(+), 107 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationEscalationConfig.java diff --git a/application/src/main/data/upgrade/3.4.2/schema_update.sql b/application/src/main/data/upgrade/3.4.2/schema_update.sql index 01113f2324..3dcc7c3a07 100644 --- a/application/src/main/data/upgrade/3.4.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.4.2/schema_update.sql @@ -19,12 +19,18 @@ CREATE TABLE IF NOT EXISTS notification_target ( created_time BIGINT NOT NULL, tenant_id UUID NOT NULL, name VARCHAR(255) NOT NULL, - configuration varchar(1000) NOT NULL + configuration VARCHAR(1000) NOT NULL ); CREATE INDEX IF NOT EXISTS idx_notification_target_tenant_id_created_time ON notification_target(tenant_id, created_time DESC); CREATE TABLE IF NOT EXISTS notification_rule ( - id UUID NOT NULL CONSTRAINT notification_rule_pkey PRIMARY KEY + id UUID NOT NULL CONSTRAINT notification_rule_pkey PRIMARY KEY, + created_time BIGINT NOT NULL, + tenant_id UUID NOT NULL, + name VARCHAR(255) NOT NULL, + notification_text_template VARCHAR NOT NULL, + initial_notification_target_id UUID NULL CONSTRAINT fk_notification_rule_target_id REFERENCES notification_target(id), + escalation_config VARCHAR(500) ); ALTER TABLE alarm ADD COLUMN IF NOT EXISTS notification_rule_id UUID; diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java new file mode 100644 index 0000000000..78f5827c6d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java @@ -0,0 +1,81 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.NotificationRuleId; +import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.notification.NotificationRuleService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.security.permission.Resource; + +import java.util.UUID; + +@RestController +@TbCoreComponent +@RequestMapping("/api") +@RequiredArgsConstructor +@Slf4j +public class NotificationRuleController extends BaseController { + // todo: logEntityAction + private final NotificationRuleService notificationRuleService; + + @PostMapping("/notification/rule") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + public NotificationRule saveNotificationRule(@RequestBody NotificationRule notificationRule, + @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { +// accessControlService.checkPermission(user, Resource.NOTIFICATION_RULE, notificationRule.getId() == null ? Operation.CREATE : Operation.WRITE, notificationRule.getId(), ); + return notificationRuleService.saveNotificationRule(user.getTenantId(), notificationRule); + } + + @GetMapping("/notification/rule/{id}") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + public NotificationRule getNotificationRuleById(@PathVariable UUID id, + @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { + NotificationRuleId notificationRuleId = new NotificationRuleId(id); + NotificationRule notificationRule = notificationRuleService.findNotificationRuleById(user.getTenantId(), notificationRuleId); + accessControlService.checkPermission(user, Resource.NOTIFICATION_RULE, Operation.READ, notificationRuleId, notificationRule); + return notificationRule; + } + + @GetMapping("/notification/rules") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + public PageData getNotificationRules(@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 { + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return notificationRuleService.findNotificationRulesByTenantId(user.getTenantId(), pageLink); + } + +} 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 34451ddede..22ab0475c2 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java @@ -32,20 +32,17 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.NotificationTargetId; -import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfigType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; -import java.util.List; import java.util.UUID; @RestController @@ -120,7 +117,7 @@ public class NotificationTargetController extends BaseController { @RequestParam(required = false) String sortOrder, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return notificationTargetService.findNotificationTargetsByTenantIdAndPageLink(user.getTenantId(), pageLink); + return notificationTargetService.findNotificationTargetsByTenantId(user.getTenantId(), pageLink); } @DeleteMapping("/target/{id}") diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index edd6843c2f..00f3be68c4 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -41,9 +41,9 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.ws.SessionEvent; import org.thingsboard.server.service.ws.WebSocketMsgEndpoint; -import org.thingsboard.server.service.ws.WebSocketSessionType; import org.thingsboard.server.service.ws.WebSocketService; import org.thingsboard.server.service.ws.WebSocketSessionRef; +import org.thingsboard.server.service.ws.WebSocketSessionType; import javax.websocket.RemoteEndpoint; import javax.websocket.SendHandler; @@ -58,6 +58,7 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; import static org.thingsboard.server.service.ws.DefaultWebSocketService.NUMBER_OF_PING_ATTEMPTS; @@ -215,7 +216,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke private final RemoteEndpoint.Async asyncRemote; private final WebSocketSessionRef sessionRef; - private volatile boolean isSending = false; + private final AtomicBoolean isSending = new AtomicBoolean(false); private final Queue> msgQueue; private volatile long lastActivityTime; @@ -262,7 +263,9 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke } synchronized void sendMsg(TbWebSocketMsg msg) { - if (isSending) { + if (isSending.compareAndSet(false, true)) { + sendMsgInternal(msg); + } else { try { msgQueue.add(msg); } catch (RuntimeException e) { @@ -273,9 +276,6 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke } closeSession(CloseStatus.POLICY_VIOLATION.withReason("Max pending updates limit reached!")); } - } else { - isSending = true; - sendMsgInternal(msg); } } @@ -310,7 +310,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke if (msg != null) { sendMsgInternal(msg); } else { - isSending = false; + isSending.set(false); } } } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationManager.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationManager.java index 4bb8afc61d..f4232fb72d 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationManager.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationManager.java @@ -39,8 +39,10 @@ import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.NotificationsTopicService; import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import org.thingsboard.server.service.telemetry.AbstractSubscriptionService; @@ -61,24 +63,27 @@ public class DefaultNotificationManager extends AbstractSubscriptionService impl private final NotificationService notificationService; private final DbCallbackExecutorService dbCallbackExecutorService; private final NotificationsTopicService notificationsTopicService; + private final TbQueueProducerProvider producerProvider; public DefaultNotificationManager(TbClusterService clusterService, PartitionService partitionService, NotificationTargetService notificationTargetService, NotificationRequestService notificationRequestService, NotificationService notificationService, DbCallbackExecutorService dbCallbackExecutorService, - NotificationsTopicService notificationsTopicService) { + NotificationsTopicService notificationsTopicService, + TbQueueProducerProvider producerProvider) { super(clusterService, partitionService); this.notificationTargetService = notificationTargetService; this.notificationRequestService = notificationRequestService; this.notificationService = notificationService; this.dbCallbackExecutorService = dbCallbackExecutorService; this.notificationsTopicService = notificationsTopicService; + this.producerProvider = producerProvider; } @Override public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { - log.info("Processing notification request (tenant id: {}, notification target id: {})", tenantId, notificationRequest.getTargetId()); + log.debug("Processing notification request (tenant id: {}, notification target id: {})", tenantId, notificationRequest.getTargetId()); notificationRequest.setTenantId(tenantId); if (notificationRequest.getAdditionalConfig() != null) { NotificationRequestConfig config = notificationRequest.getAdditionalConfig(); @@ -201,11 +206,11 @@ public class DefaultNotificationManager extends AbstractSubscriptionService impl private void onNotificationRequestUpdate(TenantId tenantId, NotificationRequestUpdate update) { log.trace("Submitting notification request update: {}", update); wsCallBackExecutor.submit(() -> { - TransportProtos.ToCoreMsg notificationRequestDeletedProto = TbSubscriptionUtils.notificationRequestUpdateToProto(tenantId, update); + TransportProtos.ToCoreNotificationMsg notificationRequestUpdateProto = TbSubscriptionUtils.notificationRequestUpdateToProto(tenantId, update); Set coreServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_CORE)); for (String serviceId : coreServices) { TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); - clusterService.pushMsgToCore(tpi, UUID.randomUUID(), notificationRequestDeletedProto, null); + producerProvider.getTbCoreNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), notificationRequestUpdateProto), null); } }); } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java index ac7e875e03..c8e472a8cc 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java @@ -29,7 +29,6 @@ import org.thingsboard.server.common.data.notification.NotificationInfo; import org.thingsboard.server.common.data.notification.NotificationOriginatorType; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequestConfig; -import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.notification.NotificationSeverity; import org.thingsboard.server.common.data.notification.rule.NonConfirmedNotificationEscalation; import org.thingsboard.server.common.data.notification.rule.NotificationRule; @@ -51,53 +50,63 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul private final DbCallbackExecutorService dbCallbackExecutorService; @Override - public ListenableFuture onAlarmCreatedOrUpdated(TenantId tenantId, Alarm alarm) { - return processAlarmUpdate(tenantId, alarm); + public ListenableFuture onAlarmCreatedOrUpdated(TenantId tenantId, Alarm alarm) { + return processAlarmUpdate(tenantId, alarm, false); } @Override - public ListenableFuture onAlarmAcknowledged(TenantId tenantId, Alarm alarm) { - return processAlarmUpdate(tenantId, alarm); + public ListenableFuture onAlarmDeleted(TenantId tenantId, Alarm alarm) { + return processAlarmUpdate(tenantId, alarm, true); } - private ListenableFuture processAlarmUpdate(TenantId tenantId, Alarm alarm) { + private ListenableFuture processAlarmUpdate(TenantId tenantId, Alarm alarm, boolean deleted) { if (alarm.getNotificationRuleId() == null) return Futures.immediateFuture(null); return dbCallbackExecutorService.submit(() -> { - onAlarmUpdate(tenantId, alarm.getNotificationRuleId(), alarm); + onAlarmUpdate(tenantId, alarm.getNotificationRuleId(), alarm, deleted); + return null; }); } - private void onAlarmUpdate(TenantId tenantId, NotificationRuleId notificationRuleId, Alarm alarm) { + @Override + public ListenableFuture onNotificationRuleDeleted(TenantId tenantId, NotificationRuleId ruleId) { + return dbCallbackExecutorService.submit(() -> { + // need to remove fk constraint in notificationRequest to rule + // todo: do we need to remove all notifications when notification request is deleted? + return null; + }); + } + + // todo: think about: what if notification rule was updated? + private void onAlarmUpdate(TenantId tenantId, NotificationRuleId notificationRuleId, Alarm alarm, boolean deleted) { List notificationRequests = notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityId(tenantId, notificationRuleId, alarm.getId()); NotificationRule notificationRule = notificationRuleService.findNotificationRuleById(tenantId, notificationRuleId); + if (notificationRule == null) return; - if (notificationRequests.isEmpty()) { // in case it is first notification for alarm, or it was previously acked and now we need to send notifications again - NotificationTargetId initialNotificationTargetId = notificationRule.getInitialNotificationTargetId(); - submitNotificationRequest(tenantId, initialNotificationTargetId, notificationRule, alarm, 0); + if (alarmAcknowledged(alarm) || deleted) { + for (NotificationRequest notificationRequest : notificationRequests) { + notificationManager.deleteNotificationRequest(tenantId, notificationRequest.getId()); + // todo: or should we mark already sent notifications as read and delete only scheduled? + } + return; + } - for (NonConfirmedNotificationEscalation escalation : notificationRule.getEscalations()) { - submitNotificationRequest(tenantId, escalation.getNotificationTargetId(), notificationRule, alarm, escalation.getDelayInMinutes()); + if (notificationRequests.isEmpty()) { + NotificationTargetId initialNotificationTargetId = notificationRule.getInitialNotificationTargetId(); + if (initialNotificationTargetId != null) { + submitNotificationRequest(tenantId, initialNotificationTargetId, notificationRule, alarm, 0); } - } else { - if (alarmAcknowledged(alarm)) { - for (NotificationRequest notificationRequest : notificationRequests) { - if (notificationRequest.getStatus() == NotificationRequestStatus.SCHEDULED) { - // using regular service due to no need to send an update to subscription manager - notificationRequestService.deleteNotificationRequestById(tenantId, notificationRequest.getId()); - } else { - notificationManager.deleteNotificationRequest(tenantId, notificationRequest.getId()); - // todo: or should we mark already sent notifications as read? - } + if (notificationRule.getEscalationConfig() != null) { + for (NonConfirmedNotificationEscalation escalation : notificationRule.getEscalationConfig().getEscalations()) { + submitNotificationRequest(tenantId, escalation.getNotificationTargetId(), notificationRule, alarm, escalation.getDelayInMinutes()); } - } else { - NotificationInfo newNotificationInfo = constructNotificationInfo(alarm, notificationRule); - for (NotificationRequest notificationRequest : notificationRequests) { - NotificationInfo previousNotificationInfo = notificationRequest.getNotificationInfo(); - if (!previousNotificationInfo.equals(newNotificationInfo)) { - notificationRequest.setNotificationInfo(newNotificationInfo); - notificationManager.updateNotificationRequest(tenantId, notificationRequest); - } - // fixme: no need to send an update event for scheduled requests, only for sent + } + } else { + NotificationInfo newNotificationInfo = constructNotificationInfo(alarm, notificationRule); + for (NotificationRequest notificationRequest : notificationRequests) { + NotificationInfo previousNotificationInfo = notificationRequest.getNotificationInfo(); + if (!previousNotificationInfo.equals(newNotificationInfo)) { + notificationRequest.setNotificationInfo(newNotificationInfo); + notificationManager.updateNotificationRequest(tenantId, notificationRequest); } } } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/NotificationRuleProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/NotificationRuleProcessingService.java index 4f3d0f7d9e..2283e49cdd 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/NotificationRuleProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/NotificationRuleProcessingService.java @@ -17,12 +17,15 @@ package org.thingsboard.server.service.notification; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; public interface NotificationRuleProcessingService { - ListenableFuture onAlarmCreatedOrUpdated(TenantId tenantId, Alarm alarm); + ListenableFuture onAlarmCreatedOrUpdated(TenantId tenantId, Alarm alarm); - ListenableFuture onAlarmAcknowledged(TenantId tenantId, Alarm alarm); + ListenableFuture onAlarmDeleted(TenantId tenantId, Alarm alarm); + + ListenableFuture onNotificationRuleDeleted(TenantId tenantId, NotificationRuleId ruleId); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index 60a9567488..518197f690 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -347,6 +347,8 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService alarm, true ); + notificationRuleProcessingService.onAlarmDeleted(tenantId, alarm); callback.onSuccess(); } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java index 58fd7d6db7..d35f7eb275 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java @@ -50,6 +50,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesDeletePr import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesSubscriptionProto; import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; @@ -380,7 +381,7 @@ public class TbSubscriptionUtils { return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build(); } - public static TransportProtos.ToCoreNotificationMsg notificationsSubUpdateToProto(TbSubscription subscription, NotificationsSubscriptionUpdate update) { + public static ToCoreNotificationMsg notificationsSubUpdateToProto(TbSubscription subscription, NotificationsSubscriptionUpdate update) { TransportProtos.NotificationsSubscriptionUpdateProto.Builder updateProto = TransportProtos.NotificationsSubscriptionUpdateProto.newBuilder() .setSessionId(subscription.getSessionId()) .setSubscriptionId(subscription.getSubscriptionId()); @@ -390,7 +391,7 @@ public class TbSubscriptionUtils { if (update.getNotificationRequestUpdate() != null) { updateProto.setNotificationRequestUpdate(JacksonUtil.toString(update.getNotificationRequestUpdate())); } - return TransportProtos.ToCoreNotificationMsg.newBuilder() + return ToCoreNotificationMsg.newBuilder() .setToLocalSubscriptionServiceMsg(TransportProtos.LocalSubscriptionServiceMsgProto.newBuilder() .setNotificationsSubUpdate(updateProto) .build()) @@ -412,13 +413,13 @@ public class TbSubscriptionUtils { .build(); } - public static ToCoreMsg notificationRequestUpdateToProto(TenantId tenantId, NotificationRequestUpdate notificationRequestUpdate) { + public static ToCoreNotificationMsg notificationRequestUpdateToProto(TenantId tenantId, NotificationRequestUpdate notificationRequestUpdate) { TransportProtos.NotificationRequestUpdateProto updateProto = TransportProtos.NotificationRequestUpdateProto.newBuilder() .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) .setUpdate(JacksonUtil.toString(notificationRequestUpdate)) .build(); - return ToCoreMsg.newBuilder() + return ToCoreNotificationMsg.newBuilder() .setToSubscriptionMgrMsg(SubscriptionMgrMsgProto.newBuilder() .setNotificationRequestUpdate(updateProto) .build()) diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java index 947c80a175..eb84c3b40f 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java @@ -168,7 +168,6 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService }); } }); - // todo: handle notification rule } private void onAlarmDeleted(AlarmOperationResult result) { diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java index 5e31924e45..5dd8bd7096 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java @@ -105,8 +105,10 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH log.trace("[{}, subId: {}] Fetching unread notifications from DB", subscription.getSessionId(), subscription.getSubscriptionId()); PageData notifications = notificationService.findLatestUnreadNotificationsByUserId(subscription.getTenantId(), (UserId) subscription.getEntityId(), subscription.getLimit()); - subscription.getUnreadNotifications().clear(); - subscription.getUnreadNotifications().putAll(notifications.getData().stream().collect(Collectors.toMap(IdBased::getUuidId, n -> n))); + subscription.getLatestUnreadNotifications().clear(); + notifications.getData().forEach(notification -> { + subscription.getLatestUnreadNotifications().put(notification.getUuidId(), notification); + }); subscription.getTotalUnreadCounter().set((int) notifications.getTotalElements()); } @@ -129,19 +131,30 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH private void handleNotificationUpdate(NotificationsSubscription subscription, NotificationUpdate update) { log.trace("[{}, subId: {}] Handling notification update: {}", subscription.getSessionId(), subscription.getSubscriptionId(), update); Notification notification = update.getNotification(); - if (notification.getStatus() == NotificationStatus.READ) { - fetchUnreadNotifications(subscription); - sendUpdate(subscription.getSessionId(), subscription.createFullUpdate()); - } else { - subscription.getUnreadNotifications().put(notification.getUuidId(), notification); - if (update.isNew()) { - subscription.getTotalUnreadCounter().incrementAndGet(); - Set beyondLimit = subscription.getUnreadNotifications().keySet().stream() - .skip(subscription.getLimit()) - .collect(Collectors.toSet()); - beyondLimit.forEach(notificationId -> subscription.getUnreadNotifications().remove(notificationId)); + if (update.isNew()) { + subscription.getLatestUnreadNotifications().put(notification.getUuidId(), notification); + subscription.getTotalUnreadCounter().incrementAndGet(); + if (subscription.getLatestUnreadNotifications().size() > subscription.getLimit()) { + Set beyondLimit = subscription.getSortedNotifications().stream().skip(subscription.getLimit()) + .map(IdBased::getUuidId).collect(Collectors.toSet()); + beyondLimit.forEach(notificationId -> subscription.getLatestUnreadNotifications().remove(notificationId)); } sendUpdate(subscription.getSessionId(), subscription.createPartialUpdate(notification)); + } else { + if (notification.getStatus() != NotificationStatus.READ) { + if (subscription.getLatestUnreadNotifications().containsKey(notification.getUuidId())) { + subscription.getLatestUnreadNotifications().put(notification.getUuidId(), notification); + sendUpdate(subscription.getSessionId(), subscription.createPartialUpdate(notification)); + } + } else { + if (subscription.getLatestUnreadNotifications().containsKey(notification.getUuidId())) { + fetchUnreadNotifications(subscription); + sendUpdate(subscription.getSessionId(), subscription.createFullUpdate()); + } else { + subscription.getTotalUnreadCounter().decrementAndGet(); + sendUpdate(subscription.getSessionId(), subscription.createCountUpdate()); + } + } } } @@ -149,14 +162,14 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH log.trace("[{}, subId: {}] Handling notification request update: {}", subscription.getSessionId(), subscription.getSubscriptionId(), update); NotificationRequestId notificationRequestId = update.getNotificationRequestId(); if (update.isDeleted()) { - if (subscription.getUnreadNotifications().values().stream() + if (subscription.getLatestUnreadNotifications().values().stream() .anyMatch(notification -> notification.getRequestId().equals(notificationRequestId))) { fetchUnreadNotifications(subscription); sendUpdate(subscription.getSessionId(), subscription.createFullUpdate()); } } else { NotificationInfo notificationInfo = update.getNotificationInfo(); - subscription.getUnreadNotifications().values().stream() + subscription.getLatestUnreadNotifications().values().stream() .filter(notification -> notification.getRequestId().equals(notificationRequestId)) .forEach(notification -> { notification.setInfo(notificationInfo); @@ -197,7 +210,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH private void sendUpdate(String sessionId, CmdUpdate update) { - log.trace("[{}] Sending WS update: {}", sessionId, update); + log.trace("[{}, cmdId: {}] Sending WS update: {}", sessionId, update.getCmdId(), update); wsService.sendWsMsg(sessionId, update); } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java index 25d6e8725e..137f49763e 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsSubscription.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.ws.notification.sub; import lombok.Builder; import lombok.Getter; +import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.Notification; @@ -24,16 +25,19 @@ import org.thingsboard.server.service.subscription.TbSubscription; import org.thingsboard.server.service.subscription.TbSubscriptionType; import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate; -import java.util.LinkedHashMap; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; +import java.util.stream.Collectors; @Getter public class NotificationsSubscription extends TbSubscription { - private final Map unreadNotifications = new LinkedHashMap<>(); + private final Map latestUnreadNotifications = new HashMap<>(); private final int limit; private final AtomicInteger totalUnreadCounter = new AtomicInteger(); @@ -48,11 +52,17 @@ public class NotificationsSubscription extends TbSubscription getSortedNotifications() { + return latestUnreadNotifications.values().stream() + .sorted(Comparator.comparing(BaseData::getCreatedTime, Comparator.reverseOrder())) + .collect(Collectors.toList()); + } + public UnreadNotificationsUpdate createPartialUpdate(Notification notification) { return UnreadNotificationsUpdate.builder() .cmdId(getSubscriptionId()) @@ -61,4 +71,11 @@ public class NotificationsSubscription extends TbSubscription notifications; + public NotificationApiWsClient(String wsUrl, String token) throws URISyntaxException { super(new URI(wsUrl + "/api/ws/plugins/notifications?token=" + token)); } @@ -50,6 +56,7 @@ public class NotificationApiWsClient extends TbTestWebSocketClient { NotificationCmdsWrapper cmdsWrapper = new NotificationCmdsWrapper(); cmdsWrapper.setUnreadSubCmd(new NotificationsSubCmd(1, limit)); sendCmd(cmdsWrapper); + this.limit = limit; } public void subscribeForUnreadNotificationsCount() { @@ -75,8 +82,30 @@ public class NotificationApiWsClient extends TbTestWebSocketClient { CmdUpdateType updateType = CmdUpdateType.valueOf(update.get("cmdUpdateType").asText()); if (updateType == CmdUpdateType.NOTIFICATIONS) { lastDataUpdate = JacksonUtil.treeToValue(update, UnreadNotificationsUpdate.class); + unreadCount = lastDataUpdate.getTotalUnreadCount(); + if (lastDataUpdate.getNotifications() != null) { + notifications = new ArrayList<>(lastDataUpdate.getNotifications()); + } else { + Notification notificationUpdate = lastDataUpdate.getUpdate(); + boolean updated = false; + for (int i = 0; i < notifications.size(); i++) { + Notification existing = notifications.get(i); + if (existing.getId().equals(notificationUpdate.getId())) { + notifications.set(i, notificationUpdate); + updated = true; + break; + } + } + if (!updated) { + notifications.add(0, notificationUpdate); + if (notifications.size() > limit) { + notifications = notifications.subList(0, limit); + } + } + } } else if (updateType == CmdUpdateType.NOTIFICATIONS_COUNT) { lastCountUpdate = JacksonUtil.treeToValue(update, UnreadNotificationsCountUpdate.class); + unreadCount = lastCountUpdate.getTotalUnreadCount(); } super.onMessage(s); } diff --git a/common/cluster-api/src/main/proto/queue.proto b/common/cluster-api/src/main/proto/queue.proto index d10c337df3..05a8f41be1 100644 --- a/common/cluster-api/src/main/proto/queue.proto +++ b/common/cluster-api/src/main/proto/queue.proto @@ -970,6 +970,7 @@ message ToCoreNotificationMsg { VersionControlResponseMsg vcResponseMsg = 7; bytes toEdgeSyncRequestMsg = 8; bytes fromEdgeSyncResponseMsg = 9; + SubscriptionMgrMsgProto toSubscriptionMgrMsg = 10; } /* Messages that are handled by ThingsBoard RuleEngine Service */ diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java index 291688a422..958012c8c7 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java @@ -18,9 +18,15 @@ package org.thingsboard.server.dao.notification; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; public interface NotificationRuleService { + NotificationRule saveNotificationRule(TenantId tenantId, NotificationRule notificationRule); + NotificationRule findNotificationRuleById(TenantId tenantId, NotificationRuleId notificationRuleId); + PageData findNotificationRulesByTenantId(TenantId tenantId, PageLink pageLink); + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java index 10c7155d97..eb5c209383 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java @@ -29,7 +29,7 @@ public interface NotificationTargetService { NotificationTarget findNotificationTargetById(TenantId tenantId, NotificationTargetId id); - PageData findNotificationTargetsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + PageData findNotificationTargetsByTenantId(TenantId tenantId, PageLink pageLink); PageData findRecipientsForNotificationTarget(TenantId tenantId, NotificationTargetId notificationTargetId, PageLink pageLink); 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 bdcff739f7..37f69b575f 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 @@ -21,5 +21,5 @@ package org.thingsboard.server.common.data; public enum EntityType { TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, ASSET_PROFILE, API_USAGE_STATE, TB_RESOURCE, OTA_PACKAGE, EDGE, RPC, QUEUE, - NOTIFICATION_TARGET, NOTIFICATION_REQUEST; + NOTIFICATION_TARGET, NOTIFICATION_REQUEST, NOTIFICATION_RULE; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index 012a8e269f..0462952ad0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -85,6 +85,8 @@ public class EntityIdFactory { return new NotificationTargetId(uuid); case NOTIFICATION_REQUEST: return new NotificationRequestId(uuid); + case NOTIFICATION_RULE: + return new NotificationRuleId(uuid); } throw new IllegalArgumentException("EntityType " + type + " is not supported!"); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRuleId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRuleId.java index 2b963c5b88..b9b07ca334 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRuleId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/NotificationRuleId.java @@ -21,16 +21,16 @@ import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -public class NotificationRuleId extends UUIDBased { +public class NotificationRuleId extends UUIDBased implements EntityId { @JsonCreator public NotificationRuleId(@JsonProperty("id") UUID id) { super(id); } -// @Override -// public EntityType getEntityType() { -// return EntityType.NOTIFICATION_TARGET; -// } + @Override + public EntityType getEntityType() { + return EntityType.NOTIFICATION_RULE; + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java index f962db97e4..8ba27a96a8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java @@ -56,7 +56,7 @@ public class NotificationRequest extends BaseData impleme private NotificationOriginatorType originatorType; private EntityId originatorEntityId; // userId, alarmId or tenantId - private NotificationRuleId ruleId; // maybe move to child class + private NotificationRuleId ruleId; private NotificationRequestConfig additionalConfig; private NotificationRequestStatus status; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationEscalationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationEscalationConfig.java new file mode 100644 index 0000000000..8a81ad7990 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationEscalationConfig.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule; + +import lombok.Data; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + +@Data +public class NotificationEscalationConfig { + + @NotNull + @Valid + private List escalations; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java index 4d2a8ed184..9ce62f1420 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java @@ -27,10 +27,6 @@ import org.thingsboard.server.common.data.id.TenantId; import javax.validation.Valid; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import java.util.UUID; @Data @EqualsAndHashCode(callSuper = true) @@ -44,8 +40,7 @@ public class NotificationRule extends BaseData implements Ha private String notificationTextTemplate; @NotNull private NotificationTargetId initialNotificationTargetId; - @NotNull @Valid - private List escalations; + private NotificationEscalationConfig escalationConfig; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 9d477059d6..0cb2c38c6c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -677,7 +677,9 @@ public class ModelConstants { public static final String NOTIFICATION_REQUEST_RULE_ID_PROPERTY = "rule_id"; public static final String NOTIFICATION_RULE_TABLE_NAME = "notification_rule"; - // ... + public static final String NOTIFICATION_RULE_NOTIFICATION_TEXT_TEMPLATE_PROPERTY = "notification_text_template"; + public static final String NOTIFICATION_RULE_INITIAL_NOTIFICATION_TARGET_ID_PROPERTY = "initial_notification_target_id"; + public static final String NOTIFICATION_RULE_ESCALATION_CONFIG_PROPERTY = "escalation_config"; protected static final String[] NONE_AGGREGATION_COLUMNS = new String[]{LONG_VALUE_COLUMN, DOUBLE_VALUE_COLUMN, BOOLEAN_VALUE_COLUMN, STRING_VALUE_COLUMN, JSON_VALUE_COLUMN, KEY_COLUMN, TS_COLUMN}; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java index fb669a970d..f5f88d8f3a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java @@ -15,11 +15,15 @@ */ package org.thingsboard.server.dao.model.sql; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.id.NotificationRuleId; +import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.NotificationEscalationConfig; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; @@ -42,6 +46,15 @@ public class NotificationRuleEntity extends BaseSqlEntity { @Column(name = ModelConstants.NAME_PROPERTY, nullable = false) private String name; + @Column(name = ModelConstants.NOTIFICATION_RULE_NOTIFICATION_TEXT_TEMPLATE_PROPERTY, nullable = false) + private String notificationTextTemplate; + + @Column(name = ModelConstants.NOTIFICATION_RULE_INITIAL_NOTIFICATION_TARGET_ID_PROPERTY) + private UUID initialNotificationTargetId; + + @Type(type = "json") + @Column(name = ModelConstants.NOTIFICATION_RULE_ESCALATION_CONFIG_PROPERTY) + private JsonNode escalationConfig; public NotificationRuleEntity() {} @@ -50,6 +63,9 @@ public class NotificationRuleEntity extends BaseSqlEntity { setCreatedTime(notificationRule.getCreatedTime()); setTenantId(getUuid(notificationRule.getTenantId())); setName(notificationRule.getName()); + setNotificationTextTemplate(notificationRule.getNotificationTextTemplate()); + setInitialNotificationTargetId(getUuid(notificationRule.getInitialNotificationTargetId())); + setEscalationConfig(toJson(notificationRule.getEscalationConfig())); } @Override @@ -59,6 +75,9 @@ public class NotificationRuleEntity extends BaseSqlEntity { notificationRule.setCreatedTime(createdTime); notificationRule.setTenantId(createId(tenantId, TenantId::fromUUID)); notificationRule.setName(name); + notificationRule.setNotificationTextTemplate(notificationTextTemplate); + notificationRule.setInitialNotificationTargetId(createId(initialNotificationTargetId, NotificationTargetId::new)); + notificationRule.setEscalationConfig(fromJson(escalationConfig, NotificationEscalationConfig.class)); return notificationRule; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java index 7c9e404b3b..ae475a76a1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java @@ -20,6 +20,9 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.service.DataValidator; @Service @RequiredArgsConstructor @@ -27,9 +30,25 @@ public class DefaultNotificationRuleService implements NotificationRuleService { private final NotificationRuleDao notificationRuleDao; + private final NotificationRuleValidator validator = new NotificationRuleValidator(); + + @Override + public NotificationRule saveNotificationRule(TenantId tenantId, NotificationRule notificationRule) { + validator.validate(notificationRule, NotificationRule::getTenantId); + return notificationRuleDao.save(tenantId, notificationRule); + } + @Override public NotificationRule findNotificationRuleById(TenantId tenantId, NotificationRuleId notificationRuleId) { return notificationRuleDao.findById(tenantId, notificationRuleId.getId()); } + @Override + public PageData findNotificationRulesByTenantId(TenantId tenantId, PageLink pageLink) { + return notificationRuleDao.findByTenantIdAndPageLink(tenantId, pageLink); + } + + private static class NotificationRuleValidator extends DataValidator { + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java index 00892576e4..04c5403a91 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java @@ -57,7 +57,7 @@ public class DefaultNotificationTargetService implements NotificationTargetServi } @Override - public PageData findNotificationTargetsByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { + public PageData findNotificationTargetsByTenantId(TenantId tenantId, PageLink pageLink) { return notificationTargetDao.findByTenantIdAndPageLink(tenantId, pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java index 3a7cbb777b..215766d1c8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java @@ -15,8 +15,14 @@ */ package org.thingsboard.server.dao.notification; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; public interface NotificationRuleDao extends Dao { + + PageData findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java index 365a439bd5..40c081512f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java @@ -15,10 +15,15 @@ */ package org.thingsboard.server.dao.sql.notification; +import com.google.common.base.Strings; import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.NotificationRuleEntity; import org.thingsboard.server.dao.notification.NotificationRuleDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; @@ -33,6 +38,12 @@ public class JpaNotificationRuleDao extends JpaAbstractDao findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { + return DaoUtil.toPageData(notificationRuleRepository.findByTenantIdAndNameContainingIgnoreCase(tenantId.getId(), + Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); + } + @Override protected Class getEntityClass() { return NotificationRuleEntity.class; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java index a991407ab2..fd041c52e7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.dao.sql.notification; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import org.thingsboard.server.dao.model.sql.NotificationRuleEntity; @@ -23,4 +25,7 @@ import java.util.UUID; @Repository public interface NotificationRuleRepository extends JpaRepository { + + Page findByTenantIdAndNameContainingIgnoreCase(UUID tenantId, String searchText, Pageable pageable); + } diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 885608c7f5..d25ab2f6f2 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -788,7 +788,13 @@ CREATE TABLE IF NOT EXISTS notification_target ( ); CREATE TABLE IF NOT EXISTS notification_rule ( - id UUID NOT NULL CONSTRAINT notification_rule_pkey PRIMARY KEY + id UUID NOT NULL CONSTRAINT notification_rule_pkey PRIMARY KEY, + created_time BIGINT NOT NULL, + tenant_id UUID NOT NULL, + name VARCHAR(255) NOT NULL, + notification_text_template VARCHAR NOT NULL, + initial_notification_target_id UUID NULL CONSTRAINT fk_notification_rule_target_id REFERENCES notification_target(id), + escalation_config VARCHAR(500) ); CREATE TABLE IF NOT EXISTS notification_request ( From 6e8dba6e2cfc4d2a222324e797d9230b64e034aa Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 10 Nov 2022 15:42:29 +0200 Subject: [PATCH 023/488] More tests for notifications --- .../DefaultNotificationManager.java | 2 +- ...aultNotificationRuleProcessingService.java | 8 +- .../DefaultNotificationSchedulerService.java | 14 +- .../controller/TbTestWebSocketClient.java | 28 ++- .../notification/NotificationApiTest.java | 235 +++++++++++++++--- .../notification/NotificationApiWsClient.java | 7 + .../notification/NotificationsClient.java | 86 +++++++ .../NotificationRequestConfig.java | 2 +- .../NonConfirmedNotificationEscalation.java | 2 +- .../main/resources/sql/schema-entities.sql | 2 +- 10 files changed, 340 insertions(+), 46 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/service/notification/NotificationsClient.java diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationManager.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationManager.java index f4232fb72d..46d9604577 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationManager.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationManager.java @@ -87,7 +87,7 @@ public class DefaultNotificationManager extends AbstractSubscriptionService impl notificationRequest.setTenantId(tenantId); if (notificationRequest.getAdditionalConfig() != null) { NotificationRequestConfig config = notificationRequest.getAdditionalConfig(); - if (config.getSendingDelayInMinutes() > 0 && notificationRequest.getId() == null) { + if (config.getSendingDelayInSec() > 0 && notificationRequest.getId() == null) { notificationRequest.setStatus(NotificationRequestStatus.SCHEDULED); NotificationRequest savedNotificationRequest = notificationRequestService.saveNotificationRequest(tenantId, notificationRequest); forwardToNotificationSchedulerService(tenantId, savedNotificationRequest.getId(), false); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java index c8e472a8cc..b103eb28f8 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java @@ -97,7 +97,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul } if (notificationRule.getEscalationConfig() != null) { for (NonConfirmedNotificationEscalation escalation : notificationRule.getEscalationConfig().getEscalations()) { - submitNotificationRequest(tenantId, escalation.getNotificationTargetId(), notificationRule, alarm, escalation.getDelayInMinutes()); + submitNotificationRequest(tenantId, escalation.getNotificationTargetId(), notificationRule, alarm, escalation.getDelayInSec()); } } } else { @@ -116,10 +116,10 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul return alarm.getStatus().isAck() && alarm.getStatus().isCleared(); } - private void submitNotificationRequest(TenantId tenantId, NotificationTargetId targetId, NotificationRule notificationRule, Alarm alarm, int delayInMinutes) { + private void submitNotificationRequest(TenantId tenantId, NotificationTargetId targetId, NotificationRule notificationRule, Alarm alarm, int delayInSec) { NotificationRequestConfig config = new NotificationRequestConfig(); - if (delayInMinutes > 0) { - config.setSendingDelayInMinutes(delayInMinutes); + if (delayInSec > 0) { + config.setSendingDelayInSec(delayInSec); } NotificationInfo notificationInfo = constructNotificationInfo(alarm, notificationRule); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java index 36faeeee87..2ae1ca43b6 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java @@ -83,12 +83,16 @@ public class DefaultNotificationSchedulerService extends AbstractPartitionBasedS } private void scheduleNotificationRequest(TenantId tenantId, NotificationRequest request, long requestTs) { - int delayInMinutes = Optional.ofNullable(request) + int delayInSec = Optional.ofNullable(request) .map(NotificationRequest::getAdditionalConfig) - .map(NotificationRequestConfig::getSendingDelayInMinutes) + .map(NotificationRequestConfig::getSendingDelayInSec) .orElse(0); - if (delayInMinutes <= 0) return; // todo: think about: if server was down for some time and delayMs will be negative - need to send these requests as well (but when the value is within some range) - long delayMs = TimeUnit.MINUTES.toMillis(delayInMinutes) - (System.currentTimeMillis() - requestTs); + if (delayInSec <= 0) return; + long delayInMs = TimeUnit.SECONDS.toMillis(delayInSec) - (System.currentTimeMillis() - requestTs); + if (delayInMs < 0) { // in case the scheduled request processing time was during the downtime + delayInMs = 0; + // or maybe no need to process outdated notification requests ? + } ListenableScheduledFuture scheduledTask = scheduledExecutor.schedule(() -> { NotificationRequest notificationRequest = notificationRequestService.findNotificationRequestById(tenantId, request.getId()); @@ -96,7 +100,7 @@ public class DefaultNotificationSchedulerService extends AbstractPartitionBasedS notificationManager.processNotificationRequest(tenantId, notificationRequest); scheduledNotificationRequests.remove(notificationRequest.getId()); - }, delayMs, TimeUnit.MILLISECONDS); + }, delayInMs, TimeUnit.MILLISECONDS); scheduledNotificationRequests.put(request.getId(), scheduledTask); } diff --git a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java index 112442afe0..aca00222a3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; @@ -43,6 +44,7 @@ import java.util.concurrent.TimeUnit; @Slf4j public class TbTestWebSocketClient extends WebSocketClient { + @Getter private volatile String lastMsg; private volatile CountDownLatch reply; private volatile CountDownLatch update; @@ -107,10 +109,18 @@ public class TbTestWebSocketClient extends WebSocketClient { } public String waitForUpdate() { - return waitForUpdate(TimeUnit.SECONDS.toMillis(3)); + return waitForUpdate(false); + } + + public String waitForUpdate(boolean throwExceptionOnTimeout) { + return waitForUpdate(TimeUnit.SECONDS.toMillis(3), throwExceptionOnTimeout); } public String waitForUpdate(long ms) { + return waitForUpdate(ms, false); + } + + public String waitForUpdate(long ms, boolean throwExceptionOnTimeout) { try { if (update.await(ms, TimeUnit.MILLISECONDS)) { return lastMsg; @@ -118,10 +128,18 @@ public class TbTestWebSocketClient extends WebSocketClient { } catch (InterruptedException e) { log.warn("Failed to await reply", e); } - return null; + if (throwExceptionOnTimeout) { + throw new AssertionError("Waited for update for " + ms + " ms but none arrived"); + } else { + return null; + } } public String waitForReply() { + return waitForReply(false); + } + + public String waitForReply(boolean throwExceptionOnTimeout) { try { if (reply.await(3, TimeUnit.SECONDS)) { return lastMsg; @@ -129,7 +147,11 @@ public class TbTestWebSocketClient extends WebSocketClient { } catch (InterruptedException e) { log.warn("Failed to await reply", e); } - return null; + if (throwExceptionOnTimeout) { + throw new AssertionError("Waited for reply for 3 seconds but none arrived"); + } else { + return null; + } } public EntityDataUpdate parseDataReply(String msg) { diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java index b524cf7c4c..3d7e55757b 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java @@ -15,14 +15,28 @@ */ package org.thingsboard.server.service.notification; +import com.fasterxml.jackson.core.type.TypeReference; +import org.assertj.core.data.Offset; +import org.java_websocket.client.WebSocketClient; import org.junit.Before; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.rule.engine.api.NotificationManager; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationInfo; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationRequestConfig; +import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.SingleUserNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.UserListNotificationTargetConfig; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.controller.TbTestWebSocketClient; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -30,13 +44,22 @@ import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsCou import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate; import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; @DaoSqlTest public class NotificationApiTest extends AbstractControllerTest { + @Autowired + private NotificationManager notificationManager; + @Before public void beforeEach() throws Exception { loginTenantAdmin(); @@ -46,12 +69,12 @@ public class NotificationApiTest extends AbstractControllerTest { public void testSubscribingToUnreadNotificationsCount() { NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId); String notificationText1 = "Notification 1"; - submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText1); + submitNotificationRequest(notificationTarget.getId(), notificationText1); String notificationText2 = "Notification 2"; - submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText2); + submitNotificationRequest(notificationTarget.getId(), notificationText2); getWsClient().subscribeForUnreadNotificationsCount(); - getWsClient().waitForReply(); + getWsClient().waitForReply(true); UnreadNotificationsCountUpdate update = getWsClient().getLastCountUpdate(); assertThat(update.getTotalUnreadCount()).isEqualTo(2); @@ -61,17 +84,17 @@ public class NotificationApiTest extends AbstractControllerTest { public void testReceivingCountUpdates_multipleSessions() { getWsClient().subscribeForUnreadNotificationsCount(); getAnotherWsClient().subscribeForUnreadNotificationsCount(); - getWsClient().waitForReply(); - getAnotherWsClient().waitForReply(); + getWsClient().waitForReply(true); + getAnotherWsClient().waitForReply(true); assertThat(getWsClient().getLastCountUpdate().getTotalUnreadCount()).isZero(); getWsClient().registerWaitForUpdate(); getAnotherWsClient().registerWaitForUpdate(); NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId); String notificationText = "Notification"; - submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText); - getWsClient().waitForUpdate(); - getAnotherWsClient().waitForUpdate(); + submitNotificationRequest(notificationTarget.getId(), notificationText); + getWsClient().waitForUpdate(true); + getAnotherWsClient().waitForUpdate(true); assertThat(getWsClient().getLastCountUpdate().getTotalUnreadCount()).isOne(); assertThat(getAnotherWsClient().getLastCountUpdate().getTotalUnreadCount()).isOne(); @@ -81,14 +104,14 @@ public class NotificationApiTest extends AbstractControllerTest { public void testSubscribingToUnreadNotifications_multipleSessions() throws Exception { NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId); String notificationText1 = "Notification 1"; - submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText1); + submitNotificationRequest(notificationTarget.getId(), notificationText1); String notificationText2 = "Notification 2"; - submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText2); + submitNotificationRequest(notificationTarget.getId(), notificationText2); getWsClient().subscribeForUnreadNotifications(10); getAnotherWsClient().subscribeForUnreadNotifications(10); - getWsClient().waitForReply(); - getAnotherWsClient().waitForReply(); + getWsClient().waitForReply(true); + getAnotherWsClient().waitForReply(true); checkFullNotificationsUpdate(getWsClient().getLastDataUpdate(), notificationText1, notificationText2); checkFullNotificationsUpdate(getAnotherWsClient().getLastDataUpdate(), notificationText1, notificationText2); @@ -98,8 +121,8 @@ public class NotificationApiTest extends AbstractControllerTest { public void testReceivingNotificationUpdates_multipleSessions() { getWsClient().subscribeForUnreadNotifications(10); getAnotherWsClient().subscribeForUnreadNotifications(10); - getWsClient().waitForReply(); - getAnotherWsClient().waitForReply(); + getWsClient().waitForReply(true); + getAnotherWsClient().waitForReply(true); UnreadNotificationsUpdate notificationsUpdate = getWsClient().getLastDataUpdate(); assertThat(notificationsUpdate.getTotalUnreadCount()).isZero(); @@ -107,9 +130,9 @@ public class NotificationApiTest extends AbstractControllerTest { getAnotherWsClient().registerWaitForUpdate(); NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId); String notificationText = "Notification 1"; - submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText); - assertThat(getWsClient().waitForUpdate()).isNotNull(); - assertThat(getAnotherWsClient().waitForUpdate()).isNotNull(); + submitNotificationRequest(notificationTarget.getId(), notificationText); + getWsClient().waitForUpdate(true); + getAnotherWsClient().waitForUpdate(true); checkPartialNotificationsUpdate(getWsClient().getLastDataUpdate(), notificationText, 1); checkPartialNotificationsUpdate(getAnotherWsClient().getLastDataUpdate(), notificationText, 1); @@ -119,26 +142,26 @@ public class NotificationApiTest extends AbstractControllerTest { public void testMarkingAsRead_multipleSessions() { getWsClient().subscribeForUnreadNotifications(10); getAnotherWsClient().subscribeForUnreadNotifications(10); - getWsClient().waitForReply(); - getAnotherWsClient().waitForReply(); + getWsClient().waitForReply(true); + getAnotherWsClient().waitForReply(true); getAnotherWsClient().subscribeForUnreadNotificationsCount(); - getAnotherWsClient().waitForReply(); + getAnotherWsClient().waitForReply(true); NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId); getWsClient().registerWaitForUpdate(); getAnotherWsClient().registerWaitForUpdate(2); String notificationText1 = "Notification 1"; - submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText1); - assertThat(getWsClient().waitForUpdate()).isNotNull(); - assertThat(getAnotherWsClient().waitForUpdate()).isNotNull(); + submitNotificationRequest(notificationTarget.getId(), notificationText1); + getWsClient().waitForUpdate(true); + getAnotherWsClient().waitForUpdate(true); Notification notification1 = getWsClient().getLastDataUpdate().getUpdate(); getWsClient().registerWaitForUpdate(); getAnotherWsClient().registerWaitForUpdate(2); String notificationText2 = "Notification 2"; - submitNotificationRequest(notificationTarget.getId(), "Just a test", notificationText2); - assertThat(getWsClient().waitForUpdate()).isNotNull(); - assertThat(getAnotherWsClient().waitForUpdate()).isNotNull(); + submitNotificationRequest(notificationTarget.getId(), notificationText2); + getWsClient().waitForUpdate(true); + getAnotherWsClient().waitForUpdate(true); assertThat(getWsClient().getLastDataUpdate().getTotalUnreadCount()).isEqualTo(2); assertThat(getAnotherWsClient().getLastDataUpdate().getTotalUnreadCount()).isEqualTo(2); assertThat(getAnotherWsClient().getLastCountUpdate().getTotalUnreadCount()).isEqualTo(2); @@ -146,14 +169,139 @@ public class NotificationApiTest extends AbstractControllerTest { getWsClient().registerWaitForUpdate(); getAnotherWsClient().registerWaitForUpdate(2); getWsClient().markNotificationAsRead(notification1.getUuidId()); - assertThat(getWsClient().waitForUpdate()).isNotNull(); - assertThat(getAnotherWsClient().waitForUpdate()).isNotNull(); + getWsClient().waitForUpdate(true); + getAnotherWsClient().waitForUpdate(true); checkFullNotificationsUpdate(getWsClient().getLastDataUpdate(), notificationText2); checkFullNotificationsUpdate(getAnotherWsClient().getLastDataUpdate(), notificationText2); assertThat(getAnotherWsClient().getLastCountUpdate().getTotalUnreadCount()).isOne(); } + @Test + public void testDelayedNotificationRequest() throws Exception { + getWsClient().subscribeForUnreadNotifications(5); + getWsClient().waitForReply(true); + + getWsClient().registerWaitForUpdate(); + NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId); + String notificationText = "Was scheduled for 5 sec"; + NotificationRequest notificationRequest = submitNotificationRequest(notificationTarget.getId(), notificationText, 5); + assertThat(notificationRequest.getStatus()).isEqualTo(NotificationRequestStatus.SCHEDULED); + await().atLeast(4, TimeUnit.SECONDS) + .atMost(6, TimeUnit.SECONDS) + .until(() -> getWsClient().getLastMsg() != null); + + Notification delayedNotification = getWsClient().getLastDataUpdate().getUpdate(); + assertThat(delayedNotification).extracting(Notification::getText).isEqualTo(notificationText); + assertThat(delayedNotification.getCreatedTime() - notificationRequest.getCreatedTime()) + .isCloseTo(TimeUnit.SECONDS.toMillis(5), Offset.offset(500L)); + assertThat(findNotificationRequest(notificationRequest.getId()).getStatus()).isEqualTo(NotificationRequestStatus.PROCESSED); + } + + @Test + public void whenNotificationRequestIsDeleted_thenDeleteNotifications() throws Exception { + getWsClient().subscribeForUnreadNotifications(10); + getWsClient().waitForReply(true); + + getWsClient().registerWaitForUpdate(); + NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId); + NotificationRequest notificationRequest = submitNotificationRequest(notificationTarget.getId(), "Test"); + getWsClient().waitForUpdate(true); + assertThat(getWsClient().getNotifications()).singleElement().extracting(Notification::getRequestId) + .isEqualTo(notificationRequest.getId()); + assertThat(getWsClient().getUnreadCount()).isOne(); + + getWsClient().registerWaitForUpdate(); + deleteNotificationRequest(notificationRequest.getId()); + getWsClient().waitForUpdate(true); + + assertThat(getWsClient().getNotifications()).isEmpty(); + assertThat(getWsClient().getUnreadCount()).isZero(); + assertThat(getMyNotifications(false, 10)).size().isZero(); + } + + @Test + public void whenNotificationRequestIsUpdated_thenUpdateNotifications() throws Exception { + getWsClient().subscribeForUnreadNotifications(10); + getWsClient().waitForReply(true); + + NotificationTarget notificationTarget = createNotificationTarget(tenantAdminUserId); + String notificationText = "Text"; + getWsClient().registerWaitForUpdate(); + NotificationRequest notificationRequest = submitNotificationRequest(notificationTarget.getId(), notificationText); + getWsClient().waitForUpdate(true); + Notification initialNotification = getWsClient().getLastDataUpdate().getUpdate(); + assertThat(getMyNotifications(false, 10)).singleElement().isEqualTo(initialNotification); + assertThat(initialNotification.getInfo()).isNotNull().isEqualTo(notificationRequest.getNotificationInfo()); + + getWsClient().registerWaitForUpdate(); + NotificationInfo newNotificationInfo = new NotificationInfo(); + newNotificationInfo.setDescription("New description"); + notificationRequest.setNotificationInfo(newNotificationInfo); + notificationManager.updateNotificationRequest(tenantId, notificationRequest); + getWsClient().waitForUpdate(true); + Notification updatedNotification = getWsClient().getLastDataUpdate().getUpdate(); + assertThat(updatedNotification.getInfo()).isEqualTo(newNotificationInfo); + assertThat(getMyNotifications(false, 10)).singleElement().isEqualTo(updatedNotification); + } + + @Test + public void testNotificationUpdatesForUsersInTarget() throws Exception { + Map wsSessions = createUsersAndSetUpWsSessions(150); + wsSessions.forEach((user, wsClient) -> { + wsClient.subscribeForUnreadNotifications(10); + wsClient.waitForReply(true); + wsClient.subscribeForUnreadNotificationsCount(); + wsClient.waitForReply(true); + }); + + NotificationTarget notificationTarget = new NotificationTarget(); + UserListNotificationTargetConfig config = new UserListNotificationTargetConfig(); + config.setUsersIds(wsSessions.keySet().stream().map(User::getUuidId).collect(Collectors.toList())); + notificationTarget.setName("150 users"); + notificationTarget.setTenantId(tenantId); + notificationTarget.setConfiguration(config); + notificationTarget = saveNotificationTarget(notificationTarget); + + wsSessions.forEach((user, wsClient) -> wsClient.registerWaitForUpdate(2)); + NotificationRequest notificationRequest = submitNotificationRequest(notificationTarget.getId(), "Hello, ${recipientEmail}"); + await().atMost(3, TimeUnit.SECONDS) + .pollDelay(1, TimeUnit.SECONDS).pollInterval(500, TimeUnit.MILLISECONDS) + .until(() -> wsSessions.values().stream() + .allMatch(wsClient -> wsClient.getLastDataUpdate() != null + && wsClient.getLastCountUpdate() != null)); + + wsSessions.forEach((user, wsClient) -> { + assertThat(wsClient.getLastDataUpdate().getTotalUnreadCount()).isOne(); + assertThat(wsClient.getLastCountUpdate().getTotalUnreadCount()).isOne(); + + Notification notification = wsClient.getLastDataUpdate().getUpdate(); + assertThat(notification.getRecipientId()).isEqualTo(user.getId()); + assertThat(notification.getRequestId()).isEqualTo(notificationRequest.getId()); + assertThat(notification.getText()).isEqualTo("Hello, " + user.getEmail()); + }); + wsSessions.values().forEach(WebSocketClient::close); + } + + private Map createUsersAndSetUpWsSessions(int count) throws Exception { + List users = new LinkedList<>(); + for (int i = 1; i <= count; i++) { + User user = new User(); + user.setTenantId(tenantId); + user.setAuthority(Authority.TENANT_ADMIN); + user.setEmail("test-user-" + i + "@thingsboard.org"); + users.add(createUser(user, "12345678")); + } + Map wsSessions = new HashMap<>(); + for (User user : users) { + login(user.getEmail(), "12345678"); + NotificationApiWsClient wsClient = (NotificationApiWsClient) buildAndConnectWebSocketClient(); + wsSessions.put(user, wsClient); + } + loginTenantAdmin(); + return wsSessions; + } + private void checkFullNotificationsUpdate(UnreadNotificationsUpdate notificationsUpdate, String... expectedNotifications) { assertThat(notificationsUpdate.getNotifications()).extracting(Notification::getText).containsOnly(expectedNotifications); @@ -172,19 +320,46 @@ public class NotificationApiTest extends AbstractControllerTest { SingleUserNotificationTargetConfig config = new SingleUserNotificationTargetConfig(); config.setUserId(userId.getId()); notificationTarget.setConfiguration(config); + return saveNotificationTarget(notificationTarget); + } + + private NotificationTarget saveNotificationTarget(NotificationTarget notificationTarget) { return doPost("/api/notification/target", notificationTarget, NotificationTarget.class); } - private NotificationRequest submitNotificationRequest(NotificationTargetId targetId, String notificationReason, String text) { + private NotificationRequest submitNotificationRequest(NotificationTargetId targetId, String text) { + return submitNotificationRequest(targetId, text, 0); + } + + private NotificationRequest submitNotificationRequest(NotificationTargetId targetId, String text, int delayInSec) { + NotificationRequestConfig config = new NotificationRequestConfig(); + config.setSendingDelayInSec(delayInSec); + NotificationInfo notificationInfo = new NotificationInfo(); + notificationInfo.setDescription("The text: " + text); NotificationRequest notificationRequest = NotificationRequest.builder() .tenantId(tenantId) .targetId(targetId) - .notificationReason(notificationReason) + .notificationReason("Test") .textTemplate(text) + .notificationInfo(notificationInfo) + .additionalConfig(config) .build(); return doPost("/api/notification/request", notificationRequest, NotificationRequest.class); } + private NotificationRequest findNotificationRequest(NotificationRequestId id) throws Exception { + return doGet("/api/notification/request/" + id, NotificationRequest.class); + } + + private void deleteNotificationRequest(NotificationRequestId id) throws Exception { + doDelete("/api/notification/request/" + id); + } + + private List getMyNotifications(boolean unreadOnly, int limit) throws Exception { + return doGetTypedWithPageLink("/api/notifications?unreadOnly={unreadOnly}&", new TypeReference>() {}, + new PageLink(limit, 0), unreadOnly).getData(); + } + @Override protected TbTestWebSocketClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException { NotificationApiWsClient wsClient = new NotificationApiWsClient(WS_URL + wsPort, token); diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiWsClient.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiWsClient.java index 38b907bd2c..f266e763fa 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiWsClient.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiWsClient.java @@ -76,6 +76,13 @@ public class NotificationApiWsClient extends TbTestWebSocketClient { send(cmd); } + @Override + public void registerWaitForUpdate(int count) { + lastDataUpdate = null; + lastCountUpdate = null; + super.registerWaitForUpdate(count); + } + @Override public void onMessage(String s) { JsonNode update = JacksonUtil.toJsonNode(s); diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationsClient.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationsClient.java new file mode 100644 index 0000000000..bcd6134478 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationsClient.java @@ -0,0 +1,86 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.notification; + +import com.google.common.base.Strings; +import org.apache.commons.lang3.StringUtils; +import org.thingsboard.rest.client.RestClient; +import org.thingsboard.server.common.data.notification.AlarmOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationInfo; +import org.thingsboard.server.common.data.notification.NotificationOriginatorType; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Scanner; + +public class NotificationsClient extends NotificationApiWsClient { + + private NotificationsClient(String wsUrl, String token) throws Exception { + super(wsUrl, token); + } + + public static NotificationsClient newInstance(String username, String password) throws Exception { + RestClient restClient = new RestClient("http://localhost:8080"); + restClient.login(username, password); + NotificationsClient client = new NotificationsClient("ws://localhost:8080", restClient.getToken()); + client.connectBlocking(); + return client; + } + + @Override + public void onMessage(String s) { + super.onMessage(s); +// printNotificationsCount(); + printNotifications(); + } + + public void printNotifications() { + System.out.println(StringUtils.repeat(System.lineSeparator(), 20)); + List notifications = getNotifications(); + System.out.printf(" %s NEW MESSAGE%s\n\n", getUnreadCount(), notifications.size() > 1 ? "S" : ""); + notifications.forEach(notification -> { + String notificationInfoStr = ""; + if (notification.getOriginatorType() == NotificationOriginatorType.ALARM) { + AlarmOriginatedNotificationInfo info = (AlarmOriginatedNotificationInfo) notification.getInfo(); + notificationInfoStr = String.format("Alarm of type %s - %s severity - status: %s", + info.getAlarmType(), info.getAlarmSeverity(), info.getAlarmStatus()); + } else if (notification.getInfo() != null) { + notificationInfoStr = Strings.nullToEmpty(notification.getInfo().getDescription()); + } + SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); + String time = format.format(new Date(notification.getCreatedTime())); + System.out.printf("[%s] %-19s | %-30s | (%s)\n", time, notification.getReason(), notification.getText(), notificationInfoStr); + }); + System.out.println(StringUtils.repeat(System.lineSeparator(), 5)); + } + + public void printNotificationsCount() { + System.out.println(); + System.out.println(); + System.out.println(); + int unreadCount = getUnreadCount(); + System.out.printf("\r\r%s NEW MESSAGE%s", unreadCount, unreadCount > 1 ? "S" : ""); + } + + public static void main(String[] args) throws Exception { + NotificationsClient client = NotificationsClient.newInstance("tenant@thingsboard.org", "tenant"); + client.subscribeForUnreadNotifications(5); +// client.subscribeForUnreadNotificationsCount(); + new Scanner(System.in).nextLine(); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java index fd01c91255..1ecff879f6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java @@ -19,5 +19,5 @@ import lombok.Data; @Data public class NotificationRequestConfig { - private int sendingDelayInMinutes; + private int sendingDelayInSec; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NonConfirmedNotificationEscalation.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NonConfirmedNotificationEscalation.java index 1174cedb06..a62af09eb5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NonConfirmedNotificationEscalation.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NonConfirmedNotificationEscalation.java @@ -25,7 +25,7 @@ import javax.validation.constraints.NotNull; public class NonConfirmedNotificationEscalation { @Min(1) - private int delayInMinutes; // delay since initial notification request // if no one from previous escalation item has read the notification, send notifications after this time to other recipients + private int delayInSec; @NotNull private NotificationTargetId notificationTargetId; diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index d25ab2f6f2..262bd9a609 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -784,7 +784,7 @@ CREATE TABLE IF NOT EXISTS notification_target ( created_time BIGINT NOT NULL, tenant_id UUID NOT NULL, name VARCHAR(255) NOT NULL, - configuration varchar(1000) NOT NULL + configuration VARCHAR NOT NULL ); CREATE TABLE IF NOT EXISTS notification_rule ( From 80ca9cc787c3836264ddbc8af7551aaa036c60e7 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 10 Nov 2022 15:49:02 +0200 Subject: [PATCH 024/488] WS session updates: remove 'synchronized' and use binary semaphore --- .../controller/plugin/TbWebSocketHandler.java | 60 +++++++------------ 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index 00f3be68c4..87bf110c81 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -52,13 +52,11 @@ import javax.websocket.Session; import java.io.IOException; import java.net.URI; import java.security.InvalidParameterException; -import java.util.Queue; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.Semaphore; import static org.thingsboard.server.service.ws.DefaultWebSocketService.NUMBER_OF_PING_ATTEMPTS; @@ -139,9 +137,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke if (!checkLimits(session, sessionRef)) { return; } - var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultProfileConfiguration(); - internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef, tenantProfileConfiguration.getWsMsgQueueLimitPerSession() > 0 ? - tenantProfileConfiguration.getWsMsgQueueLimitPerSession() : 500)); + internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef)); externalSessionMap.put(externalSessionId, internalSessionId); processInWebSocketService(sessionRef, SessionEvent.onEstablished()); @@ -216,22 +212,21 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke private final RemoteEndpoint.Async asyncRemote; private final WebSocketSessionRef sessionRef; - private final AtomicBoolean isSending = new AtomicBoolean(false); - private final Queue> msgQueue; + // TODO: carefully review ( + discuss removal of the msgQueue) + private final Semaphore sendingSemaphore = new Semaphore(1); private volatile long lastActivityTime; - SessionMetaData(WebSocketSession session, WebSocketSessionRef sessionRef, int maxMsgQueuePerSession) { + SessionMetaData(WebSocketSession session, WebSocketSessionRef sessionRef) { super(); this.session = session; Session nativeSession = ((NativeWebSocketSession) session).getNativeSession(Session.class); this.asyncRemote = nativeSession.getAsyncRemote(); this.sessionRef = sessionRef; - this.msgQueue = new LinkedBlockingQueue<>(maxMsgQueuePerSession); this.lastActivityTime = System.currentTimeMillis(); } - synchronized void sendPing(long currentTime) { + void sendPing(long currentTime) { try { long timeSinceLastActivity = currentTime - lastActivityTime; if (timeSinceLastActivity >= pingTimeout) { @@ -246,40 +241,37 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke } } - private void closeSession(CloseStatus reason) { + void closeSession(CloseStatus reason) { try { close(this.sessionRef, reason); } catch (IOException ioe) { log.trace("[{}] Session transport error", session.getId(), ioe); + } finally { + sendingSemaphore.release(); } } - synchronized void processPongMessage(long currentTime) { + void processPongMessage(long currentTime) { lastActivityTime = currentTime; } - synchronized void sendMsg(String msg) { + void sendMsg(String msg) { sendMsg(new TbWebSocketTextMsg(msg)); } - synchronized void sendMsg(TbWebSocketMsg msg) { - if (isSending.compareAndSet(false, true)) { + void sendMsg(TbWebSocketMsg msg) { + try { + sendingSemaphore.acquire(); sendMsgInternal(msg); - } else { - try { - msgQueue.add(msg); - } catch (RuntimeException e) { - if (log.isTraceEnabled()) { - log.trace("[{}][{}] Session closed due to queue error", sessionRef.getSecurityCtx().getTenantId(), session.getId(), e); - } else { - log.info("[{}][{}] Session closed due to queue error", sessionRef.getSecurityCtx().getTenantId(), session.getId()); - } - closeSession(CloseStatus.POLICY_VIOLATION.withReason("Max pending updates limit reached!")); - } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } catch (Exception e) { + sendingSemaphore.release(); + throw e; } } - private void sendMsgInternal(TbWebSocketMsg msg) { + void sendMsgInternal(TbWebSocketMsg msg) { try { if (TbWebSocketMsgType.TEXT.equals(msg.getType())) { TbWebSocketTextMsg textMsg = (TbWebSocketTextMsg) msg; @@ -287,7 +279,6 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke } else { TbWebSocketPingMsg pingMsg = (TbWebSocketPingMsg) msg; this.asyncRemote.sendPing(pingMsg.getMsg()); - processNextMsg(); } } catch (Exception e) { log.trace("[{}] Failed to send msg", session.getId(), e); @@ -301,16 +292,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke log.trace("[{}] Failed to send msg", session.getId(), result.getException()); closeSession(CloseStatus.SESSION_NOT_RELIABLE); } else { - processNextMsg(); - } - } - - private void processNextMsg() { - TbWebSocketMsg msg = msgQueue.poll(); - if (msg != null) { - sendMsgInternal(msg); - } else { - isSending.set(false); + sendingSemaphore.release(); } } } From 684ef4a65883cc1ea1eee91a5c7f2e0603460f24 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 11 Nov 2022 14:06:09 +0200 Subject: [PATCH 025/488] Add ability to update notification request --- .../controller/NotificationController.java | 69 ++++++++++++------- .../DefaultNotificationManager.java | 9 ++- ...aultNotificationRuleProcessingService.java | 25 +++++-- .../DefaultNotificationCommandsHandler.java | 19 +++-- .../sub/NotificationRequestUpdate.java | 1 + .../notification/NotificationApiTest.java | 6 +- .../NotificationRequestService.java | 3 + .../dao/notification/NotificationService.java | 2 +- .../notification/NotificationRequest.java | 15 +++- .../notification/NotificationRequestInfo.java | 35 ++++++++++ .../DefaultNotificationRequestService.java | 6 ++ .../DefaultNotificationService.java | 13 +--- .../dao/notification/NotificationDao.java | 2 +- .../notification/NotificationRequestDao.java | 4 ++ .../sql/notification/JpaNotificationDao.java | 4 +- .../JpaNotificationRequestDao.java | 19 +++++ .../notification/NotificationRepository.java | 17 ++++- .../rule/engine/api/NotificationManager.java | 2 +- .../rule/engine/profile/DeviceState.java | 1 - 19 files changed, 187 insertions(+), 65 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestInfo.java diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index a392e6ec74..40d3c47fff 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationOriginatorType; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationRequestInfo; import org.thingsboard.server.common.data.notification.NotificationSeverity; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -88,34 +89,43 @@ public class NotificationController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public NotificationRequest createNotificationRequest(@RequestBody NotificationRequest notificationRequest, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { - accessControlService.checkPermission(user, Resource.NOTIFICATION_REQUEST, Operation.CREATE, null, notificationRequest); // todo: check permission for notification target - if (notificationRequest.getId() != null) { - // TODO: think about notification request update - throw new IllegalArgumentException("Notification request cannot be changed. You can delete it and create a new one"); - } - notificationRequest.setOriginatorType(NotificationOriginatorType.USER); - notificationRequest.setOriginatorEntityId(user.getId()); - if (StringUtils.isBlank(notificationRequest.getNotificationReason())) { - notificationRequest.setNotificationReason(NotificationRequest.GENERAL_NOTIFICATION_REASON); - } - if (notificationRequest.getNotificationSeverity() == null) { - notificationRequest.setNotificationSeverity(NotificationSeverity.NORMAL); - } - if (notificationRequest.getNotificationInfo() != null && notificationRequest.getNotificationInfo().getOriginatorType() != null) { - throw new IllegalArgumentException("Unsupported notification info type"); - } - notificationRequest.setRuleId(null); - notificationRequest.setStatus(null); - try { - NotificationRequest savedNotificationRequest = notificationManager.processNotificationRequest(user.getTenantId(), notificationRequest); - logEntityAction(user, EntityType.NOTIFICATION_REQUEST, savedNotificationRequest, ActionType.ADDED); - return savedNotificationRequest; - } catch (Exception e) { - logEntityAction(user, EntityType.NOTIFICATION_REQUEST, notificationRequest, null, ActionType.ADDED, e); - throw e; + if (notificationRequest.getId() == null) { + accessControlService.checkPermission(user, Resource.NOTIFICATION_REQUEST, Operation.CREATE, null, notificationRequest); + notificationRequest.setOriginatorType(NotificationOriginatorType.USER); + notificationRequest.setOriginatorEntityId(user.getId()); + if (StringUtils.isBlank(notificationRequest.getNotificationReason())) { + notificationRequest.setNotificationReason("General"); + } + if (notificationRequest.getNotificationSeverity() == null) { + notificationRequest.setNotificationSeverity(NotificationSeverity.NORMAL); + } + if (notificationRequest.getNotificationInfo() != null && notificationRequest.getNotificationInfo().getOriginatorType() != null) { + throw new IllegalArgumentException("Unsupported notification info type"); + } + notificationRequest.setRuleId(null); + notificationRequest.setStatus(null); + return notificationManager.processNotificationRequest(user.getTenantId(), notificationRequest); + } else { + NotificationRequest existingNotificationRequest = notificationRequestService.findNotificationRequestById(user.getTenantId(), notificationRequest.getId()); + checkNotNull(existingNotificationRequest); + accessControlService.checkPermission(user, Resource.NOTIFICATION_REQUEST, Operation.WRITE, notificationRequest.getId(), notificationRequest); + + existingNotificationRequest.setNotificationReason(notificationRequest.getNotificationReason()); + existingNotificationRequest.setTextTemplate(notificationRequest.getTextTemplate()); + existingNotificationRequest.setNotificationSeverity(notificationRequest.getNotificationSeverity()); + return notificationManager.updateNotificationRequest(user.getTenantId(), existingNotificationRequest); } +// +// try { +// NotificationRequest savedNotificationRequest = ; +// logEntityAction(user, EntityType.NOTIFICATION_REQUEST, savedNotificationRequest, ActionType.ADDED); +// return savedNotificationRequest; +// } catch (Exception e) { +// logEntityAction(user, EntityType.NOTIFICATION_REQUEST, notificationRequest, null, ActionType.ADDED, e); +// throw e; +// } } @GetMapping("/notification/request/{id}") @@ -126,6 +136,15 @@ public class NotificationController extends BaseController { return notificationRequestService.findNotificationRequestById(user.getTenantId(), notificationRequestId); } + @GetMapping("/notification/request/info/{id}") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public NotificationRequestInfo getNotificationRequestInfoById(@PathVariable UUID id, + @AuthenticationPrincipal SecurityUser user) { + // fixme: permission checks + NotificationRequestId notificationRequestId = new NotificationRequestId(id); + return notificationRequestService.getNotificationRequestInfoById(user.getTenantId(), notificationRequestId); + } + @GetMapping("/notification/requests") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public PageData getNotificationRequests(@RequestParam int pageSize, diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationManager.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationManager.java index 46d9604577..73926c70be 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationManager.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationManager.java @@ -153,15 +153,18 @@ public class DefaultNotificationManager extends AbstractSubscriptionService impl } @Override - public void updateNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { + public NotificationRequest updateNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { log.debug("Updating notification request {}", notificationRequest.getId()); - notificationRequestService.saveNotificationRequest(tenantId, notificationRequest); - notificationService.updateNotificationsInfosByRequestId(tenantId, notificationRequest.getId(), notificationRequest.getNotificationInfo()); + notificationRequest = notificationRequestService.saveNotificationRequest(tenantId, notificationRequest); + notificationService.updateNotificationsByRequestId(tenantId, notificationRequest.getId(), + notificationRequest.getNotificationReason(), notificationRequest.getNotificationInfo()); onNotificationRequestUpdate(tenantId, NotificationRequestUpdate.builder() .notificationRequestId(notificationRequest.getId()) + .notificationReason(notificationRequest.getNotificationReason()) .notificationInfo(notificationRequest.getNotificationInfo()) .deleted(false) .build()); + return notificationRequest; } private Notification createNotification(User recipient, NotificationRequest notificationRequest) { diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java index b103eb28f8..a0e1dc1bb1 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java @@ -18,8 +18,10 @@ package org.thingsboard.server.service.notification; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.rule.engine.api.NotificationManager; +import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.NotificationTargetId; @@ -38,10 +40,12 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import java.util.List; +import java.util.Map; @Service @TbCoreComponent @RequiredArgsConstructor +@Slf4j public class DefaultNotificationRuleProcessingService implements NotificationRuleProcessingService { private final NotificationRuleService notificationRuleService; @@ -78,6 +82,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul // todo: think about: what if notification rule was updated? private void onAlarmUpdate(TenantId tenantId, NotificationRuleId notificationRuleId, Alarm alarm, boolean deleted) { + log.debug("Processing alarm update ({}) with notification rule {}", alarm.getId(), notificationRuleId); List notificationRequests = notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityId(tenantId, notificationRuleId, alarm.getId()); NotificationRule notificationRule = notificationRuleService.findNotificationRuleById(tenantId, notificationRuleId); if (notificationRule == null) return; @@ -101,7 +106,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul } } } else { - NotificationInfo newNotificationInfo = constructNotificationInfo(alarm, notificationRule); + NotificationInfo newNotificationInfo = constructNotificationInfo(alarm); for (NotificationRequest notificationRequest : notificationRequests) { NotificationInfo previousNotificationInfo = notificationRequest.getNotificationInfo(); if (!previousNotificationInfo.equals(newNotificationInfo)) { @@ -121,13 +126,13 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul if (delayInSec > 0) { config.setSendingDelayInSec(delayInSec); } - NotificationInfo notificationInfo = constructNotificationInfo(alarm, notificationRule); + NotificationInfo notificationInfo = constructNotificationInfo(alarm); NotificationRequest notificationRequest = NotificationRequest.builder() .tenantId(tenantId) .targetId(targetId) .notificationReason("Alarm") - .textTemplate(notificationRule.getNotificationTextTemplate()) // todo: format with alarm vars + .textTemplate(formatNotificationTextTemplate(notificationRule.getNotificationTextTemplate(), alarm)) .notificationInfo(notificationInfo) .notificationSeverity(NotificationSeverity.NORMAL) // todo: from alarm severity .originatorType(NotificationOriginatorType.ALARM) @@ -138,7 +143,19 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul notificationManager.processNotificationRequest(tenantId, notificationRequest); } - private NotificationInfo constructNotificationInfo(Alarm alarm, NotificationRule notificationRule) { + private String formatNotificationTextTemplate(String textTemplate, Alarm alarm) { + Map context = Map.of( // fixme: notification text is not updatable + "alarmType", alarm.getType(), + "alarmId", alarm.getId().toString(), + "alarmOriginatorEntityType", alarm.getOriginator().getEntityType().toString(), + "alarmOriginatorId", alarm.getOriginator().getId().toString(), + "alarmSeverity", alarm.getSeverity().toString(), + "alarmStatus", alarm.getStatus().toString() + ); + return TbNodeUtils.processTemplate(textTemplate, context); + } + + private NotificationInfo constructNotificationInfo(Alarm alarm) { return AlarmOriginatedNotificationInfo.builder() .alarmId(alarm.getId()) .alarmType(alarm.getType()) diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java index 5dd8bd7096..e86fd58161 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java @@ -20,30 +20,29 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import org.thingsboard.rule.engine.api.NotificationManager; import org.thingsboard.server.common.data.id.IdBased; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; -import org.thingsboard.server.common.data.notification.NotificationInfo; import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.rule.engine.api.NotificationManager; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.ws.notification.cmd.NotificationsCountSubCmd; -import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; -import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; -import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscription; import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; +import org.thingsboard.server.service.ws.WebSocketService; import org.thingsboard.server.service.ws.WebSocketSessionRef; import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationsAsReadCmd; +import org.thingsboard.server.service.ws.notification.cmd.NotificationsCountSubCmd; import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; -import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; +import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; +import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationsCountSubscription; -import org.thingsboard.server.service.ws.WebSocketService; +import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscription; +import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; @@ -168,11 +167,11 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH sendUpdate(subscription.getSessionId(), subscription.createFullUpdate()); } } else { - NotificationInfo notificationInfo = update.getNotificationInfo(); subscription.getLatestUnreadNotifications().values().stream() .filter(notification -> notification.getRequestId().equals(notificationRequestId)) .forEach(notification -> { - notification.setInfo(notificationInfo); + notification.setReason(update.getNotificationReason()); + notification.setInfo(update.getNotificationInfo()); sendUpdate(subscription.getSessionId(), subscription.createPartialUpdate(notification)); }); } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationRequestUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationRequestUpdate.java index 36c3b2a441..05642743ee 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationRequestUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationRequestUpdate.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.notification.NotificationInfo; @Builder public class NotificationRequestUpdate { private NotificationRequestId notificationRequestId; + private String notificationReason; private NotificationInfo notificationInfo; private boolean deleted; } diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java index 3d7e55757b..cce7ff749e 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java @@ -247,7 +247,7 @@ public class NotificationApiTest extends AbstractControllerTest { @Test public void testNotificationUpdatesForUsersInTarget() throws Exception { - Map wsSessions = createUsersAndSetUpWsSessions(150); + Map wsSessions = createUsersAndSetUpWsSessions(100); wsSessions.forEach((user, wsClient) -> { wsClient.subscribeForUnreadNotifications(10); wsClient.waitForReply(true); @@ -258,14 +258,14 @@ public class NotificationApiTest extends AbstractControllerTest { NotificationTarget notificationTarget = new NotificationTarget(); UserListNotificationTargetConfig config = new UserListNotificationTargetConfig(); config.setUsersIds(wsSessions.keySet().stream().map(User::getUuidId).collect(Collectors.toList())); - notificationTarget.setName("150 users"); + notificationTarget.setName("100 users"); notificationTarget.setTenantId(tenantId); notificationTarget.setConfiguration(config); notificationTarget = saveNotificationTarget(notificationTarget); wsSessions.forEach((user, wsClient) -> wsClient.registerWaitForUpdate(2)); NotificationRequest notificationRequest = submitNotificationRequest(notificationTarget.getId(), "Hello, ${recipientEmail}"); - await().atMost(3, TimeUnit.SECONDS) + await().atMost(5, TimeUnit.SECONDS) .pollDelay(1, TimeUnit.SECONDS).pollInterval(500, TimeUnit.MILLISECONDS) .until(() -> wsSessions.values().stream() .allMatch(wsClient -> wsClient.getLastDataUpdate() != null diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java index 7ae96ada79..9a5687d04c 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationRequestInfo; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -39,4 +40,6 @@ public interface NotificationRequestService { PageData findScheduledNotificationRequests(PageLink pageLink); + NotificationRequestInfo getNotificationRequestInfoById(TenantId tenantId, NotificationRequestId id); + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java index 498e3dfa3f..7cc4fd9183 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java @@ -38,6 +38,6 @@ public interface NotificationService { int countUnreadNotificationsByUserId(TenantId tenantId, UserId userId); - int updateNotificationsInfosByRequestId(TenantId tenantId, NotificationRequestId notificationRequestId, NotificationInfo notificationInfo); + int updateNotificationsByRequestId(TenantId tenantId, NotificationRequestId notificationRequestId, String notificationReason, NotificationInfo notificationInfo); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java index 8ba27a96a8..142c3c6b9e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java @@ -61,7 +61,20 @@ public class NotificationRequest extends BaseData impleme private NotificationRequestConfig additionalConfig; private NotificationRequestStatus status; - public static final String GENERAL_NOTIFICATION_REASON = "General"; + public NotificationRequest(NotificationRequest other) { + super(other); + this.tenantId = other.tenantId; + this.targetId = other.targetId; + this.notificationReason = other.notificationReason; + this.textTemplate = other.textTemplate; + this.notificationInfo = other.notificationInfo; + this.notificationSeverity = other.notificationSeverity; + this.originatorType = other.originatorType; + this.originatorEntityId = other.originatorEntityId; + this.ruleId = other.ruleId; + this.additionalConfig = other.additionalConfig; + this.status = other.status; + } @JsonIgnore @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestInfo.java new file mode 100644 index 0000000000..53835200d7 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestInfo.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Map; + +@Data +@EqualsAndHashCode(callSuper = true) +public class NotificationRequestInfo extends NotificationRequest { + + private int sent; + private int read; + private Map statusesByRecipient; + + public NotificationRequestInfo(NotificationRequest notificationRequest) { + super(notificationRequest); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java index af67c8ae40..be49f05c09 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationRequestInfo; import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -71,6 +72,11 @@ public class DefaultNotificationRequestService implements NotificationRequestSer return notificationRequestDao.findAllByStatus(NotificationRequestStatus.SCHEDULED, pageLink); } + @Override + public NotificationRequestInfo getNotificationRequestInfoById(TenantId tenantId, NotificationRequestId id) { + return notificationRequestDao.getNotificationRequestInfoById(tenantId, id); + } + private static class NotificationRequestValidator extends DataValidator { diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java index 37a45a3a72..1e57ffaf5d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java @@ -17,28 +17,19 @@ package org.thingsboard.server.dao.notification; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; -import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationInfo; -import org.thingsboard.server.common.data.notification.NotificationRequest; -import org.thingsboard.server.common.data.notification.NotificationRequestStatus; -import org.thingsboard.server.common.data.notification.NotificationSeverity; import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; -import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.sql.query.EntityKeyMapping; -import java.util.List; - @Service @Slf4j @RequiredArgsConstructor @@ -83,8 +74,8 @@ public class DefaultNotificationService implements NotificationService { } @Override - public int updateNotificationsInfosByRequestId(TenantId tenantId, NotificationRequestId notificationRequestId, NotificationInfo notificationInfo) { - return notificationDao.updateInfosByRequestId(tenantId, notificationRequestId, notificationInfo); + public int updateNotificationsByRequestId(TenantId tenantId, NotificationRequestId notificationRequestId, String notificationReason, NotificationInfo notificationInfo) { + return notificationDao.updateByRequestId(tenantId, notificationRequestId, notificationReason, notificationInfo); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java index dcfba4d704..8df17f4491 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java @@ -38,6 +38,6 @@ public interface NotificationDao extends Dao { PageData findByRequestId(TenantId tenantId, NotificationRequestId notificationRequestId, PageLink pageLink); - int updateInfosByRequestId(TenantId tenantId, NotificationRequestId notificationRequestId, NotificationInfo notificationInfo); + int updateByRequestId(TenantId tenantId, NotificationRequestId notificationRequestId, String notificationReason, NotificationInfo notificationInfo); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java index 2036105ffb..6036143f87 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java @@ -16,9 +16,11 @@ package org.thingsboard.server.dao.notification; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationRequestInfo; import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -34,4 +36,6 @@ public interface NotificationRequestDao extends Dao { PageData findAllByStatus(NotificationRequestStatus status, PageLink pageLink); + NotificationRequestInfo getNotificationRequestInfoById(TenantId tenantId, NotificationRequestId id); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java index cd98941011..5b4bf6eaa5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java @@ -91,8 +91,8 @@ public class JpaNotificationDao extends JpaAbstractDao implements NotificationRequestDao { private final NotificationRequestRepository notificationRequestRepository; + private final NotificationRepository notificationRepository; @Override public PageData findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { @@ -58,6 +65,18 @@ public class JpaNotificationRequestDao extends JpaAbstractDao statusesByRecipient = notificationRepository.getStatusesByRecipientForRequestId(id.getId()).stream() + .collect(Collectors.toMap(r -> (String) r[0], r -> (NotificationStatus) r[1])); + notificationRequestInfo.setStatusesByRecipient(statusesByRecipient); + return notificationRequestInfo; + } + @Override protected Class getEntityClass() { return NotificationRequestEntity.class; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java index 3f46a3dfa9..09ce53e7b8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java @@ -27,6 +27,7 @@ import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.dao.model.sql.NotificationEntity; +import java.util.List; import java.util.UUID; @Repository @@ -50,7 +51,19 @@ public interface NotificationRepository extends JpaRepository getStatusesByRecipientForRequestId(@Param("requestId") UUID requestId); } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationManager.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationManager.java index ac336e9fe4..dc57a876fd 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationManager.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationManager.java @@ -29,6 +29,6 @@ public interface NotificationManager { void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId); - void updateNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest); + NotificationRequest updateNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java index 1ef06547ba..5ab5adc7fc 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java @@ -191,7 +191,6 @@ class DeviceState { AlarmState alarmState = alarmStates.computeIfAbsent(alarm.getId(), a -> new AlarmState(this.deviceProfile, deviceId, alarm, getOrInitPersistedAlarmState(alarm), dynamicPredicateValueCtx)); alarmState.processAckAlarm(alarmNf); - // todo: process notification rule } ctx.tellSuccess(msg); } From d01201acf354bc36ea04ce9564cc690fedce0621 Mon Sep 17 00:00:00 2001 From: devaskim Date: Tue, 15 Nov 2022 21:56:59 +0500 Subject: [PATCH 026/488] Add filter duplication option. --- .../filter/filters-dialog.component.html | 9 +++++ .../filter/filters-dialog.component.ts | 34 ++++++++++++++++++- .../assets/locale/locale.constant-en_US.json | 1 + 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/filters-dialog.component.html b/ui-ngx/src/app/modules/home/components/filter/filters-dialog.component.html index 25f6e57e2d..55079955a8 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filters-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filters-dialog.component.html @@ -64,6 +64,15 @@ matTooltipPosition="above"> edit +