Browse Source

Merge branch 'master' into task/4114-gateway-version-compatibility

pull/11516/head
Max Petrov 2 years ago
committed by GitHub
parent
commit
5b8ea8fc0d
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      application/src/test/java/org/thingsboard/server/controller/DomainControllerTest.java
  2. 4
      application/src/test/java/org/thingsboard/server/controller/MobileAppControllerTest.java
  3. 148
      application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/MqttClientSideRpcIntegrationTest.java
  4. 5
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java
  5. 49
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
  6. 25
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/limits/GatewaySessionLimits.java
  7. 29
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/limits/SessionLimits.java
  8. 1
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java
  9. 5
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java
  10. 7
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java
  11. 7
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
  12. 2
      dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java
  13. 2
      dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppServiceImpl.java
  14. 4
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java
  15. 12
      dao/src/test/java/org/thingsboard/server/dao/service/AssetProfileServiceTest.java
  16. 12
      dao/src/test/java/org/thingsboard/server/dao/service/DeviceProfileServiceTest.java
  17. 5
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java
  18. 2
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html
  19. 2
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts
  20. 1
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.models.ts
  21. 1
      ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss
  22. 6
      ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts
  23. 4
      ui-ngx/src/app/modules/home/models/widget-component.models.ts
  24. 5
      ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/clients-table-config.resolver.ts

4
application/src/test/java/org/thingsboard/server/controller/DomainControllerTest.java

@ -107,8 +107,8 @@ public class DomainControllerTest extends AbstractControllerTest {
doPut("/api/domain/" + savedDomain.getId() + "/oauth2Clients", List.of(savedOAuth2Client.getId().getId(), savedOAuth2Client2.getId().getId()));
DomainInfo retrievedDomainInfo = doGet("/api/domain/info/{id}", DomainInfo.class, savedDomain.getId().getId());
assertThat(retrievedDomainInfo).isEqualTo(new DomainInfo(savedDomain, List.of(new OAuth2ClientInfo(savedOAuth2Client),
new OAuth2ClientInfo(savedOAuth2Client2))));
assertThat(retrievedDomainInfo).isEqualTo(new DomainInfo(savedDomain, List.of(new OAuth2ClientInfo(savedOAuth2Client2),
new OAuth2ClientInfo(savedOAuth2Client))));
doPut("/api/domain/" + savedDomain.getId() + "/oauth2Clients", List.of(savedOAuth2Client2.getId().getId()));
DomainInfo retrievedDomainInfo2 = doGet("/api/domain/info/{id}", DomainInfo.class, savedDomain.getId().getId());

4
application/src/test/java/org/thingsboard/server/controller/MobileAppControllerTest.java

@ -109,8 +109,8 @@ public class MobileAppControllerTest extends AbstractControllerTest {
doPut("/api/mobileApp/" + savedMobileApp.getId() + "/oauth2Clients", List.of(savedOAuth2Client.getId().getId(), savedOAuth2Client2.getId().getId()));
MobileAppInfo retrievedMobileAppInfo = doGet("/api/mobileApp/info/{id}", MobileAppInfo.class, savedMobileApp.getId().getId());
assertThat(retrievedMobileAppInfo).isEqualTo(new MobileAppInfo(savedMobileApp, List.of(new OAuth2ClientInfo(savedOAuth2Client),
new OAuth2ClientInfo(savedOAuth2Client2))));
assertThat(retrievedMobileAppInfo).isEqualTo(new MobileAppInfo(savedMobileApp, List.of(new OAuth2ClientInfo(savedOAuth2Client2),
new OAuth2ClientInfo(savedOAuth2Client))));
doPut("/api/mobileApp/" + savedMobileApp.getId() + "/oauth2Clients", List.of(savedOAuth2Client2.getId().getId()));
MobileAppInfo retrievedMobileAppInfo2 = doGet("/api/mobileApp/info/{id}", MobileAppInfo.class, savedMobileApp.getId().getId());

148
application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/MqttClientSideRpcIntegrationTest.java

@ -0,0 +1,148 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.mqtt.mqttv3.rpc;
import io.netty.handler.codec.mqtt.MqttQoS;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Value;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import org.thingsboard.server.transport.mqtt.limits.GatewaySessionLimits;
import org.thingsboard.server.transport.mqtt.limits.SessionLimits;
import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestCallback;
import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestClient;
import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestSubscribeOnTopicCallback;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_REQUESTS_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_RESPONSE_SUB_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_RESPONSE_TOPIC;
@DaoSqlTest
public class MqttClientSideRpcIntegrationTest extends AbstractMqttIntegrationTest {
@Value("${transport.mqtt.netty.max_payload_size}")
private Integer maxPayloadSize;
@Value("${transport.mqtt.msg_queue_size_per_device_limit}")
private int maxInflightMessages;
@Test
public void getSessionLimitsRpcForDeviceTest() throws Exception {
loginSysAdmin();
TenantProfile tenantProfile = doGet("/api/tenantProfile/" + tenantProfileId, TenantProfile.class);
DefaultTenantProfileConfiguration profileConfiguration = tenantProfile.getDefaultProfileConfiguration();
profileConfiguration.setTransportDeviceMsgRateLimit("20:600");
profileConfiguration.setTransportDeviceTelemetryMsgRateLimit("10:600");
profileConfiguration.setTransportDeviceTelemetryDataPointsRateLimit("40:600");
doPost("/api/tenantProfile", tenantProfile);
var expectedLimits = new SessionLimits();
var deviceLimits = new SessionLimits.SessionRateLimits(profileConfiguration.getTransportDeviceMsgRateLimit(),
profileConfiguration.getTransportDeviceTelemetryMsgRateLimit(),
profileConfiguration.getTransportDeviceTelemetryDataPointsRateLimit());
expectedLimits.setRateLimits(deviceLimits);
expectedLimits.setMaxPayloadSize(maxPayloadSize);
expectedLimits.setMaxInflightMessages(maxInflightMessages);
MqttTestConfigProperties configProperties = MqttTestConfigProperties.builder()
.deviceName("Test Get Service Configuration")
.transportPayloadType(TransportPayloadType.JSON)
.build();
processBeforeTest(configProperties);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
MqttTestCallback callback = new MqttTestSubscribeOnTopicCallback(DEVICE_RPC_RESPONSE_TOPIC + "1");
client.setCallback(callback);
client.subscribeAndWait(DEVICE_RPC_RESPONSE_SUB_TOPIC, MqttQoS.AT_MOST_ONCE);
client.publishAndWait(DEVICE_RPC_REQUESTS_TOPIC + "1", "{\"method\":\"getSessionLimits\",\"params\":{}}".getBytes());
assertThat(callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS))
.as("await callback").isTrue();
var payload = callback.getPayloadBytes();
SessionLimits actualLimits = JacksonUtil.fromBytes(payload, SessionLimits.class);
assertEquals(expectedLimits, actualLimits);
client.disconnect();
}
@Test
public void getSessionLimitsRpcForGatewayTest() throws Exception {
loginSysAdmin();
TenantProfile tenantProfile = doGet("/api/tenantProfile/" + tenantProfileId, TenantProfile.class);
DefaultTenantProfileConfiguration profileConfiguration = tenantProfile.getDefaultProfileConfiguration();
profileConfiguration.setTransportGatewayMsgRateLimit("100:600");
profileConfiguration.setTransportGatewayTelemetryMsgRateLimit("50:600");
profileConfiguration.setTransportGatewayTelemetryDataPointsRateLimit("200:600");
profileConfiguration.setTransportGatewayDeviceMsgRateLimit("20:600");
profileConfiguration.setTransportGatewayDeviceTelemetryMsgRateLimit("10:600");
profileConfiguration.setTransportGatewayDeviceTelemetryDataPointsRateLimit("40:600");
doPost("/api/tenantProfile", tenantProfile);
var expectedLimits = new GatewaySessionLimits();
var gatewayLimits = new SessionLimits.SessionRateLimits(profileConfiguration.getTransportGatewayMsgRateLimit(),
profileConfiguration.getTransportGatewayTelemetryMsgRateLimit(),
profileConfiguration.getTransportGatewayTelemetryDataPointsRateLimit());
var gatewayDeviceLimits = new SessionLimits.SessionRateLimits(profileConfiguration.getTransportGatewayDeviceMsgRateLimit(),
profileConfiguration.getTransportGatewayDeviceTelemetryMsgRateLimit(),
profileConfiguration.getTransportGatewayDeviceTelemetryDataPointsRateLimit());
expectedLimits.setGatewayRateLimits(gatewayLimits);
expectedLimits.setRateLimits(gatewayDeviceLimits);
expectedLimits.setMaxPayloadSize(maxPayloadSize);
expectedLimits.setMaxInflightMessages(maxInflightMessages);
MqttTestConfigProperties configProperties = MqttTestConfigProperties.builder()
.gatewayName("Test Get Service Configuration Gateway")
.transportPayloadType(TransportPayloadType.JSON)
.build();
processBeforeTest(configProperties);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
MqttTestCallback callback = new MqttTestSubscribeOnTopicCallback(DEVICE_RPC_RESPONSE_TOPIC + "1");
client.setCallback(callback);
client.subscribeAndWait(DEVICE_RPC_RESPONSE_SUB_TOPIC, MqttQoS.AT_MOST_ONCE);
client.publishAndWait(DEVICE_RPC_REQUESTS_TOPIC + "1", "{\"method\":\"getSessionLimits\",\"params\":{}}".getBytes());
assertTrue(callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS));
var payload = callback.getPayloadBytes();
SessionLimits actualLimits = JacksonUtil.fromBytes(payload, GatewaySessionLimits.class);
assertEquals(expectedLimits, actualLimits);
client.disconnect();
}
}

5
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java

@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.common.transport.TransportTenantProfileCache;
import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor;
import org.thingsboard.server.transport.mqtt.adaptors.ProtoMqttAdaptor;
@ -51,6 +52,10 @@ public class MqttTransportContext extends TransportContext {
@Autowired
private ProtoMqttAdaptor protoMqttAdaptor;
@Getter
@Autowired
private TransportTenantProfileCache tenantProfileCache;
@Getter
@Value("${transport.mqtt.netty.max_payload_size}")
private Integer maxPayloadSize;

49
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java

@ -60,6 +60,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.rpc.RpcStatus;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
@ -78,6 +79,8 @@ import org.thingsboard.server.gen.transport.mqtt.SparkplugBProto;
import org.thingsboard.server.queue.scheduler.SchedulerComponent;
import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
import org.thingsboard.server.transport.mqtt.adaptors.ProtoMqttAdaptor;
import org.thingsboard.server.transport.mqtt.limits.GatewaySessionLimits;
import org.thingsboard.server.transport.mqtt.limits.SessionLimits;
import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx;
import org.thingsboard.server.transport.mqtt.session.GatewaySessionHandler;
import org.thingsboard.server.transport.mqtt.session.MqttTopicMatcher;
@ -130,6 +133,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
private static final Pattern FW_REQUEST_PATTERN = Pattern.compile(MqttTopics.DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN);
private static final Pattern SW_REQUEST_PATTERN = Pattern.compile(MqttTopics.DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN);
private static final String SESSION_LIMITS = "getSessionLimits";
private static final String PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE";
@ -493,8 +497,12 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
transportService.process(deviceSessionCtx.getSessionInfo(), rpcResponseMsg, getPubAckCallback(ctx, msgId, rpcResponseMsg));
} else if (topicName.startsWith(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC)) {
TransportProtos.ToServerRpcRequestMsg rpcRequestMsg = payloadAdaptor.convertToServerRpcRequest(deviceSessionCtx, mqttMsg, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC);
transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequestMsg, getPubAckCallback(ctx, msgId, rpcRequestMsg));
toServerRpcSubTopicType = TopicType.V1;
if (SESSION_LIMITS.equals(rpcRequestMsg.getMethodName())) {
onGetSessionLimitsRpc(deviceSessionCtx.getSessionInfo(), ctx, msgId, rpcRequestMsg);
} else {
transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequestMsg, getPubAckCallback(ctx, msgId, rpcRequestMsg));
}
} else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) {
TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg);
transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg));
@ -928,7 +936,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
}
} else {
log.debug("[{}] Failed to process unsubscription [{}] to [{}] - Subscription not found", sessionId, mqttMsg.variableHeader().messageId(), topicName);
unSubResults.add((short)MqttReasonCodes.UnsubAck.NO_SUBSCRIPTION_EXISTED.byteValue());
unSubResults.add((short) MqttReasonCodes.UnsubAck.NO_SUBSCRIPTION_EXISTED.byteValue());
}
}
if (!activityReported) {
@ -1330,6 +1338,43 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
}
}
private void onGetSessionLimitsRpc(TransportProtos.SessionInfoProto sessionInfo, ChannelHandlerContext ctx, int msgId, TransportProtos.ToServerRpcRequestMsg rpcRequestMsg) {
var tenantProfile = context.getTenantProfileCache().get(deviceSessionCtx.getTenantId());
DefaultTenantProfileConfiguration profile = tenantProfile.getDefaultProfileConfiguration();
SessionLimits sessionLimits;
if (sessionInfo.getIsGateway()) {
var gatewaySessionLimits = new GatewaySessionLimits();
var gatewayLimits = new SessionLimits.SessionRateLimits(profile.getTransportGatewayMsgRateLimit(),
profile.getTransportGatewayTelemetryMsgRateLimit(),
profile.getTransportGatewayTelemetryDataPointsRateLimit());
var gatewayDeviceLimits = new SessionLimits.SessionRateLimits(profile.getTransportGatewayDeviceMsgRateLimit(),
profile.getTransportGatewayDeviceTelemetryMsgRateLimit(),
profile.getTransportGatewayDeviceTelemetryDataPointsRateLimit());
gatewaySessionLimits.setGatewayRateLimits(gatewayLimits);
gatewaySessionLimits.setRateLimits(gatewayDeviceLimits);
sessionLimits = gatewaySessionLimits;
} else {
var rateLimits = new SessionLimits.SessionRateLimits(profile.getTransportDeviceMsgRateLimit(),
profile.getTransportDeviceTelemetryMsgRateLimit(),
profile.getTransportDeviceTelemetryDataPointsRateLimit());
sessionLimits = new SessionLimits();
sessionLimits.setRateLimits(rateLimits);
}
sessionLimits.setMaxPayloadSize(context.getMaxPayloadSize());
sessionLimits.setMaxInflightMessages(context.getMessageQueueSizePerDeviceLimit());
ack(ctx, msgId, MqttReasonCodes.PubAck.SUCCESS);
TransportProtos.ToServerRpcResponseMsg responseMsg = TransportProtos.ToServerRpcResponseMsg.newBuilder()
.setRequestId(rpcRequestMsg.getRequestId())
.setPayload(JacksonUtil.toString(sessionLimits))
.build();
onToServerRpcResponse(responseMsg);
}
private void handleToSparkplugDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws ThingsboardException {
SparkplugMessageType messageType = SparkplugMessageType.parseMessageType(rpcRequest.getMethodName());
SparkplugRpcRequestHeader header;

25
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/limits/GatewaySessionLimits.java

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

29
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/limits/SessionLimits.java

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.mqtt.limits;
import lombok.Data;
import org.thingsboard.server.common.data.TransportPayloadType;
@Data
public class SessionLimits {
private int maxPayloadSize;
private int maxInflightMessages;
private SessionRateLimits rateLimits;
public record SessionRateLimits(String messages, String telemetryMessages, String telemetryDataPoints) {}
}

1
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/DeviceSessionCtx.java

@ -82,6 +82,7 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
private volatile MqttTopicFilter telemetryTopicFilter = MqttTopicFilterFactory.getDefaultTelemetryFilter();
private volatile MqttTopicFilter attributesPublishTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter();
private volatile MqttTopicFilter attributesSubscribeTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter();
@Getter
private volatile TransportPayloadType payloadType = TransportPayloadType.JSON;
private volatile Descriptors.Descriptor attributesDynamicMessageDescriptor;
private volatile Descriptors.Descriptor telemetryDynamicMessageDescriptor;

5
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/session/DeviceAwareSessionContext.java

@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.transport.auth.TransportDeviceInfo;
import org.thingsboard.server.gen.transport.TransportProtos;
@ -40,6 +41,8 @@ public abstract class DeviceAwareSessionContext implements SessionContext {
@Getter
private volatile DeviceId deviceId;
@Getter
private volatile TenantId tenantId;
@Getter
protected volatile TransportDeviceInfo deviceInfo;
@Getter
@Setter
@ -58,6 +61,7 @@ public abstract class DeviceAwareSessionContext implements SessionContext {
public void setDeviceInfo(TransportDeviceInfo deviceInfo) {
this.deviceInfo = deviceInfo;
this.deviceId = deviceInfo.getDeviceId();
this.tenantId = deviceInfo.getTenantId();
}
@Override
@ -65,7 +69,6 @@ public abstract class DeviceAwareSessionContext implements SessionContext {
this.sessionInfo = sessionInfo;
this.deviceProfile = deviceProfile;
this.deviceInfo.setDeviceType(deviceProfile.getName());
}
@Override

7
dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java

@ -236,8 +236,9 @@ public class AssetProfileServiceImpl extends CachedVersionedEntityService<AssetP
log.trace("Executing findOrCreateAssetProfile");
AssetProfile assetProfile = findAssetProfileByName(tenantId, name, false);
if (assetProfile == null) {
boolean isDefault = "default".equals(name) && findDefaultAssetProfile(tenantId) == null;
try {
assetProfile = this.doCreateDefaultAssetProfile(tenantId, name, name.equals("default"), true);
assetProfile = this.doCreateAssetProfile(tenantId, name, isDefault, true);
} catch (DataValidationException e) {
if (ASSET_PROFILE_WITH_SUCH_NAME_ALREADY_EXISTS.equals(e.getMessage())) {
assetProfile = findAssetProfileByName(tenantId, name, false);
@ -252,10 +253,10 @@ public class AssetProfileServiceImpl extends CachedVersionedEntityService<AssetP
@Override
public AssetProfile createDefaultAssetProfile(TenantId tenantId) {
log.trace("Executing createDefaultAssetProfile tenantId [{}]", tenantId);
return doCreateDefaultAssetProfile(tenantId, "default", true, false);
return doCreateAssetProfile(tenantId, "default", true, false);
}
private AssetProfile doCreateDefaultAssetProfile(TenantId tenantId, String profileName, boolean defaultProfile, boolean publishSaveEvent) {
private AssetProfile doCreateAssetProfile(TenantId tenantId, String profileName, boolean defaultProfile, boolean publishSaveEvent) {
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
AssetProfile assetProfile = new AssetProfile();
assetProfile.setTenantId(tenantId);

7
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java

@ -271,8 +271,9 @@ public class DeviceProfileServiceImpl extends CachedVersionedEntityService<Devic
log.trace("Executing findOrCreateDefaultDeviceProfile");
DeviceProfile deviceProfile = findDeviceProfileByName(tenantId, name, false);
if (deviceProfile == null) {
boolean isDefault = "default".equals(name) && findDefaultDeviceProfile(tenantId) == null;
try {
deviceProfile = this.doCreateDefaultDeviceProfile(tenantId, name, name.equals("default"), true);
deviceProfile = this.doCreateDeviceProfile(tenantId, name, isDefault, true);
} catch (DataValidationException e) {
if (DEVICE_PROFILE_WITH_SUCH_NAME_ALREADY_EXISTS.equals(e.getMessage())) {
deviceProfile = findDeviceProfileByName(tenantId, name, false);
@ -287,10 +288,10 @@ public class DeviceProfileServiceImpl extends CachedVersionedEntityService<Devic
@Override
public DeviceProfile createDefaultDeviceProfile(TenantId tenantId) {
log.trace("Executing createDefaultDeviceProfile tenantId [{}]", tenantId);
return doCreateDefaultDeviceProfile(tenantId, "default", true, false);
return doCreateDeviceProfile(tenantId, "default", true, false);
}
private DeviceProfile doCreateDefaultDeviceProfile(TenantId tenantId, String profileName, boolean defaultProfile, boolean publishSaveEvent) {
private DeviceProfile doCreateDeviceProfile(TenantId tenantId, String profileName, boolean defaultProfile, boolean publishSaveEvent) {
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
DeviceProfile deviceProfile = new DeviceProfile();
deviceProfile.setTenantId(tenantId);

2
dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java

@ -36,6 +36,7 @@ import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.oauth2.OAuth2ClientDao;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -149,6 +150,7 @@ public class DomainServiceImpl extends AbstractEntityService implements DomainSe
}
List<OAuth2ClientInfo> clients = oauth2ClientDao.findByDomainId(domain.getUuidId()).stream()
.map(OAuth2ClientInfo::new)
.sorted(Comparator.comparing(OAuth2ClientInfo::getTitle))
.collect(Collectors.toList());
return new DomainInfo(domain, clients);
}

2
dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppServiceImpl.java

@ -36,6 +36,7 @@ import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.oauth2.OAuth2ClientDao;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -143,6 +144,7 @@ public class MobileAppServiceImpl extends AbstractEntityService implements Mobil
private MobileAppInfo getMobileAppInfo(MobileApp mobileApp) {
List<OAuth2ClientInfo> clients = oauth2ClientDao.findByMobileAppId(mobileApp.getUuidId()).stream()
.map(OAuth2ClientInfo::new)
.sorted(Comparator.comparing(OAuth2ClientInfo::getTitle))
.collect(Collectors.toList());
return new MobileAppInfo(mobileApp, clients);
}

4
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java

@ -35,6 +35,7 @@ import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.service.DataValidator;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@ -68,7 +69,6 @@ public class OAuth2ClientServiceImpl extends AbstractEntityService implements OA
}
@Override
@Transactional
public OAuth2Client saveOAuth2Client(TenantId tenantId, OAuth2Client oAuth2Client) {
log.trace("Executing saveOAuth2Client [{}]", oAuth2Client);
oAuth2ClientDataValidator.validate(oAuth2Client, OAuth2Client::getTenantId);
@ -96,7 +96,6 @@ public class OAuth2ClientServiceImpl extends AbstractEntityService implements OA
}
@Override
@Transactional
public void deleteOAuth2ClientById(TenantId tenantId, OAuth2ClientId oAuth2ClientId) {
log.trace("Executing deleteOAuth2ClientById [{}]", oAuth2ClientId);
oauth2ClientDao.removeById(tenantId, oAuth2ClientId.getId());
@ -126,6 +125,7 @@ public class OAuth2ClientServiceImpl extends AbstractEntityService implements OA
return oauth2ClientDao.findByIds(tenantId.getId(), oAuth2ClientIds)
.stream()
.map(OAuth2ClientInfo::new)
.sorted(Comparator.comparing(OAuth2ClientInfo::getTitle))
.collect(Collectors.toList());
}

12
dao/src/test/java/org/thingsboard/server/dao/service/AssetProfileServiceTest.java

@ -163,6 +163,18 @@ public class AssetProfileServiceTest extends AbstractServiceTest {
});
}
@Test
public void testSaveAssetProfileWithNameDefaultAndProfileWithIsDefaultAndDifferentNameAlreadyExists() {
AssetProfile defaultAssetProfile = assetProfileService.findDefaultAssetProfile(tenantId);
Assert.assertNotNull(defaultAssetProfile);
defaultAssetProfile.setName("Asset Profile 1");
assetProfileService.saveAssetProfile(defaultAssetProfile);
AssetProfile createdAssetProfile = assetProfileService.findOrCreateAssetProfile(tenantId, "default");
Assert.assertNotNull(createdAssetProfile);
Assert.assertEquals("default", createdAssetProfile.getName());
Assert.assertFalse(createdAssetProfile.isDefault());
}
@Test
public void testDeleteAssetProfileWithExistingAsset() {
AssetProfile assetProfile = this.createAssetProfile(tenantId, "Asset Profile");

12
dao/src/test/java/org/thingsboard/server/dao/service/DeviceProfileServiceTest.java

@ -209,6 +209,18 @@ public class DeviceProfileServiceTest extends AbstractServiceTest {
});
}
@Test
public void testSaveDeviceProfileWithNameDefaultAndProfileWithIsDefaultAndDifferentNameAlreadyExists() {
DeviceProfile defaultDeviceProfile = deviceProfileService.findDefaultDeviceProfile(tenantId);
Assert.assertNotNull(defaultDeviceProfile);
defaultDeviceProfile.setName("Device Profile 1");
deviceProfileService.saveDeviceProfile(defaultDeviceProfile);
DeviceProfile createdDeviceProfile = deviceProfileService.findOrCreateDeviceProfile(tenantId, "default");
Assert.assertNotNull(createdDeviceProfile);
Assert.assertEquals("default", createdDeviceProfile.getName());
Assert.assertFalse(createdDeviceProfile.isDefault());
}
@Ignore
@Test
public void testChangeDeviceProfileTypeWithExistingDevices() {

5
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java

@ -122,7 +122,7 @@ public class TbHttpClient {
SslContext sslContext = config.getCredentials().initSslContext();
httpClient = httpClient.secure(t -> t.sslContext(sslContext));
}
} else if (!config.isUseSimpleClientHttpFactory()) {
} else if (config.isUseSimpleClientHttpFactory()) {
if (CredentialsType.CERT_PEM == config.getCredentials().getType()) {
throw new TbNodeException("Simple HTTP Factory does not support CERT PEM credentials!");
}
@ -347,8 +347,7 @@ public class TbHttpClient {
Properties properties = System.getProperties();
if (properties.containsKey(HTTP_PROXY_HOST) || properties.containsKey(HTTPS_PROXY_HOST)) {
createHttpProxyFrom(option, properties);
}
if (properties.containsKey(SOCKS_PROXY_HOST)) {
} else if (properties.containsKey(SOCKS_PROXY_HOST)) {
createSocksProxyFrom(option, properties);
}
}

2
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html

@ -277,7 +277,7 @@
'tb-dashboard-toolbar-animated': isToolbarOpenedAnimate,
'tb-dashboard-toolbar-closed': !toolbarOpened }">
<mat-drawer-container hasBackdrop="false" class="tb-absolute-fill tb-dashboard-drawer-container">
<mat-drawer-content fxLayout="column" fxLayoutAlign="center start">
<mat-drawer-content #dashboardContent fxLayout="column" fxLayoutAlign="center start">
<section *ngIf="!widgetEditMode" class="tb-dashboard-title"
[ngStyle]="{'color': dashboard.configuration.settings.titleColor}">
<h3 [fxShow]="!isEdit && displayTitle()">{{ translatedDashboardTitle }}</h3>

2
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts

@ -222,6 +222,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
@ViewChild('dashboardContainer') dashboardContainer: ElementRef<HTMLElement>;
@ViewChild('dashboardContent', {read: ElementRef}) dashboardContent: ElementRef<HTMLElement>;
prevDashboard: Dashboard;
iframeMode = this.utils.iframeMode;

1
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.models.ts

@ -56,6 +56,7 @@ export interface DashboardContext {
export interface IDashboardController {
dashboardCtx: DashboardContext;
dashboardContainer: ElementRef;
dashboardContent: ElementRef;
elRef: ElementRef;
openRightLayout();
openDashboardState(stateId: string, openRightLayout: boolean);

1
ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss

@ -173,6 +173,7 @@ gridster-item:hover {
}
.tooltipster-sidetip.tb-widget-edit-actions-tooltip {
position: absolute !important;
.tooltipster-box {
user-select: none;
background: #fff;

6
ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts

@ -148,9 +148,9 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, O
$(this.gridsterItem.el).on('mousedown', (e) => this.onMouseDown(e.originalEvent));
$(this.gridsterItem.el).on('click', (e) => this.onClicked(e.originalEvent));
$(this.gridsterItem.el).on('contextmenu', (e) => this.onContextMenu(e.originalEvent));
const dashboardElement = this.widget.widgetContext.dashboardPageElement;
if (dashboardElement) {
this.initEditWidgetActionTooltip(dashboardElement);
const dashboardContentElement = this.widget.widgetContext.dashboardContentElement;
if (dashboardContentElement) {
this.initEditWidgetActionTooltip(dashboardContentElement);
}
}

4
ui-ngx/src/app/modules/home/models/widget-component.models.ts

@ -184,6 +184,10 @@ export class WidgetContext {
return this.dashboard?.stateController?.dashboardCtrl?.elRef?.nativeElement;
}
get dashboardContentElement(): HTMLElement {
return this.dashboard?.stateController?.dashboardCtrl?.dashboardContent?.nativeElement;
}
authService: AuthService;
deviceService: DeviceService;
assetService: AssetService;

5
ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/clients-table-config.resolver.ts

@ -64,9 +64,8 @@ export class ClientsTableConfigResolver implements Resolve<EntityTableConfig<OAu
this.config.columns.push(
new DateEntityTableColumn<OAuth2ClientInfo>('createdTime', 'common.created-time', this.datePipe, '170px'),
new EntityTableColumn<OAuth2ClientInfo>('title', 'admin.oauth2.title', '35%'),
new EntityTableColumn<OAuth2ClientInfo>('providerName', 'admin.oauth2.provider', '170px'),
new EntityTableColumn<OAuth2ClientInfo>('platforms', 'admin.oauth2.allowed-platforms', '170px',
new EntityTableColumn<OAuth2ClientInfo>('title', 'admin.oauth2.title', '350px'),
new EntityTableColumn<OAuth2ClientInfo>('platforms', 'admin.oauth2.allowed-platforms', '100%',
(clientInfo) => {
return clientInfo.platforms && clientInfo.platforms.length ?
clientInfo.platforms.map(platform => this.translate.instant(platformTypeTranslations.get(platform))).join(', ') :

Loading…
Cancel
Save