From e09aef8d224781d5ab1267c1d2909a76c1721724 Mon Sep 17 00:00:00 2001 From: thingsboard Date: Wed, 20 Jul 2022 17:43:46 +0300 Subject: [PATCH 01/30] 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 02/30] 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 03/30] 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 04/30] 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 05/30] 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 06/30] 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 07/30] 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 6e71abe10bb9476095880664d945fb56c1d3a110 Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Tue, 27 Dec 2022 11:46:54 +0200 Subject: [PATCH 08/30] improvements for report device activity during RPC processing --- .../transport/coap/client/DefaultCoapClientContext.java | 2 +- .../server/rpc/RpcDownlinkRequestCallbackProxy.java | 2 +- .../server/transport/mqtt/MqttTransportHandler.java | 5 +---- .../server/common/transport/TransportService.java | 2 ++ .../transport/service/DefaultTransportService.java | 9 ++++++++- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DefaultCoapClientContext.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DefaultCoapClientContext.java index f8336025d4..9933fd50a6 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DefaultCoapClientContext.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DefaultCoapClientContext.java @@ -598,7 +598,7 @@ public class DefaultCoapClientContext implements CoapClientContext { response.addMessageObserver(new TbCoapMessageObserver(requestId, id -> { TransportProtos.ToDeviceRpcRequestMsg rpcRequestMsg = transportContext.getRpcAwaitingAck().remove(id); if (rpcRequestMsg != null) { - transportService.process(state.getSession(), rpcRequestMsg, RpcStatus.DELIVERED, TransportServiceCallback.EMPTY); + transportService.process(state.getSession(), rpcRequestMsg, RpcStatus.DELIVERED, true, TransportServiceCallback.EMPTY); } }, id -> { TransportProtos.ToDeviceRpcRequestMsg rpcRequestMsg = transportContext.getRpcAwaitingAck().remove(id); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDownlinkRequestCallbackProxy.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDownlinkRequestCallbackProxy.java index c26ef2c327..5a16efeaf7 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDownlinkRequestCallbackProxy.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDownlinkRequestCallbackProxy.java @@ -65,7 +65,7 @@ public abstract class RpcDownlinkRequestCallbackProxy implements DownlinkR @Override public void onSuccess(R request, T response) { - transportService.process(client.getSession(), this.request, RpcStatus.DELIVERED, TransportServiceCallback.EMPTY); + transportService.process(client.getSession(), this.request, RpcStatus.DELIVERED, true, TransportServiceCallback.EMPTY); sendRpcReplyOnSuccess(response); if (callback != null) { callback.onSuccess(request, response); 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 7515a1fcd2..eccb7e5cb5 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 @@ -21,7 +21,6 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.codec.mqtt.MqttConnAckMessage; -import io.netty.handler.codec.mqtt.MqttConnAckVariableHeader; import io.netty.handler.codec.mqtt.MqttConnectMessage; import io.netty.handler.codec.mqtt.MqttConnectReturnCode; import io.netty.handler.codec.mqtt.MqttFixedHeader; @@ -95,11 +94,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.amazonaws.util.StringUtils.UTF8; -import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK; import static io.netty.handler.codec.mqtt.MqttMessageType.CONNECT; import static io.netty.handler.codec.mqtt.MqttMessageType.PINGRESP; import static io.netty.handler.codec.mqtt.MqttMessageType.SUBACK; -import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK; import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE; import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE; import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_CLOSED; @@ -305,7 +302,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement int msgId = ((MqttPubAckMessage) msg).variableHeader().messageId(); TransportProtos.ToDeviceRpcRequestMsg rpcRequest = rpcAwaitingAck.remove(msgId); if (rpcRequest != null) { - transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequest, RpcStatus.DELIVERED, TransportServiceCallback.EMPTY); + transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequest, RpcStatus.DELIVERED, true, TransportServiceCallback.EMPTY); } break; default: diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java index f1d3f7e26c..ce9834834d 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java @@ -117,6 +117,8 @@ public interface TransportService { void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfo, ToDeviceRpcRequestMsg msg, RpcStatus rpcStatus, boolean reportActivity, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfo, ToDeviceRpcRequestMsg msg, RpcStatus rpcStatus, TransportServiceCallback callback); void process(SessionInfoProto sessionInfo, SubscriptionInfoProto msg, TransportServiceCallback callback); diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index bf03122b18..b5de6d6976 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -647,6 +647,11 @@ public class DefaultTransportService implements TransportService { @Override public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcRequestMsg msg, RpcStatus rpcStatus, TransportServiceCallback callback) { + process(sessionInfo, msg, rpcStatus, false, callback); + } + + @Override + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcRequestMsg msg, RpcStatus rpcStatus, boolean reportActivity, TransportServiceCallback callback) { TransportProtos.ToDeviceRpcResponseStatusMsg responseMsg = TransportProtos.ToDeviceRpcResponseStatusMsg.newBuilder() .setRequestId(msg.getRequestId()) .setRequestIdLSB(msg.getRequestIdLSB()) @@ -655,7 +660,9 @@ public class DefaultTransportService implements TransportService { .build(); if (checkLimits(sessionInfo, responseMsg, callback)) { - reportActivityInternal(sessionInfo); + if (reportActivity) { + reportActivityInternal(sessionInfo); + } sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setRpcResponseStatusMsg(responseMsg).build(), new ApiStatsProxyCallback<>(getTenantId(sessionInfo), getCustomerId(sessionInfo), 1, TransportServiceCallback.EMPTY)); } From 7e346894fb74f24f0f5e5a4a992a6931730f084b Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Tue, 27 Dec 2022 12:02:43 +0200 Subject: [PATCH 09/30] Remove quotes if needed --- .../rule/engine/rest/TbHttpClient.java | 15 +++++++++++++-- .../rest/TbRestApiCallNodeConfiguration.java | 2 ++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java index b7414c5e0d..20d99e7107 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java @@ -35,7 +35,6 @@ import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory; import org.springframework.http.client.Netty4ClientHttpRequestFactory; -import org.thingsboard.server.common.data.StringUtils; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureCallback; import org.springframework.web.client.AsyncRestTemplate; @@ -49,6 +48,7 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.credentials.BasicCredentials; import org.thingsboard.rule.engine.credentials.ClientCredentials; import org.thingsboard.rule.engine.credentials.CredentialsType; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -192,7 +192,11 @@ public class TbHttpClient { config.isIgnoreRequestBody()) { entity = new HttpEntity<>(headers); } else { - entity = new HttpEntity<>(msg.getData(), headers); + String data = msg.getData(); + if (config.isTrimDoubleQuotes()) { + data = trimDoubleQuotes(data); + } + entity = new HttpEntity<>(data, headers); } URI uri = buildEncodedUri(endpointUrl); @@ -334,4 +338,11 @@ public class TbHttpClient { } } + private static String trimDoubleQuotes(String s) { + if (s.startsWith("\"") && s.endsWith("\"")) { + return s.substring(1, s.length() - 1); + } + return s; + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java index 76e0c0d9c4..4c06ccc9a1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java @@ -38,6 +38,7 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration Date: Wed, 1 Mar 2023 17:49:26 +0200 Subject: [PATCH 10/30] Delete unused variables --- .../rule/engine/rest/TbRestApiCallNodeConfiguration.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java index 5d4651a983..3fb28d474b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java @@ -37,9 +37,7 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration Date: Thu, 2 Mar 2023 11:40:46 +0200 Subject: [PATCH 11/30] Change logic similar to existing on PE --- .../rule/engine/rest/TbHttpClient.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java index 1473201d1b..6f9541f5b1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java @@ -192,11 +192,7 @@ public class TbHttpClient { config.isIgnoreRequestBody()) { entity = new HttpEntity<>(headers); } else { - String data = msg.getData(); - if (config.isTrimDoubleQuotes()) { - data = trimDoubleQuotes(data); - } - entity = new HttpEntity<>(data, headers); + entity = new HttpEntity<>(getData(msg), headers); } URI uri = buildEncodedUri(endpointUrl); @@ -247,6 +243,18 @@ public class TbHttpClient { return uri; } + private String getData(TbMsg msg) { + String data = msg.getData(); + + if (config.isTrimDoubleQuotes()) { + final String dataBefore = data; + data = data.replaceAll("^\"|\"$", "");; + log.trace("Trimming double quotes. Before trim: [{}], after trim: [{}]", dataBefore, data); + } + + return data; + } + private TbMsg processResponse(TbContext ctx, TbMsg origMsg, ResponseEntity response) { TbMsgMetaData metaData = origMsg.getMetaData(); metaData.putValue(STATUS, response.getStatusCode().name()); @@ -338,11 +346,4 @@ public class TbHttpClient { } } - private static String trimDoubleQuotes(String s) { - if (s.startsWith("\"") && s.endsWith("\"")) { - return s.substring(1, s.length() - 1); - } - return s; - } - } From ba6a83e57585a72e713115773b6d9a3c0391aefb Mon Sep 17 00:00:00 2001 From: zbeacon Date: Thu, 23 Mar 2023 21:09:42 +0200 Subject: [PATCH 12/30] Added getting users for assignment --- .../server/controller/UserController.java | 45 ++++++++++++++++++- .../server/dao/user/UserService.java | 2 + .../server/dao/sql/user/JpaUserDao.java | 13 ++++++ .../server/dao/sql/user/UserRepository.java | 10 ++++- .../thingsboard/server/dao/user/UserDao.java | 10 +++++ .../server/dao/user/UserServiceImpl.java | 9 ++++ ui-ngx/src/app/core/http/user.service.ts | 7 +++ .../alarm/alarm-assignee-panel.component.ts | 4 +- 8 files changed, 96 insertions(+), 4 deletions(-) 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 e57bb73262..577a25f65d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -42,8 +42,10 @@ import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.UserEmailInfo; +import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; @@ -58,9 +60,9 @@ import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.UserSettings; import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent; +import org.thingsboard.server.common.data.security.model.JwtPair; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.user.TbUserService; -import org.thingsboard.server.common.data.security.model.JwtPair; import org.thingsboard.server.service.query.EntityQueryService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; @@ -70,12 +72,12 @@ import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.system.SystemSecurityService; import javax.servlet.http.HttpServletRequest; - import java.util.Arrays; import java.util.List; import java.util.Map; import static org.thingsboard.server.common.data.query.EntityKeyType.ENTITY_FIELD; +import static org.thingsboard.server.controller.ControllerConstants.ALARM_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.DEFAULT_DASHBOARD; @@ -440,6 +442,45 @@ public class UserController extends BaseController { } } + @ApiOperation(value = "Get usersForAssign (getUsersForAssign)", + notes = "Returns page of user data objects that can be assigned to provided alarmId. " + + "Search is been executed by email, firstName and lastName fields. " + + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/users/assign", params = {"alarmId", "pageSize", "page"}, method = RequestMethod.GET) + @ResponseBody + public PageData getAssignees( + @ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) + @RequestParam String alarmId, + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @ApiParam(value = USER_TEXT_SEARCH_DESCRIPTION) + @RequestParam(required = false) String textSearch, + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = USER_SORT_PROPERTY_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortProperty, + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortOrder) throws ThingsboardException { + try { + checkParameter("alarmId", alarmId); + AlarmId alarmEntityId = new AlarmId(toUUID(alarmId)); + Alarm alarm = checkAlarmId(alarmEntityId, Operation.READ); + SecurityUser currentUser = getCurrentUser(); + TenantId tenantId = currentUser.getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + PageData pageData; + if (Authority.TENANT_ADMIN.equals(currentUser.getAuthority())) { + pageData = userService.findUsersForAssignForTenant(tenantId, alarm.getCustomerId(), pageLink); + } else { + pageData = userService.findCustomerUsers(tenantId, alarm.getCustomerId(), pageLink); + } + return pageData.mapData(user -> new UserEmailInfo(user.getId(), user.getEmail(), user.getFirstName(), user.getLastName())); + } catch (Exception e) { + throw handleException(e); + } + } + @ApiOperation(value = "Save user settings (saveUserSettings)", notes = "Save user settings represented in json format for authorized user. " ) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 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 bb40507772..fc4bf8c8d5 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 @@ -64,6 +64,8 @@ public interface UserService extends EntityDaoService { PageData findCustomerUsers(TenantId tenantId, CustomerId customerId, PageLink pageLink); + PageData findUsersForAssignForTenant(TenantId tenantId, CustomerId customerId, PageLink pageLink); + void deleteCustomerUsers(TenantId tenantId, CustomerId customerId); void setUserCredentialsEnabled(TenantId tenantId, UserId userId, boolean enabled); 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 473c145b5d..b667e1227d 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 @@ -100,6 +100,19 @@ public class JpaUserDao extends JpaAbstractSearchTextDao imple } + @Override + public PageData findUsersForAssignForTenant(UUID tenantId, UUID customerId, PageLink pageLink) { + return DaoUtil.toPageData( + userRepository + .findUsersByCustomerIdAndNullCustomerId( + tenantId, + customerId, + Objects.toString(pageLink.getTextSearch(), ""), + NULL_UUID, + 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/sql/user/UserRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java index 8a7ee7d581..13b3214dd0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java @@ -20,7 +20,6 @@ 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.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.model.sql.UserEntity; @@ -44,6 +43,15 @@ public interface UserRepository extends JpaRepository { @Param("authority") Authority authority, Pageable pageable); + @Query("SELECT u FROM UserEntity u WHERE u.tenantId = :tenantId " + + "AND u.customerId IN (:customerId, :nullCustomerId) " + + "AND LOWER(u.searchText) LIKE LOWER(CONCAT('%', :searchText, '%'))") + Page findUsersByCustomerIdAndNullCustomerId(@Param("tenantId") UUID tenantId, + @Param("customerId") UUID customerId, + @Param("searchText") String searchText, + @Param("nullCustomerId") UUID nullCustomerId, + Pageable pageable); + @Query("SELECT u FROM UserEntity u WHERE u.tenantId = :tenantId " + "AND LOWER(u.searchText) LIKE LOWER(CONCAT('%', :searchText, '%'))") Page findByTenantId(@Param("tenantId") UUID tenantId, 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 c2551c84d3..3bf2138cf5 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 @@ -79,4 +79,14 @@ public interface UserDao extends Dao, TenantEntityDao { */ PageData findCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink); + /** + * Find users for alarm assignment by tenantId, customerId and page link. + * + * @param tenantId the tenantId + * @param customerId the customerId + * @param pageLink the page link + * @return the list of user entities + */ + PageData findUsersForAssignForTenant(UUID tenantId, UUID customerId, 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 b99883c52f..08622bb776 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 @@ -265,6 +265,15 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic return userDao.findCustomerUsers(tenantId.getId(), customerId.getId(), pageLink); } + @Override + public PageData findUsersForAssignForTenant(TenantId tenantId, CustomerId customerId, PageLink pageLink) { + log.trace("Executing findUsersForAssignForTenant, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(customerId, "Incorrect customerId " + customerId); + validatePageLink(pageLink); + return userDao.findUsersForAssignForTenant(tenantId.getId(), customerId.getId(), pageLink); + } + @Override public void deleteCustomerUsers(TenantId tenantId, CustomerId customerId) { log.trace("Executing deleteCustomerUsers, customerId [{}]", customerId); diff --git a/ui-ngx/src/app/core/http/user.service.ts b/ui-ngx/src/app/core/http/user.service.ts index 9a6b70ce29..48ca76a4a0 100644 --- a/ui-ngx/src/app/core/http/user.service.ts +++ b/ui-ngx/src/app/core/http/user.service.ts @@ -51,6 +51,13 @@ export class UserService { defaultHttpOptionsFromConfig(config)); } + public getUsersForAssign(alarmId: string, pageLink: PageLink, + config?: RequestConfig): Observable> { + let url = `/api/users/assign${pageLink.toQuery()}`; + url += `&alarmId=` + alarmId; + return this.http.get>(url, defaultHttpOptionsFromConfig(config)); + } + public getUser(userId: string, config?: RequestConfig): Observable { return this.http.get(`/api/user/${userId}`, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.ts index bd50beef31..121a09452c 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.ts @@ -44,6 +44,8 @@ import { AlarmService } from '@core/http/alarm.service'; import { OverlayRef } from '@angular/cdk/overlay'; import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { UtilsService } from '@core/services/utils.service'; +import {Authority} from "@shared/models/authority.enum"; +import {NULL_UUID} from "@shared/models/id/has-uuid"; export const ALARM_ASSIGNEE_PANEL_DATA = new InjectionToken('AlarmAssigneePanelData'); @@ -144,7 +146,7 @@ export class AlarmAssigneePanelComponent implements OnInit, AfterViewInit, OnDe property: 'email', direction: Direction.ASC }); - return this.userService.findUsersByQuery(pageLink, {ignoreLoading: true}) + return this.userService.getUsersForAssign(this.alarmId, pageLink, {ignoreLoading: true}) .pipe( catchError(() => of(emptyPageData())), map(pageData => { From d533bb14b8320cf8827dac78873032a6bcb73a85 Mon Sep 17 00:00:00 2001 From: zbeacon Date: Thu, 23 Mar 2023 21:11:47 +0200 Subject: [PATCH 13/30] Added ability for customer users operate with other customer users --- .../security/permission/CustomerUserPermissions.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java index 838cd7270b..1a8f87713e 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java @@ -119,6 +119,15 @@ public class CustomerUserPermissions extends AbstractPermissions { if (!Authority.CUSTOMER_USER.equals(userEntity.getAuthority())) { return false; } + + if (!user.getCustomerId().equals(userEntity.getCustomerId())) { + return false; + } + + if (Operation.READ.equals(operation)) { + return true; + } + return user.getId().equals(userId); } From 32b1bf900dd426b6683d7c02602d6b5efa165080 Mon Sep 17 00:00:00 2001 From: zbeacon Date: Fri, 24 Mar 2023 10:39:58 +0200 Subject: [PATCH 14/30] Refactoring and added method to get users for assignment to rest client --- .../server/controller/UserController.java | 10 +++++----- .../org/thingsboard/rest/client/RestClient.java | 14 ++++++++++++++ ui-ngx/src/app/core/http/user.service.ts | 5 ++--- .../alarm/alarm-assignee-panel.component.ts | 2 -- 4 files changed, 21 insertions(+), 10 deletions(-) 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 577a25f65d..dc35ce9b46 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -447,11 +447,11 @@ public class UserController extends BaseController { "Search is been executed by email, firstName and lastName fields. " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/users/assign", params = {"alarmId", "pageSize", "page"}, method = RequestMethod.GET) + @RequestMapping(value = "/users/assign/{alarmId}", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody - public PageData getAssignees( + public PageData getUsersForAssign( @ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) - @RequestParam String alarmId, + @PathVariable("alarmId") String strAlarmId, @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) @@ -463,8 +463,8 @@ public class UserController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { try { - checkParameter("alarmId", alarmId); - AlarmId alarmEntityId = new AlarmId(toUUID(alarmId)); + checkParameter("alarmId", strAlarmId); + AlarmId alarmEntityId = new AlarmId(toUUID(strAlarmId)); Alarm alarm = checkAlarmId(alarmEntityId, Operation.READ); SecurityUser currentUser = getCurrentUser(); TenantId tenantId = currentUser.getTenantId(); diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index c2c0a6d794..61bd7fb72b 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -65,6 +65,7 @@ import org.thingsboard.server.common.data.TenantInfo; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.UpdateMessage; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.UserEmailInfo; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; @@ -2565,6 +2566,19 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { }, params).getBody(); } + public PageData getUsersForAssign(AlarmId alarmId, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("alarmId", alarmId.getId().toString()); + addPageLinkToParam(params, pageLink); + + return restTemplate.exchange( + baseURL + "/users/assign/{alarmId}" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + public void setUserCredentialsEnabled(UserId userId, boolean userCredentialsEnabled) { restTemplate.postForLocation( baseURL + "/api/user/{userId}/userCredentialsEnabled?userCredentialsEnabled={userCredentialsEnabled}", diff --git a/ui-ngx/src/app/core/http/user.service.ts b/ui-ngx/src/app/core/http/user.service.ts index 48ca76a4a0..39a5a20e6e 100644 --- a/ui-ngx/src/app/core/http/user.service.ts +++ b/ui-ngx/src/app/core/http/user.service.ts @@ -53,9 +53,8 @@ export class UserService { public getUsersForAssign(alarmId: string, pageLink: PageLink, config?: RequestConfig): Observable> { - let url = `/api/users/assign${pageLink.toQuery()}`; - url += `&alarmId=` + alarmId; - return this.http.get>(url, defaultHttpOptionsFromConfig(config)); + return this.http.get>(`/api/users/assign/${alarmId}${pageLink.toQuery()}`, + defaultHttpOptionsFromConfig(config)); } public getUser(userId: string, config?: RequestConfig): Observable { diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.ts index 121a09452c..82c7515ed9 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.ts @@ -44,8 +44,6 @@ import { AlarmService } from '@core/http/alarm.service'; import { OverlayRef } from '@angular/cdk/overlay'; import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { UtilsService } from '@core/services/utils.service'; -import {Authority} from "@shared/models/authority.enum"; -import {NULL_UUID} from "@shared/models/id/has-uuid"; export const ALARM_ASSIGNEE_PANEL_DATA = new InjectionToken('AlarmAssigneePanelData'); From 01996334e2e8f262a3a3a13b5c1b13ec8ed964ff Mon Sep 17 00:00:00 2001 From: imbeacon Date: Mon, 27 Mar 2023 13:52:46 +0300 Subject: [PATCH 15/30] Refactoring, added tests --- .../server/controller/UserController.java | 8 +- .../server/controller/AbstractWebTest.java | 9 ++ .../controller/BaseUserControllerTest.java | 99 ++++++++++++++++++- .../server/dao/user/UserService.java | 2 +- .../server/dao/sql/user/JpaUserDao.java | 4 +- .../server/dao/sql/user/UserRepository.java | 12 +-- .../thingsboard/server/dao/user/UserDao.java | 2 +- .../server/dao/user/UserServiceImpl.java | 8 +- 8 files changed, 127 insertions(+), 17 deletions(-) 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 c3c09d3d63..27af6f35da 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -448,7 +448,7 @@ public class UserController extends BaseController { @RequestMapping(value = "/users/assign/{alarmId}", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody public PageData getUsersForAssign( - @ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) + @ApiParam(value = ALARM_ID_PARAM_DESCRIPTION, required = true) @PathVariable("alarmId") String strAlarmId, @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -469,7 +469,11 @@ public class UserController extends BaseController { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); PageData pageData; if (Authority.TENANT_ADMIN.equals(currentUser.getAuthority())) { - pageData = userService.findUsersForAssignForTenant(tenantId, alarm.getCustomerId(), pageLink); + if (alarm.getCustomerId() == null) { + pageData = userService.findTenantAdmins(tenantId, pageLink); + } else { + pageData = userService.findTenantAndCustomerUsers(tenantId, alarm.getCustomerId(), pageLink); + } } else { pageData = userService.findCustomerUsers(tenantId, alarm.getCustomerId(), pageLink); } 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 b93e74c959..9bd6f56d17 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -798,6 +798,15 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { } + public class EntityIdComparator implements Comparator { + + @Override + public int compare(D o1, D o2) { + return o1.getId().compareTo(o2.getId()); + } + + } + protected static ResultMatcher statusReason(Matcher matcher) { return jsonPath("$.message", matcher); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java index 7149f52630..f81acf7f37 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java @@ -31,10 +31,13 @@ import org.springframework.http.HttpHeaders; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.web.servlet.ResultActions; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.UserEmailInfo; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -53,7 +56,6 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -66,6 +68,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { private IdComparator idComparator = new IdComparator<>(); private IdComparator userDataIdComparator = new IdComparator<>(); + private EntityIdComparator userIdComparator = new EntityIdComparator<>(); + private CustomerId customerNUULId = (CustomerId) createEntityId_NULL_UUID(new Customer()); @Autowired @@ -647,6 +651,89 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } + @Test + public void testGetUsersForAssign() throws Exception { + loginTenantAdmin(); + + String email = "testEmail1"; + List expectedCustomerUserIds = new ArrayList<>(); + expectedCustomerUserIds.add(customerUserId); + for (int i = 0; i < 45; i++) { + User customerUser = createCustomerUser( customerId); + customerUser.setEmail(email + StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10)) + "@thingsboard.org"); + User user = doPost("/api/user", customerUser, User.class); + expectedCustomerUserIds.add(user.getId()); + } + List expectedTenantUserIds = new ArrayList<>(List.copyOf(expectedCustomerUserIds)); + expectedTenantUserIds.add(tenantAdminUserId); + + Device device = new Device(); + device.setName("testDevice"); + Device savedDevice = doPost("/api/device", device, Device.class); + + Alarm alarm = createTestAlarm(savedDevice); + + List loadedTenantUserIds = new ArrayList<>(); + PageLink pageLink = new PageLink(33, 0); + PageData pageData; + do { + pageData = doGetTypedWithPageLink("/api/users/assign/" + alarm.getId().getId().toString() + "?", + new TypeReference<>() {}, pageLink); + loadedTenantUserIds.addAll(pageData.getData().stream().map(UserEmailInfo::getId) + .collect(Collectors.toList())); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); + + Assert.assertEquals(1, loadedTenantUserIds.size()); + Assert.assertEquals(tenantAdminUserId, loadedTenantUserIds.get(0)); + + doDelete("/api/alarm/" + alarm.getId().getId().toString()); + + savedDevice.setCustomerId(customerId); + savedDevice = doPost("/api/customer/" + customerId.getId() + + "/device/" + savedDevice.getId().getId(), Device.class); + + alarm = createTestAlarm(savedDevice); + + List loadedUserIds = new ArrayList<>(); + pageLink = new PageLink(16, 0); + do { + pageData = doGetTypedWithPageLink("/api/users/assign/" + alarm.getId().getId().toString() + "?", + new TypeReference<>() {}, pageLink); + loadedUserIds.addAll(pageData.getData().stream().map(UserEmailInfo::getId) + .collect(Collectors.toList())); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); + + expectedTenantUserIds.sort(userIdComparator); + loadedUserIds.sort(userIdComparator); + + Assert.assertEquals(expectedTenantUserIds, loadedUserIds); + + loginCustomerUser(); + + loadedUserIds = new ArrayList<>(); + pageLink = new PageLink(16, 0); + do { + pageData = doGetTypedWithPageLink("/api/users/assign/" + alarm.getId().getId().toString() + "?", + new TypeReference<>() {}, pageLink); + loadedUserIds.addAll(pageData.getData().stream().map(UserEmailInfo::getId) + .collect(Collectors.toList())); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); + + expectedCustomerUserIds.sort(userIdComparator); + loadedUserIds.sort(userIdComparator); + + Assert.assertEquals(expectedCustomerUserIds, loadedUserIds); + } + @Test public void testDeleteUserWithDeleteRelationsOk() throws Exception { loginSysAdmin(); @@ -984,4 +1071,14 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { return loadedCustomerUsers; } + private Alarm createTestAlarm(Device device) { + Alarm alarm = new Alarm(); + alarm.setOriginator(device.getId()); + alarm.setCustomerId(device.getCustomerId()); + alarm.setSeverity(AlarmSeverity.MAJOR); + alarm.setType("testAlarm"); + alarm.setStartTs(System.currentTimeMillis()); + return doPost("/api/alarm", alarm, Alarm.class); + } + } 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 ba5f1722d2..7b11f1c6ff 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 @@ -77,7 +77,7 @@ public interface UserService extends EntityDaoService { PageData findCustomerUsers(TenantId tenantId, CustomerId customerId, PageLink pageLink); - PageData findUsersForAssignForTenant(TenantId tenantId, CustomerId customerId, PageLink pageLink); + PageData findTenantAndCustomerUsers(TenantId tenantId, CustomerId customerId, PageLink pageLink); void deleteCustomerUsers(TenantId tenantId, CustomerId customerId); 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 8c9f41eef6..2bfab53b64 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 @@ -103,10 +103,10 @@ public class JpaUserDao extends JpaAbstractSearchTextDao imple } @Override - public PageData findUsersForAssignForTenant(UUID tenantId, UUID customerId, PageLink pageLink) { + public PageData findTenantAndCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink) { return DaoUtil.toPageData( userRepository - .findUsersByCustomerIdAndNullCustomerId( + .findTenantAndCustomerUsers( tenantId, customerId, Objects.toString(pageLink.getTextSearch(), ""), diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java index bdb9e829fa..9cd7029146 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java @@ -45,13 +45,13 @@ public interface UserRepository extends JpaRepository { Pageable pageable); @Query("SELECT u FROM UserEntity u WHERE u.tenantId = :tenantId " + - "AND u.customerId IN (:customerId, :nullCustomerId) " + + "AND (:customerId IS NULL OR u.customerId IN (:customerId, :nullCustomerId)) " + "AND LOWER(u.searchText) LIKE LOWER(CONCAT('%', :searchText, '%'))") - Page findUsersByCustomerIdAndNullCustomerId(@Param("tenantId") UUID tenantId, - @Param("customerId") UUID customerId, - @Param("searchText") String searchText, - @Param("nullCustomerId") UUID nullCustomerId, - Pageable pageable); + Page findTenantAndCustomerUsers(@Param("tenantId") UUID tenantId, + @Param("customerId") UUID customerId, + @Param("searchText") String searchText, + @Param("nullCustomerId") UUID nullCustomerId, + Pageable pageable); @Query("SELECT u FROM UserEntity u WHERE u.tenantId = :tenantId " + "AND LOWER(u.searchText) LIKE LOWER(CONCAT('%', :searchText, '%'))") 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 c20439b24d..2dee06db87 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 @@ -90,7 +90,7 @@ public interface UserDao extends Dao, TenantEntityDao { * @param pageLink the page link * @return the list of user entities */ - PageData findUsersForAssignForTenant(UUID tenantId, UUID customerId, PageLink pageLink); + PageData findTenantAndCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink); PageData findAll(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 b273beb516..222298d124 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 @@ -294,12 +294,12 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic } @Override - public PageData findUsersForAssignForTenant(TenantId tenantId, CustomerId customerId, PageLink pageLink) { - log.trace("Executing findUsersForAssignForTenant, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); + public PageData findTenantAndCustomerUsers(TenantId tenantId, CustomerId customerId, PageLink pageLink) { + log.trace("Executing findTenantAndCustomerUsers, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, "Incorrect customerId " + customerId); validatePageLink(pageLink); - return userDao.findUsersForAssignForTenant(tenantId.getId(), customerId.getId(), pageLink); + validateId(customerId, "Incorrect customerId " + customerId); + return userDao.findTenantAndCustomerUsers(tenantId.getId(), customerId.getId(), pageLink); } @Override From 92c2d4f021bde0ccf88a190fb7db0f6da6461e6f Mon Sep 17 00:00:00 2001 From: rusikv Date: Wed, 29 Mar 2023 12:15:46 +0300 Subject: [PATCH 16/30] Alarm details redesign --- .../system/widget_bundles/alarm_widgets.json | 2 +- .../alarm/alarm-assignee-panel.component.html | 3 +- .../alarm/alarm-assignee-panel.component.scss | 2 - .../alarm/alarm-assignee.component.html | 30 +-- .../alarm/alarm-assignee.component.scss | 55 ++-- .../alarm/alarm-assignee.component.ts | 92 ++++--- .../alarm/alarm-comment-dialog.component.html | 17 +- .../alarm/alarm-comment-dialog.component.ts | 4 - .../alarm/alarm-comment.component.html | 241 +++++++++--------- .../alarm/alarm-comment.component.scss | 150 +++++++++-- .../alarm/alarm-comment.component.ts | 25 +- .../alarm/alarm-details-dialog.component.html | 68 ++--- .../alarm/alarm-details-dialog.component.ts | 40 ++- .../components/alarm/alarm-table-config.ts | 5 +- .../alarm/alarm-table.component.scss | 66 ++--- .../lib/alarms-table-widget.component.ts | 26 +- ...larms-table-widget-settings.component.html | 4 +- .../alarms-table-widget-settings.component.ts | 2 +- .../assets/locale/locale.constant-en_US.json | 13 +- 19 files changed, 464 insertions(+), 381 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/alarm_widgets.json b/application/src/main/data/json/system/widget_bundles/alarm_widgets.json index e7a97b36f8..16a155cebb 100644 --- a/application/src/main/data/json/system/widget_bundles/alarm_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/alarm_widgets.json @@ -23,7 +23,7 @@ "dataKeySettingsSchema": "", "settingsDirective": "tb-alarms-table-widget-settings", "dataKeySettingsDirective": "tb-alarms-table-key-settings", - "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"allowAssign\":true,\"displayComments\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStickyAction\":false,\"enableFilter\":true},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418},{\"name\":\"assignee\",\"type\":\"alarm\",\"label\":\"Assignee\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.5008441077416634}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{},\"alarmStatusList\":[],\"alarmSeverityList\":[],\"alarmTypeList\":[],\"searchPropagatedAlarms\":false}" + "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"allowAssign\":true,\"displayActivity\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStickyAction\":false,\"enableFilter\":true},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418},{\"name\":\"assignee\",\"type\":\"alarm\",\"label\":\"Assignee\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.5008441077416634}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{},\"alarmStatusList\":[],\"alarmSeverityList\":[],\"alarmTypeList\":[],\"searchPropagatedAlarms\":false}" } } ] diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html index f6ba02e7a0..f01c550444 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html @@ -25,8 +25,7 @@ + (optionSelected)="selected($event)"> account_circle alarm.unassigned diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.scss b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.scss index a10848cc70..cb9ae63dc2 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.scss +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.scss @@ -37,7 +37,6 @@ .tb-assignee-autocomplete { &.tb-assignee-autocomplete.mat-mdc-autocomplete-panel { position: relative; - left: -8px; margin-top: 8px; box-shadow: none !important; } @@ -58,7 +57,6 @@ align-items: center; margin-right: 8px; border-radius: 50%; - background-color: #5cb445; width: 28px; height: 28px; min-width: 28px; diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.html b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.html index 52066ff3ac..e0bec8088d 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.html @@ -16,28 +16,14 @@ --> -
- - +
+ + alarm.assignee + + {{ getUserInitials(alarm.assignee) }} - - {{ getUserDisplayName(alarm.assignee) }} - - - - account_circle - alarm.unassigned - - + account_circle + arrow_drop_down +
diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss index 935ac2f32d..82f10650cf 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss @@ -14,36 +14,29 @@ * limitations under the License. */ -:host { - .tb-assignee { - cursor: pointer; - max-width: 273px; - - .assigned-container { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; +.user-avatar { + display: inline-flex; + justify-content: center; + align-items: center; + border-radius: 50%; + width: 28px; + height: 28px; + min-width: 28px; + min-height: 28px; + color: white; + font-size: 13px; + font-weight: 700; + margin-left: 12px; + margin-right: 20px; +} - .user-avatar { - display: inline-flex; - justify-content: center; - align-items: center; - border-radius: 50%; - width: 28px; - height: 28px; - min-width: 28px; - min-height: 28px; - color: white; - font-size: 13px; - font-weight: 700; - } - } - .material-icons.unassigned-icon { - width: 28px; - height: 28px; - font-size: 28px; - color: rgba(0, 0, 0, 0.38); - overflow: visible; - } - } +.unassigned-icon { + width: 28px; + height: 28px; + font-size: 28px; + color: rgba(0, 0, 0, 0.38); + overflow: visible; + margin-left: 12px; + margin-right: 20px; + padding: 0; } diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.ts index 5d7f29d55b..a66ff6e070 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.ts @@ -14,17 +14,17 @@ /// limitations under the License. /// -import { - Component, EventEmitter, Injector, Input, Output, StaticProvider, ViewContainerRef -} from '@angular/core'; +import { Component, EventEmitter, Injector, Input, Output, StaticProvider, ViewContainerRef } from '@angular/core'; import { UtilsService } from '@core/services/utils.service'; import { AlarmAssignee, AlarmInfo } from '@shared/models/alarm.models'; import { - ALARM_ASSIGNEE_PANEL_DATA, AlarmAssigneePanelComponent, + ALARM_ASSIGNEE_PANEL_DATA, + AlarmAssigneePanelComponent, AlarmAssigneePanelData } from '@home/components/alarm/alarm-assignee-panel.component'; import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; import { ComponentPortal } from '@angular/cdk/portal'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'tb-alarm-assignee', @@ -35,12 +35,26 @@ export class AlarmAssigneeComponent { @Input() alarm: AlarmInfo; + @Input() + allowAssign: boolean; + @Output() alarmReassigned = new EventEmitter(); constructor(private utilsService: UtilsService, private overlay: Overlay, - private viewContainerRef: ViewContainerRef) { + private viewContainerRef: ViewContainerRef, + private translateService: TranslateService) { + } + + getAssignee() { + if (this.alarm) { + if (this.alarm.assignee) { + return this.getUserDisplayName(this.alarm.assignee); + } else { + return this.translateService.instant('alarm.unassigned'); + } + } } getUserDisplayName(entity: AlarmAssignee) { @@ -103,39 +117,41 @@ export class AlarmAssigneeComponent { if ($event) { $event.stopPropagation(); } - const target = $event.target || $event.srcElement || $event.currentTarget; - const config = new OverlayConfig(); - config.backdropClass = 'cdk-overlay-transparent-backdrop'; - config.hasBackdrop = true; - const connectedPosition: ConnectedPosition = { - originX: 'end', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - }; - config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) - .withPositions([connectedPosition]); - config.minWidth = '260px'; - const overlayRef = this.overlay.create(config); - overlayRef.backdropClick().subscribe(() => { - overlayRef.dispose(); - }); - const providers: StaticProvider[] = [ - { - provide: ALARM_ASSIGNEE_PANEL_DATA, - useValue: { - alarmId: alarm.id.id, - assigneeId: alarm.assigneeId?.id - } as AlarmAssigneePanelData - }, - { - provide: OverlayRef, - useValue: overlayRef - } - ]; - const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); - overlayRef.attach(new ComponentPortal(AlarmAssigneePanelComponent, - this.viewContainerRef, injector)).onDestroy(() => this.alarmReassigned.emit(true)); + if (this.allowAssign) { + const target = $event.currentTarget; + const config = new OverlayConfig(); + config.backdropClass = 'cdk-overlay-transparent-backdrop'; + config.hasBackdrop = true; + const connectedPosition: ConnectedPosition = { + originX: 'center', + originY: 'center', + overlayX: 'center', + overlayY: 'top' + }; + config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) + .withPositions([connectedPosition]); + config.width = (target as HTMLElement).offsetWidth; + const overlayRef = this.overlay.create(config); + overlayRef.backdropClick().subscribe(() => { + overlayRef.dispose(); + }); + const providers: StaticProvider[] = [ + { + provide: ALARM_ASSIGNEE_PANEL_DATA, + useValue: { + alarmId: alarm.id.id, + assigneeId: alarm.assigneeId?.id + } as AlarmAssigneePanelData + }, + { + provide: OverlayRef, + useValue: overlayRef + } + ]; + const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); + overlayRef.attach(new ComponentPortal(AlarmAssigneePanelComponent, + this.viewContainerRef, injector)).onDestroy(() => this.alarmReassigned.emit(true)); + } } } diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment-dialog.component.html b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment-dialog.component.html index 94f8f47eb1..d7e3366a35 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment-dialog.component.html @@ -17,11 +17,8 @@ -->
-

{{ 'alarm.comments' | translate }}

-
@@ -29,16 +26,8 @@
- +
-
- -
diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment-dialog.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment-dialog.component.ts index 026ebae8c0..0aa568bdd0 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment-dialog.component.ts @@ -25,7 +25,6 @@ import { AlarmInfo } from '@shared/models/alarm.models'; export interface AlarmCommentDialogData { alarmId?: string; alarm?: AlarmInfo; - commentsHeaderEnabled: boolean; } @Component({ @@ -37,14 +36,11 @@ export class AlarmCommentDialogComponent extends DialogComponent, protected router: Router, @Inject(MAT_DIALOG_DATA) public data: AlarmCommentDialogData, public dialogRef: MatDialogRef) { super(store, router, dialogRef); - this.commentsHeaderEnabled = this.data.commentsHeaderEnabled this.alarmId = this.data.alarmId; } diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.html b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.html index 91b4871d86..71f110e7d1 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.html @@ -17,129 +17,141 @@ -->
-
- - {{ 'alarm-comment.comments' | translate }} - -
- - +
+
+ + alarm-activity.activity + +
+ + +
-
- +
+ -
-
- - {{ displayDataElement.commentText }} - - - {{ displayDataElement.createdDateAgo }} - -
- -
-
- {{ getUserInitials(displayDataElement.displayName) }} -
-
-
- {{ displayDataElement.displayName }} - - edited {{ displayDataElement.editedDateAgo }} - - - {{ displayDataElement.createdDateAgo }} - -
- {{ displayDataElement.commentText }} -
-
- - -
+
+
+
+ + {{ displayDataElement.commentText }} + + + {{ displayDataElement.createdDateAgo }} +
- -
-
+
+
{{ getUserInitials(displayDataElement.displayName) }}
- - -
- - +
+
+ {{ displayDataElement.displayName }} + + {{ displayDataElement.createdDateAgo }} + + + Edited +
- + {{ displayDataElement.commentText }} +
+
+ + +
+ +
+
+ {{ getUserInitials(displayDataElement.displayName) }} +
+ + +
+ + +
+
+
+
- +
-
-
- {{ getUserInitials(userDisplayName) }} -
- +
+
+
+ {{ getUserInitials(userDisplayName) }} +
+ - - + + +
diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.scss b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.scss index 3cd97767a2..7749e82ec3 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.scss +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.scss @@ -13,69 +13,175 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@use '@angular/material' as mat; +@import '../theme.scss'; + +$main-color: rgba(mat.get-color-from-palette($tb-primary, 50), 0.4); +$border: 1px solid mat.get-color-from-palette($tb-primary); + :host { .tb-alarm-comments { - padding: 16px 24px 24px 24px; - background-color: #fafafa; + background-color: $main-color; max-width: 600px; + width: 100%; - &-header { - background-color: #fafafa; + .comment-input { position: sticky; - top: -25px; z-index: 1; - margin-bottom: 10px; - &-title { - color: rgba(0, 0, 0, 0.76); - letter-spacing: 0.25px; - font-weight: 500; + .inner-wrap { + border: $border; + padding: 0 24px; + background-color: $main-color; } - .mat-icon { - color: rgba(0, 0, 0, 0.38); + &.oldest-first { + bottom: -24px; + box-shadow: 0px -4px 12px rgba(0, 0, 0, 0.04); + + .inner-wrap { + border-radius: 0 0 8px 8px; + } + + &.activity-only { + bottom: -1px; + + .inner-wrap { + border: none + } + } + } + + &.newest-first { + top: 24px; + box-shadow: 0 8px 10px rgba(23, 33, 90, 0.08); + + .inner-wrap { + border-top: none; + } + + &.activity-only { + top: 48px; + box-shadow: 0 8px 10px rgba(23, 33, 90, 0.08); + + .inner-wrap { + border: none; + } + } } } - &-user-avatar { + .header { + z-index: 1; + position: sticky; + top: -24px; + background-color: white; + + &.activity-only { + top: 0; + } + + &-container { + padding: 0 24px; + background-color: $main-color; + border: $border; + border-bottom: none; + border-radius: 8px 8px 0px 0px; + + &.activity-only { + border: none; + } + + &.asc { + border-bottom: $border; + box-shadow: 0px 4px 10px rgba(23, 33, 90, 0.08); + + &.activity-only { + border: none; + } + } + + .header-title { + color: rgba(0, 0, 0, 0.76); + letter-spacing: 0.25px; + font-weight: 500; + } + + .mat-icon { + color: rgba(0, 0, 0, 0.38); + } + } + } + + .comments-container { + border: $border; + border-top: none; + border-radius: 0px 0px 8px 8px; + + &.activity-only { + border: none; + } + + &.asc { + border-bottom: none; + border-radius: unset; + } + } + + .user-comment { + padding: 4px; + border-radius: 8px; + &:hover { + background-color: $main-color; + } + } + + .user-avatar { width: 28px; min-width: 28px; height: 28px; min-height: 28px; border-radius: 50%; font-weight: 700; - color: #FFFFFF; + color: white; font-size: 13px; } - &-user-name { + .user-name { font-size: 16px; color: rgba(0, 0, 0, 0.76); font-weight: 500; letter-spacing: 0.25px; } - &-time { + .time { font-size: 14px; font-weight: 400; color: rgba(0, 0, 0, 0.38); - letter-spacing: 0.2px + letter-spacing: 0.2px; + border-radius: 16px; + padding: 0 3px; + &:hover { + background: rgba(0, 0, 0, 0.04); + } } - &-system-text { - color: rgba(0, 0, 0, 0.38); + .system-text { font-weight: 500; + font-size: 14px; letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.38); } - &-text { + .text { white-space: pre-line; word-break: break-word; - color: rgba(0, 0, 0, 0.54); + font-size: 16px; letter-spacing: 0.15px; + color: rgba(0, 0, 0, 0.54); } - &-action-buttons { + .action-buttons { visibility: hidden; .mat-icon { color: rgba(0, 0, 0, 0.38); diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts index 6e32979a14..700f83a4e9 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts @@ -34,9 +34,11 @@ import { EntityType } from '@shared/models/entity-type.models'; interface AlarmCommentsDisplayData { commentId?: string, displayName?: string, + createdTime: number, createdDateAgo?: string, edit?: boolean, isEdited?: boolean, + editedTime?: number; editedDateAgo?: string, showActions?: boolean, commentText?: string, @@ -54,7 +56,7 @@ export class AlarmCommentComponent implements OnInit { alarmId: string; @Input() - commentsHeaderEnabled: boolean = true; + alarmActivityOnly: boolean = false; authUser: AuthUser; @@ -110,7 +112,8 @@ export class AlarmCommentComponent implements OnInit { this.alarmComments = pagedData.data; this.displayData.length = 0; for (let alarmComment of pagedData.data) { - let displayDataElement: AlarmCommentsDisplayData = {}; + let displayDataElement = {} as AlarmCommentsDisplayData; + displayDataElement.createdTime = alarmComment.createdTime; displayDataElement.createdDateAgo = this.dateAgoPipe.transform(alarmComment.createdTime); displayDataElement.commentText = alarmComment.comment.text; displayDataElement.isSystemComment = alarmComment.type === AlarmCommentType.SYSTEM; @@ -119,7 +122,9 @@ export class AlarmCommentComponent implements OnInit { displayDataElement.displayName = this.getUserDisplayName(alarmComment); displayDataElement.edit = false; displayDataElement.isEdited = alarmComment.comment.edited; - displayDataElement.editedDateAgo = this.dateAgoPipe.transform(alarmComment.comment.editedOn).toLowerCase(); + displayDataElement.editedTime = alarmComment.comment.editedOn; + displayDataElement.editedDateAgo = this.dateAgoPipe.transform(alarmComment.comment.editedOn) + + ' ' + this.translate.instant('alarm-activity.ago') + '\n'; displayDataElement.showActions = false; displayDataElement.isSystemComment = false; displayDataElement.avatarBgColor = this.utilsService.stringToHslColor(displayDataElement.displayName, @@ -194,7 +199,7 @@ export class AlarmCommentComponent implements OnInit { const alarmCommentInfo: AlarmComment = this.getAlarmCommentById(commentId); const commentText: string = alarmCommentInfo.comment.text; this.dialogService.confirm( - this.translate.instant('alarm-comment.delete-alarm-comment'), + this.translate.instant('alarm-activity.delete-alarm-comment'), commentText, this.translate.instant('action.cancel'), this.translate.instant('action.delete')).subscribe( @@ -211,15 +216,17 @@ export class AlarmCommentComponent implements OnInit { } getSortDirectionIcon() { - return this.alarmCommentSortOrder.direction === Direction.DESC ? 'arrow_downward' : 'arrow_upward' + return this.alarmCommentSortOrder.direction === Direction.DESC ? 'mdi:sort-descending' : 'mdi:sort-ascending' } - isDirectionAscending() { - return this.alarmCommentSortOrder.direction === Direction.ASC; + getSortDirectionTooltipText() { + let text = this.alarmCommentSortOrder.direction === Direction.DESC ? 'alarm-activity.newest-first' : + 'alarm-activity.oldest-first'; + return this.translate.instant(text); } - isDirectionDescending() { - return this.alarmCommentSortOrder.direction === Direction.DESC; + isDirectionAscending() { + return this.alarmCommentSortOrder.direction === Direction.ASC; } onCommentMouseEnter(commentId: string, displayDataIndex: number): void { diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.html b/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.html index e2af9dcf24..213b227442 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.html @@ -28,90 +28,64 @@
-
-
+
+
alarm.originator - alarm.created-time - + alarm.severity +
- - alarm.type - + + alarm.start-time + - - alarm.severity - + + alarm.duration +
+ + alarm.type + + alarm.status - -
- alarm.advanced-info + alarm.show-more -
- - alarm.start-time - - - - alarm.end-time - - - -
-
- - alarm.ack-time - - - - alarm.clear-time - - - -
+ label="{{ 'alarm.additional-info' | translate }}">
+ +
-
- + - + alarm.show-more diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts index dbae21ca5d..9b67da4b71 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts @@ -53,7 +53,8 @@ import { Authority } from '@shared/models/authority.enum'; import { ChangeDetectorRef, Injector, StaticProvider, ViewContainerRef } from '@angular/core'; import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; import { - ALARM_ASSIGNEE_PANEL_DATA, AlarmAssigneePanelComponent, + ALARM_ASSIGNEE_PANEL_DATA, + AlarmAssigneePanelComponent, AlarmAssigneePanelData } from '@home/components/alarm/alarm-assignee-panel.component'; import { ComponentPortal } from '@angular/cdk/portal'; @@ -282,8 +283,13 @@ export class AlarmTableConfig extends EntityTableConfig } ]; const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); - overlayRef.attach(new ComponentPortal(AlarmAssigneePanelComponent, - this.viewContainerRef, injector)).onDestroy(() => this.updateData()); + const componentRef = overlayRef.attach(new ComponentPortal(AlarmAssigneePanelComponent, + this.viewContainerRef, injector)); + componentRef.onDestroy(() => { + if (componentRef.instance.reassigned) { + this.updateData() + } + }); } }