diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index d5fc8055d3..51205e97c9 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -604,7 +604,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso if (DataConstants.SHARED_SCOPE.equals(msg.getScope())) { List attributes = new ArrayList<>(msg.getValues()); if (attributes.size() > 0) { - List sharedUpdated = msg.getValues().stream().map(t -> KvProtoUtil.toProto(t.getLastUpdateTs(), t)) + List sharedUpdated = msg.getValues().stream().map(t -> KvProtoUtil.toTsKvProto(t.getLastUpdateTs(), t)) .collect(Collectors.toList()); if (!sharedUpdated.isEmpty()) { notification.addAllSharedUpdated(sharedUpdated); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java index a7372ddfaf..fc244417bc 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java @@ -73,6 +73,24 @@ public class TenantMsgConstructorV1 implements TenantMsgConstructor { @Override public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { + tenantProfile = JacksonUtil.clone(tenantProfile); + // clear all config + var tenantProfileData = tenantProfile.getProfileData(); + var configuration = tenantProfile.getDefaultProfileConfiguration(); + configuration.setRpcTtlDays(0); + configuration.setMaxJSExecutions(0); + configuration.setMaxREExecutions(0); + configuration.setMaxDPStorageDays(0); + configuration.setMaxTbelExecutions(0); + configuration.setQueueStatsTtlDays(0); + configuration.setMaxTransportMessages(0); + configuration.setDefaultStorageTtlDays(0); + configuration.setMaxTransportDataPoints(0); + configuration.setRuleEngineExceptionsTtlDays(0); + configuration.setMaxRuleNodeExecutionsPerMessage(0); + tenantProfileData.setConfiguration(configuration); + tenantProfile.setProfileData(tenantProfileData); + ByteString profileData = EdgeVersionUtils.isEdgeVersionOlderThan(edgeVersion, EdgeVersion.V_3_6_2) ? ByteString.empty() : ByteString.copyFrom(tenantProfile.getProfileDataBytes()); TenantProfileUpdateMsg.Builder builder = TenantProfileUpdateMsg.newBuilder() @@ -88,4 +106,5 @@ public class TenantMsgConstructorV1 implements TenantMsgConstructor { } return builder.build(); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java index c6ccb439ea..274e18e7f3 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java @@ -19,6 +19,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; @@ -36,6 +37,23 @@ public class TenantMsgConstructorV2 implements TenantMsgConstructor { @Override public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { + tenantProfile = JacksonUtil.clone(tenantProfile); + // clear all config + var configuration = tenantProfile.getDefaultProfileConfiguration(); + configuration.setRpcTtlDays(0); + configuration.setMaxJSExecutions(0); + configuration.setMaxREExecutions(0); + configuration.setMaxDPStorageDays(0); + configuration.setMaxTbelExecutions(0); + configuration.setQueueStatsTtlDays(0); + configuration.setMaxTransportMessages(0); + configuration.setDefaultStorageTtlDays(0); + configuration.setMaxTransportDataPoints(0); + configuration.setRuleEngineExceptionsTtlDays(0); + configuration.setMaxRuleNodeExecutionsPerMessage(0); + tenantProfile.getProfileData().setConfiguration(configuration); + return TenantProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenantProfile)).build(); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/mail/RefreshTokenExpCheckService.java b/application/src/main/java/org/thingsboard/server/service/mail/RefreshTokenExpCheckService.java index 32fe4eb15c..7c7edf2164 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/RefreshTokenExpCheckService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/RefreshTokenExpCheckService.java @@ -54,23 +54,35 @@ public class RefreshTokenExpCheckService { AdminSettings settings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail"); if (settings != null && settings.getJsonValue().has("enableOauth2") && settings.getJsonValue().get("enableOauth2").asBoolean()) { JsonNode jsonValue = settings.getJsonValue(); - if (OFFICE_365.name().equals(jsonValue.get("providerId").asText()) && jsonValue.has("refreshTokenExpires")) { - long expiresIn = jsonValue.get("refreshTokenExpires").longValue(); - if ((expiresIn - System.currentTimeMillis()) < 604800000L) { //less than 7 days - log.info("Trying to refresh refresh token."); + if (OFFICE_365.name().equals(jsonValue.get("providerId").asText()) && jsonValue.has("refreshToken") + && jsonValue.has("refreshTokenExpires")) { + try { + long expiresIn = jsonValue.get("refreshTokenExpires").longValue(); + long tokenLifeDuration = expiresIn - System.currentTimeMillis(); + if (tokenLifeDuration < 0) { + ((ObjectNode) jsonValue).put("tokenGenerated", false); + ((ObjectNode) jsonValue).remove("refreshToken"); + ((ObjectNode) jsonValue).remove("refreshTokenExpires"); - String clientId = jsonValue.get("clientId").asText(); - String clientSecret = jsonValue.get("clientSecret").asText(); - String refreshToken = jsonValue.get("refreshToken").asText(); - String tokenUri = jsonValue.get("tokenUri").asText(); + adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, settings); + } else if (tokenLifeDuration < 604800000L) { //less than 7 days + log.info("Trying to refresh refresh token."); - TokenResponse tokenResponse = new RefreshTokenRequest(new NetHttpTransport(), new GsonFactory(), - new GenericUrl(tokenUri), refreshToken) - .setClientAuthentication(new ClientParametersAuthentication(clientId, clientSecret)) - .execute(); - ((ObjectNode) jsonValue).put("refreshToken", tokenResponse.getRefreshToken()); - ((ObjectNode) jsonValue).put("refreshTokenExpires", Instant.now().plus(Duration.ofDays(AZURE_DEFAULT_REFRESH_TOKEN_LIFETIME_IN_DAYS)).toEpochMilli()); - adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, settings); + String clientId = jsonValue.get("clientId").asText(); + String clientSecret = jsonValue.get("clientSecret").asText(); + String refreshToken = jsonValue.get("refreshToken").asText(); + String tokenUri = jsonValue.get("tokenUri").asText(); + + TokenResponse tokenResponse = new RefreshTokenRequest(new NetHttpTransport(), new GsonFactory(), + new GenericUrl(tokenUri), refreshToken) + .setClientAuthentication(new ClientParametersAuthentication(clientId, clientSecret)) + .execute(); + ((ObjectNode) jsonValue).put("refreshToken", tokenResponse.getRefreshToken()); + ((ObjectNode) jsonValue).put("refreshTokenExpires", Instant.now().plus(Duration.ofDays(AZURE_DEFAULT_REFRESH_TOKEN_LIFETIME_IN_DAYS)).toEpochMilli()); + adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, settings); + } + } catch (Exception e) { + log.error("Error occurred while checking token", e); } } } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index fbbb65d4ef..cdb3f19601 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -583,7 +583,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService { Queue config = queueService.findQueueByTenantIdAndName(queueKey.getTenantId(), queueKey.getQueueName()); + if (config == null) { + if (!partitions.isEmpty()) { + log.error("[{}] Queue configuration is missing", queueKey, new RuntimeException("stacktrace")); + } + return null; + } return createConsumer(queueKey, config); }); - consumer.update(partitions); + if (consumer != null) { + consumer.update(partitions); + } } }); consumers.keySet().stream() diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java index ff8b12a32f..243a840f51 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java @@ -23,10 +23,8 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; -import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.JsonDataEntry; import org.thingsboard.server.common.data.kv.KvEntry; @@ -35,8 +33,6 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; -import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionMgrMsgProto; import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmDeleteProto; import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmUpdateProto; @@ -47,7 +43,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesDeletePr import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; @@ -60,6 +55,9 @@ import java.util.Map; import java.util.TreeMap; import java.util.UUID; +import static org.thingsboard.server.common.util.KvProtoUtil.fromKeyValueTypeProto; +import static org.thingsboard.server.common.util.KvProtoUtil.toKeyValueTypeProto; + public class TbSubscriptionUtils { public static ToCoreMsg toSubEventProto(String serviceId, TbEntitySubEvent event) { @@ -181,7 +179,7 @@ public class TbSubscriptionUtils { builder.setEntityIdLSB(entityId.getId().getLeastSignificantBits()); builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits()); builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits()); - ts.forEach(v -> builder.addData(toKeyValueProto(v.getTs(), v).build())); + ts.forEach(v -> builder.addData(toTsKvProtoBuilder(v.getTs(), v).build())); SubscriptionMgrMsgProto.Builder msgBuilder = SubscriptionMgrMsgProto.newBuilder(); msgBuilder.setTsUpdate(builder); return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build(); @@ -208,7 +206,7 @@ public class TbSubscriptionUtils { builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits()); builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits()); builder.setScope(scope); - attributes.forEach(v -> builder.addData(toKeyValueProto(v.getLastUpdateTs(), v).build())); + attributes.forEach(v -> builder.addData(toTsKvProtoBuilder(v.getLastUpdateTs(), v).build())); SubscriptionMgrMsgProto.Builder msgBuilder = SubscriptionMgrMsgProto.newBuilder(); msgBuilder.setAttrUpdate(builder); @@ -231,123 +229,10 @@ public class TbSubscriptionUtils { return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build(); } - private static TsKvProto.Builder toKeyValueProto(long ts, KvEntry attr) { - KeyValueProto.Builder dataBuilder = KeyValueProto.newBuilder(); - dataBuilder.setKey(attr.getKey()); - dataBuilder.setType(KeyValueType.forNumber(attr.getDataType().ordinal())); - switch (attr.getDataType()) { - case BOOLEAN: - attr.getBooleanValue().ifPresent(dataBuilder::setBoolV); - break; - case LONG: - attr.getLongValue().ifPresent(dataBuilder::setLongV); - break; - case DOUBLE: - attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV); - break; - case JSON: - attr.getJsonValue().ifPresent(dataBuilder::setJsonV); - break; - case STRING: - attr.getStrValue().ifPresent(dataBuilder::setStringV); - break; - } - return TsKvProto.newBuilder().setTs(ts).setKv(dataBuilder); - } - - private static TransportProtos.TsValueProto toTsValueProto(long ts, KvEntry attr) { - TransportProtos.TsValueProto.Builder dataBuilder = TransportProtos.TsValueProto.newBuilder(); - dataBuilder.setTs(ts); - dataBuilder.setType(KeyValueType.forNumber(attr.getDataType().ordinal())); - switch (attr.getDataType()) { - case BOOLEAN: - attr.getBooleanValue().ifPresent(dataBuilder::setBoolV); - break; - case LONG: - attr.getLongValue().ifPresent(dataBuilder::setLongV); - break; - case DOUBLE: - attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV); - break; - case JSON: - attr.getJsonValue().ifPresent(dataBuilder::setJsonV); - break; - case STRING: - attr.getStrValue().ifPresent(dataBuilder::setStringV); - break; - } - return dataBuilder.build(); - } - - public static EntityId toEntityId(String entityType, long entityIdMSB, long entityIdLSB) { return EntityIdFactory.getByTypeAndUuid(entityType, new UUID(entityIdMSB, entityIdLSB)); } - public static List toTsKvEntityList(List dataList) { - List result = new ArrayList<>(dataList.size()); - dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), getKvEntry(proto.getKv())))); - return result; - } - - public static List toAttributeKvList(List dataList) { - List result = new ArrayList<>(dataList.size()); - dataList.forEach(proto -> result.add(new BaseAttributeKvEntry(getKvEntry(proto.getKv()), proto.getTs()))); - return result; - } - - private static KvEntry getKvEntry(KeyValueProto proto) { - KvEntry entry = null; - DataType type = DataType.values()[proto.getType().getNumber()]; - switch (type) { - case BOOLEAN: - entry = new BooleanDataEntry(proto.getKey(), proto.getBoolV()); - break; - case LONG: - entry = new LongDataEntry(proto.getKey(), proto.getLongV()); - break; - case DOUBLE: - entry = new DoubleDataEntry(proto.getKey(), proto.getDoubleV()); - break; - case STRING: - entry = new StringDataEntry(proto.getKey(), proto.getStringV()); - break; - case JSON: - entry = new JsonDataEntry(proto.getKey(), proto.getJsonV()); - break; - } - return entry; - } - - public static List toTsKvEntityList(String key, List dataList) { - List result = new ArrayList<>(dataList.size()); - dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), getKvEntry(key, proto)))); - return result; - } - - private static KvEntry getKvEntry(String key, TransportProtos.TsValueProto proto) { - KvEntry entry = null; - DataType type = DataType.values()[proto.getType().getNumber()]; - switch (type) { - case BOOLEAN: - entry = new BooleanDataEntry(key, proto.getBoolV()); - break; - case LONG: - entry = new LongDataEntry(key, proto.getLongV()); - break; - case DOUBLE: - entry = new DoubleDataEntry(key, proto.getDoubleV()); - break; - case STRING: - entry = new StringDataEntry(key, proto.getStringV()); - break; - case JSON: - entry = new JsonDataEntry(key, proto.getJsonV()); - break; - } - return entry; - } - public static ToCoreMsg toAlarmUpdateProto(TenantId tenantId, EntityId entityId, AlarmInfo alarm) { TbAlarmUpdateProto.Builder builder = TbAlarmUpdateProto.newBuilder(); builder.setEntityType(entityId.getEntityType().name()); @@ -405,7 +290,7 @@ public class TbSubscriptionUtils { public static List fromProto(TransportProtos.TbSubUpdateProto proto) { List result = new ArrayList<>(); for (var p : proto.getDataList()) { - result.addAll(toTsKvEntityList(p.getKey(), p.getTsValueList())); + result.addAll(fromTsValueProtoList(p.getKey(), p.getTsValueList())); } return result; } @@ -447,4 +332,48 @@ public class TbSubscriptionUtils { return ToCoreNotificationMsg.newBuilder().setToLocalSubscriptionServiceMsg(result).build(); } + public static TransportProtos.TsKvProto.Builder toTsKvProtoBuilder(long ts, KvEntry attr) { + TransportProtos.KeyValueProto.Builder dataBuilder = TransportProtos.KeyValueProto.newBuilder(); + dataBuilder.setKey(attr.getKey()); + dataBuilder.setType(toKeyValueTypeProto(attr.getDataType())); + switch (attr.getDataType()) { + case BOOLEAN -> attr.getBooleanValue().ifPresent(dataBuilder::setBoolV); + case LONG -> attr.getLongValue().ifPresent(dataBuilder::setLongV); + case DOUBLE -> attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV); + case JSON -> attr.getJsonValue().ifPresent(dataBuilder::setJsonV); + case STRING -> attr.getStrValue().ifPresent(dataBuilder::setStringV); + } + return TransportProtos.TsKvProto.newBuilder().setTs(ts).setKv(dataBuilder); + } + + public static TransportProtos.TsValueProto toTsValueProto(long ts, KvEntry attr) { + TransportProtos.TsValueProto.Builder dataBuilder = TransportProtos.TsValueProto.newBuilder(); + dataBuilder.setTs(ts); + dataBuilder.setType(toKeyValueTypeProto(attr.getDataType())); + switch (attr.getDataType()) { + case BOOLEAN -> attr.getBooleanValue().ifPresent(dataBuilder::setBoolV); + case LONG -> attr.getLongValue().ifPresent(dataBuilder::setLongV); + case DOUBLE -> attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV); + case JSON -> attr.getJsonValue().ifPresent(dataBuilder::setJsonV); + case STRING -> attr.getStrValue().ifPresent(dataBuilder::setStringV); + } + return dataBuilder.build(); + } + + private static List fromTsValueProtoList(String key, List dataList) { + List result = new ArrayList<>(dataList.size()); + dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), fromTsKvProto(key, proto)))); + return result; + } + + private static KvEntry fromTsKvProto(String key, TransportProtos.TsValueProto proto) { + return switch (fromKeyValueTypeProto(proto.getType())) { + case BOOLEAN -> new BooleanDataEntry(key, proto.getBoolV()); + case LONG -> new LongDataEntry(key, proto.getLongV()); + case DOUBLE -> new DoubleDataEntry(key, proto.getDoubleV()); + case STRING -> new StringDataEntry(key, proto.getStringV()); + case JSON -> new JsonDataEntry(key, proto.getJsonV()); + }; + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java index c8683972d9..57ba35628c 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java @@ -68,7 +68,7 @@ public class RpcCleanUpService { long ttl = TimeUnit.DAYS.toMillis(tenantProfileConfiguration.get().getRpcTtlDays()); long expirationTime = System.currentTimeMillis() - ttl; - long totalRemoved = rpcDao.deleteOutdatedRpcByTenantId(tenantId, expirationTime); + int totalRemoved = rpcDao.deleteOutdatedRpcByTenantId(tenantId, expirationTime); if (totalRemoved > 0) { log.info("Removed {} outdated rpc(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime)); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java index 38cfb020c0..a8ad9b42c1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java @@ -26,7 +26,7 @@ public enum DataType { JSON(4); @Getter - private final int protoNumber; // Corresponds to EntityTypeProto + private final int protoNumber; // Corresponds to KeyValueType DataType(int protoNumber) { this.protoNumber = protoNumber; diff --git a/common/proto/src/main/java/org/thingsboard/server/common/util/KvProtoUtil.java b/common/proto/src/main/java/org/thingsboard/server/common/util/KvProtoUtil.java index 2532ce00d2..14368693c5 100644 --- a/common/proto/src/main/java/org/thingsboard/server/common/util/KvProtoUtil.java +++ b/common/proto/src/main/java/org/thingsboard/server/common/util/KvProtoUtil.java @@ -45,7 +45,7 @@ public class KvProtoUtil { public static List toAttributeKvList(List dataList) { List result = new ArrayList<>(dataList.size()); - dataList.forEach(proto -> result.add(new BaseAttributeKvEntry(fromProto(proto.getKv()), proto.getTs()))); + dataList.forEach(proto -> result.add(new BaseAttributeKvEntry(fromTsKvProto(proto.getKv()), proto.getTs()))); return result; } @@ -56,44 +56,44 @@ public class KvProtoUtil { } else { clientAttributes = new ArrayList<>(result.size()); for (AttributeKvEntry attrEntry : result) { - clientAttributes.add(toProto(attrEntry.getLastUpdateTs(), attrEntry)); + clientAttributes.add(toTsKvProto(attrEntry.getLastUpdateTs(), attrEntry)); } } return clientAttributes; } - public static List toProtoList(List result) { + public static List toTsKvProtoList(List result) { List ts; if (result == null || result.isEmpty()) { ts = Collections.emptyList(); } else { ts = new ArrayList<>(result.size()); for (TsKvEntry attrEntry : result) { - ts.add(toProto(attrEntry.getTs(), attrEntry)); + ts.add(toTsKvProto(attrEntry.getTs(), attrEntry)); } } return ts; } - public static List fromProtoList(List dataList) { + public static List fromTsKvProtoList(List dataList) { List result = new ArrayList<>(dataList.size()); - dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), fromProto(proto.getKv())))); + dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), fromTsKvProto(proto.getKv())))); return result; } - public static TransportProtos.TsKvProto toProto(long ts, KvEntry kvEntry) { + public static TransportProtos.TsKvProto toTsKvProto(long ts, KvEntry kvEntry) { return TransportProtos.TsKvProto.newBuilder().setTs(ts) - .setKv(KvProtoUtil.toProto(kvEntry)).build(); + .setKv(KvProtoUtil.toKeyValueTypeProto(kvEntry)).build(); } - public static TsKvEntry fromProto(TransportProtos.TsKvProto proto) { - return new BasicTsKvEntry(proto.getTs(), fromProto(proto.getKv())); + public static TsKvEntry fromTsKvProto(TransportProtos.TsKvProto proto) { + return new BasicTsKvEntry(proto.getTs(), fromTsKvProto(proto.getKv())); } - public static TransportProtos.KeyValueProto toProto(KvEntry kvEntry) { + public static TransportProtos.KeyValueProto toKeyValueTypeProto(KvEntry kvEntry) { TransportProtos.KeyValueProto.Builder builder = TransportProtos.KeyValueProto.newBuilder(); builder.setKey(kvEntry.getKey()); - builder.setType(toProto(kvEntry.getDataType())); + builder.setType(toKeyValueTypeProto(kvEntry.getDataType())); switch (kvEntry.getDataType()) { case BOOLEAN -> kvEntry.getBooleanValue().ifPresent(builder::setBoolV); case LONG -> kvEntry.getLongValue().ifPresent(builder::setLongV); @@ -104,8 +104,8 @@ public class KvProtoUtil { return builder.build(); } - public static KvEntry fromProto(TransportProtos.KeyValueProto proto) { - return switch (fromProto(proto.getType())) { + public static KvEntry fromTsKvProto(TransportProtos.KeyValueProto proto) { + return switch (fromKeyValueTypeProto(proto.getType())) { case BOOLEAN -> new BooleanDataEntry(proto.getKey(), proto.getBoolV()); case LONG -> new LongDataEntry(proto.getKey(), proto.getLongV()); case DOUBLE -> new DoubleDataEntry(proto.getKey(), proto.getDoubleV()); @@ -114,11 +114,11 @@ public class KvProtoUtil { }; } - public static TransportProtos.KeyValueType toProto(DataType dataType) { + public static TransportProtos.KeyValueType toKeyValueTypeProto(DataType dataType) { return TransportProtos.KeyValueType.forNumber(dataType.getProtoNumber()); } - public static DataType fromProto(TransportProtos.KeyValueType keyValueType) { + public static DataType fromKeyValueTypeProto(TransportProtos.KeyValueType keyValueType) { return dataTypeByProtoNumber[keyValueType.getNumber()]; } diff --git a/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java b/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java index 0fad4e48a2..1fbfaf52d9 100644 --- a/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java +++ b/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java @@ -75,13 +75,13 @@ class KvProtoUtilTest { @ParameterizedTest @EnumSource(DataType.class) void protoDataTypeSerialization(DataType dataType) { - assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(dataType))).as(dataType.name()).isEqualTo(dataType); + assertThat(KvProtoUtil.fromKeyValueTypeProto(KvProtoUtil.toKeyValueTypeProto(dataType))).as(dataType.name()).isEqualTo(dataType); } @ParameterizedTest @MethodSource("kvEntryData") void protoKeyValueProtoSerialization(KvEntry kvEntry) { - assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(kvEntry))) + assertThat(KvProtoUtil.fromTsKvProto(KvProtoUtil.toKeyValueTypeProto(kvEntry))) .as("deserialized") .isEqualTo(kvEntry); } @@ -89,7 +89,7 @@ class KvProtoUtilTest { @ParameterizedTest @MethodSource("basicTsKvEntryData") void protoTsKvEntrySerialization(KvEntry kvEntry) { - assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(TS, kvEntry))) + assertThat(KvProtoUtil.fromTsKvProto(KvProtoUtil.toTsKvProto(TS, kvEntry))) .as("deserialized") .isEqualTo(kvEntry); } @@ -98,7 +98,7 @@ class KvProtoUtilTest { @ValueSource(booleans = {true, false}) void protoListTsKvEntrySerialization(boolean withAggregation) { List tsKvEntries = createTsKvEntryList(withAggregation); - assertThat(KvProtoUtil.fromProtoList(KvProtoUtil.toProtoList(tsKvEntries))) + assertThat(KvProtoUtil.fromTsKvProtoList(KvProtoUtil.toTsKvProtoList(tsKvEntries))) .as("deserialized") .isEqualTo(tsKvEntries); } diff --git a/common/util/src/main/java/org/thingsboard/common/util/SslUtil.java b/common/util/src/main/java/org/thingsboard/common/util/SslUtil.java index a62870ea9b..1767c745bc 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/SslUtil.java +++ b/common/util/src/main/java/org/thingsboard/common/util/SslUtil.java @@ -104,7 +104,7 @@ public class SslUtil { } private static PrivateKey readPrivateKey(Reader reader, String passStr) throws IOException, PKCSException { - char[] password = StringUtils.isEmpty(passStr) ? EMPTY_PASS : passStr.toCharArray(); + char[] password = getPassword(passStr); PrivateKey privateKey = null; JcaPEMKeyConverter keyConverter = new JcaPEMKeyConverter(); try (PEMParser pemParser = new PEMParser(reader)) { @@ -130,4 +130,8 @@ public class SslUtil { return privateKey; } + public static char[] getPassword(String passStr) { + return StringUtils.isEmpty(passStr) ? EMPTY_PASS : passStr.toCharArray(); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index 4ee812a26b..b91e2ed96d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.entity; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.thingsboard.server.common.data.HasCustomerId; @@ -60,6 +61,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe private EntityQueryDao entityQueryDao; @Autowired + @Lazy EntityServiceRegistry entityServiceRegistry; @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java b/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java index 6e6233b1b2..8202d49db8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java @@ -15,17 +15,14 @@ */ package org.thingsboard.server.dao.entity; +import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.context.ApplicationContext; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.context.event.EventListener; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import java.util.HashMap; +import java.util.List; import java.util.Map; @Service @@ -33,14 +30,13 @@ import java.util.Map; @Slf4j public class DefaultEntityServiceRegistry implements EntityServiceRegistry { - private final ApplicationContext applicationContext; + private final List entityDaoServices; private final Map entityDaoServicesMap = new HashMap<>(); - @EventListener(ContextRefreshedEvent.class) - @Order(Ordered.HIGHEST_PRECEDENCE) + @PostConstruct public void init() { log.debug("Initializing EntityServiceRegistry on ContextRefreshedEvent"); - applicationContext.getBeansOfType(EntityDaoService.class).values().forEach(entityDaoService -> { + entityDaoServices.forEach(entityDaoService -> { EntityType entityType = entityDaoService.getEntityType(); entityDaoServicesMap.put(entityType, entityDaoService); if (EntityType.RULE_CHAIN.equals(entityType)) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java b/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java index 79b73c69aa..d2e867e763 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java @@ -30,5 +30,6 @@ public interface RpcDao extends Dao { PageData findAllRpcByTenantId(TenantId tenantId, PageLink pageLink); - Long deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime); + int deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java index 2c16db4d34..e1939a595c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java @@ -19,6 +19,7 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; @@ -67,8 +68,9 @@ public class JpaRpcDao extends JpaAbstractDao implements RpcDao return DaoUtil.toPageData(rpcRepository.findAllByTenantId(tenantId.getId(), DaoUtil.toPageable(pageLink))); } + @Transactional @Override - public Long deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime) { + public int deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime) { return rpcRepository.deleteOutdatedRpcByTenantId(tenantId.getId(), expirationTime); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java index e04d000a65..fe7c95d249 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.rpc; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.rpc.RpcStatus; @@ -32,7 +33,8 @@ public interface RpcRepository extends JpaRepository { Page findAllByTenantId(UUID tenantId, Pageable pageable); - @Query(value = "WITH deleted AS (DELETE FROM rpc WHERE (tenant_id = :tenantId AND created_time < :expirationTime) IS TRUE RETURNING *) SELECT count(*) FROM deleted", + @Modifying + @Query(value = "DELETE FROM rpc WHERE tenant_id = :tenantId AND created_time < :expirationTime", nativeQuery = true) - Long deleteOutdatedRpcByTenantId(@Param("tenantId") UUID tenantId, @Param("expirationTime") Long expirationTime); + int deleteOutdatedRpcByTenantId(@Param("tenantId") UUID tenantId, @Param("expirationTime") Long expirationTime); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDaoTest.java new file mode 100644 index 0000000000..d2889dfb3c --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDaoTest.java @@ -0,0 +1,59 @@ +/** + * 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.dao.sql.rpc; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rpc.Rpc; +import org.thingsboard.server.common.data.rpc.RpcStatus; +import org.thingsboard.server.dao.AbstractJpaDaoTest; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +public class JpaRpcDaoTest extends AbstractJpaDaoTest { + + @Autowired + JpaRpcDao rpcDao; + + @Test + public void deleteOutdated() { + Rpc rpc = new Rpc(); + rpc.setTenantId(TenantId.SYS_TENANT_ID); + rpc.setDeviceId(new DeviceId(UUID.randomUUID())); + rpc.setStatus(RpcStatus.QUEUED); + rpc.setRequest(JacksonUtil.toJsonNode("{}")); + rpcDao.saveAndFlush(rpc.getTenantId(), rpc); + + rpc.setId(null); + rpcDao.saveAndFlush(rpc.getTenantId(), rpc); + + TenantId tenantId = TenantId.fromUUID(UUID.fromString("3d193a7a-774b-4c05-84d5-f7fdcf7a37cf")); + rpc.setId(null); + rpc.setTenantId(tenantId); + rpc.setDeviceId(new DeviceId(UUID.randomUUID())); + rpcDao.saveAndFlush(rpc.getTenantId(), rpc); + + assertThat(rpcDao.deleteOutdatedRpcByTenantId(TenantId.SYS_TENANT_ID, 0L)).isEqualTo(0); + assertThat(rpcDao.deleteOutdatedRpcByTenantId(TenantId.SYS_TENANT_ID, Long.MAX_VALUE)).isEqualTo(2); + assertThat(rpcDao.deleteOutdatedRpcByTenantId(tenantId, System.currentTimeMillis() + 1)).isEqualTo(1); + } + +} diff --git a/dao/src/test/resources/logback.xml b/dao/src/test/resources/logback.xml index 61397ec6f1..5e293b2982 100644 --- a/dao/src/test/resources/logback.xml +++ b/dao/src/test/resources/logback.xml @@ -10,6 +10,9 @@ + + + diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/credentials/CertPemCredentials.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/credentials/CertPemCredentials.java index 958957a3e2..40d899412d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/credentials/CertPemCredentials.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/credentials/CertPemCredentials.java @@ -87,7 +87,7 @@ public class CertPemCredentials implements ClientCredentials { private KeyManagerFactory createAndInitKeyManagerFactory() throws Exception { KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - kmf.init(loadKeyStore(), password.toCharArray()); + kmf.init(loadKeyStore(), SslUtil.getPassword(password)); return kmf; } @@ -107,7 +107,7 @@ public class CertPemCredentials implements ClientCredentials { CertPath certPath = factory.generateCertPath(certificates); List path = certPath.getCertificates(); Certificate[] x509Certificates = path.toArray(new Certificate[0]); - keyStore.setKeyEntry(PRIVATE_KEY_ALIAS, privateKey, password.toCharArray(), x509Certificates); + keyStore.setKeyEntry(PRIVATE_KEY_ALIAS, privateKey, SslUtil.getPassword(password), x509Certificates); } return keyStore; } diff --git a/ui-ngx/.eslintrc.json b/ui-ngx/.eslintrc.json index cf9f0e8238..764733e4d7 100644 --- a/ui-ngx/.eslintrc.json +++ b/ui-ngx/.eslintrc.json @@ -51,7 +51,8 @@ "import/order": "off", "@typescript-eslint/member-ordering": "off", "no-underscore-dangle": "off", - "@typescript-eslint/naming-convention": "off" + "@typescript-eslint/naming-convention": "off", + "jsdoc/newline-after-description": 0 } }, { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.scss index 3425065ca2..3f66b67943 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.scss @@ -63,7 +63,9 @@ height: 18px; background: #305680; mask-image: url(/assets/copy-code-icon.svg); + -webkit-mask-image: url(/assets/copy-code-icon.svg); mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.scss index 444417fb09..a85d7d694b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.scss @@ -73,8 +73,11 @@ position: absolute; inset: 0; mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; mask-size: cover; + -webkit-mask-size: cover; mask-position: center; + -webkit-mask-position: center; } .tb-battery-level-container { position: absolute; @@ -95,6 +98,7 @@ &.vertical { .tb-battery-level-shape { mask-image: url(/assets/widget/battery-level/battery-shape-vertical.svg); + -webkit-mask-image: url(/assets/widget/battery-level/battery-shape-vertical.svg); } .tb-battery-level-container { flex-direction: column-reverse; @@ -122,6 +126,7 @@ &.horizontal { .tb-battery-level-shape { mask-image: url(/assets/widget/battery-level/battery-shape-horizontal.svg); + -webkit-mask-image: url(/assets/widget/battery-level/battery-shape-horizontal.svg); } .tb-battery-level-container { inset: 6.25% 8.85% 6.25% 3.54%; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts index 51a427d23b..656ba75fa1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts @@ -444,7 +444,7 @@ class InnerShadowCircle { add.x('-50%').y('-50%').width('200%').height('200%'); let effect: Effect = add.componentTransfer(components => { components.funcA({ type: 'table', tableValues: '1 0' }); - }).in(add.$fill); + }); effect = effect.gaussianBlur(this.blur, this.blur).attr({stdDeviation: this.blur}); this.blurEffect = effect; effect = effect.offset(this.dx, this.dy); @@ -454,7 +454,7 @@ class InnerShadowCircle { effect = effect.composite(this.offsetEffect, 'in'); effect.composite(add.$sourceAlpha, 'in'); add.merge(m => { - m.mergeNode(add.$fill); + m.mergeNode(); m.mergeNode(); }); }); @@ -538,14 +538,14 @@ class DefaultPowerButtonShape extends PowerButtonShape { this.pressedTimeline.finish(); const pressedScale = 0.75; powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); - powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale, origin: {x: cx, y: cy}}); this.pressedShadow.animate(6, 0.6); } protected onPressEnd() { this.pressedTimeline.finish(); powerButtonAnimation(this.centerGroup).transform({scale: 1}); - powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1, origin: {x: cx, y: cy}}); this.pressedShadow.animateRestore(); } @@ -602,14 +602,14 @@ class SimplifiedPowerButtonShape extends PowerButtonShape { this.pressedTimeline.finish(); const pressedScale = 0.75; powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); - powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale, origin: {x: cx, y: cy}}); this.pressedShadow.animate(6, 0.6); } protected onPressEnd() { this.pressedTimeline.finish(); powerButtonAnimation(this.centerGroup).transform({scale: 1}); - powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1, origin: {x: cx, y: cy}}); this.pressedShadow.animateRestore(); } } @@ -674,7 +674,7 @@ class OutlinedPowerButtonShape extends PowerButtonShape { const pressedScale = 0.75; powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); powerButtonAnimation(this.onCenterGroup).transform({scale: 0.98}); - powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale / 0.98}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale / 0.98, origin: {x: cx, y: cy}}); this.pressedShadow.animate(6, 0.6); } @@ -682,7 +682,7 @@ class OutlinedPowerButtonShape extends PowerButtonShape { this.pressedTimeline.finish(); powerButtonAnimation(this.centerGroup).transform({scale: 1}); powerButtonAnimation(this.onCenterGroup).transform({scale: 1}); - powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1, origin: {x: cx, y: cy}}); this.pressedShadow.animateRestore(); } } @@ -774,14 +774,14 @@ class DefaultVolumePowerButtonShape extends PowerButtonShape { this.innerShadow.show(); const pressedScale = 0.75; powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); - powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale, origin: {x: cx, y: cy}}); this.innerShadow.animate(6, 0.6); } protected onPressEnd() { this.pressedTimeline.finish(); powerButtonAnimation(this.centerGroup).transform({scale: 1}); - powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1, origin: {x: cx, y: cy}}); this.innerShadow.animateRestore().after(() => { if (this.disabled) { this.innerShadow.hide(); @@ -849,14 +849,14 @@ class SimplifiedVolumePowerButtonShape extends PowerButtonShape { this.backgroundShape.removeClass('tb-shadow'); } powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); - powerButtonAnimation(this.onCenterGroup).transform({scale: pressedScale}); + powerButtonAnimation(this.onCenterGroup).transform({scale: pressedScale, origin: {x: cx, y: cy}}); this.pressedShadow.animate(8, 0.4); } protected onPressEnd() { this.pressedTimeline.finish(); powerButtonAnimation(this.centerGroup).transform({scale: 1}); - powerButtonAnimation(this.onCenterGroup).transform({scale: 1}); + powerButtonAnimation(this.onCenterGroup).transform({scale: 1, origin: {x: cx, y: cy}}); this.pressedShadow.animateRestore().after(() => { if (!this.value) { this.backgroundShape.addClass('tb-shadow'); @@ -938,7 +938,7 @@ class OutlinedVolumePowerButtonShape extends PowerButtonShape { const pressedScale = 0.75; powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); powerButtonAnimation(this.onCenterGroup).transform({scale: 0.98}); - powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale / 0.98}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale / 0.98, origin: {x: cx, y: cy}}); this.pressedShadow.animate(6, 0.6); } @@ -946,7 +946,7 @@ class OutlinedVolumePowerButtonShape extends PowerButtonShape { this.pressedTimeline.finish(); powerButtonAnimation(this.centerGroup).transform({scale: 1}); powerButtonAnimation(this.onCenterGroup).transform({scale: 1}); - powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1, origin: {x: cx, y: cy}}); this.pressedShadow.animateRestore(); } diff --git a/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.scss index f7ed839541..ef5a1c0ff4 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.scss @@ -155,7 +155,9 @@ height: 18px; background: #305680; mask-image: url(/assets/copy-code-icon.svg); + -webkit-mask-image: url(/assets/copy-code-icon.svg); mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; } } } diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.scss index 4ff80e2c21..b28b28863e 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.scss @@ -92,7 +92,9 @@ height: 18px; background: $tb-primary-color; mask-image: url(/assets/copy-code-icon.svg); + -webkit-mask-image: url(/assets/copy-code-icon.svg); mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; } } &.multiline { diff --git a/ui-ngx/src/app/shared/components/time/timeinterval.component.ts b/ui-ngx/src/app/shared/components/time/timeinterval.component.ts index 8775b0a6ed..8ed6fb2034 100644 --- a/ui-ngx/src/app/shared/components/time/timeinterval.component.ts +++ b/ui-ngx/src/app/shared/components/time/timeinterval.component.ts @@ -21,6 +21,7 @@ import { coerceNumberProperty } from '@angular/cdk/coercion'; import { SubscriptSizing } from '@angular/material/form-field'; import { coerceBoolean } from '@shared/decorators/coercion'; import { Interval, IntervalMath, TimeInterval } from '@shared/models/time/time.models'; +import { isDefined } from '@core/utils'; @Component({ selector: 'tb-timeinterval', @@ -90,14 +91,17 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { secs = 0; interval: Interval = 0; - modelValue: Interval; + intervals: Array; advanced = false; - rendered = false; - intervals: Array; + private modelValue: Interval; + private rendered = false; + private propagateChangeValue: any; - private propagateChange = (_: any) => {}; + private propagateChange = (value: any) => { + this.propagateChangeValue = value; + }; constructor(private timeService: TimeService) { } @@ -108,6 +112,9 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { registerOnChange(fn: any): void { this.propagateChange = fn; + if (isDefined(this.propagateChangeValue)) { + this.propagateChange(this.propagateChangeValue); + } } registerOnTouched(fn: any): void { @@ -132,7 +139,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { } } - setInterval(interval: Interval) { + private setInterval(interval: Interval) { if (!this.advanced) { this.interval = interval; } @@ -143,7 +150,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { this.secs = intervalSeconds % 60; } - boundInterval(updateToPreferred = false) { + private boundInterval(updateToPreferred = false) { const min = this.timeService.boundMinInterval(this.minValue); const max = this.timeService.boundMaxInterval(this.maxValue); this.intervals = this.timeService.getIntervals(this.minValue, this.maxValue, this.useCalendarIntervals); @@ -165,7 +172,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { } } - updateView(updateToPreferred = false) { + private updateView(updateToPreferred = false) { if (!this.rendered) { return; } @@ -187,7 +194,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { this.boundInterval(updateToPreferred); } - calculateIntervalMs(): number { + private calculateIntervalMs(): number { return (this.days * 86400 + this.hours * 3600 + this.mins * 60 + @@ -232,7 +239,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { } } - onSecsChange() { + private onSecsChange() { if (typeof this.secs === 'undefined') { return; } @@ -252,7 +259,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { this.updateView(); } - onMinsChange() { + private onMinsChange() { if (typeof this.mins === 'undefined') { return; } @@ -272,7 +279,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { this.updateView(); } - onHoursChange() { + private onHoursChange() { if (typeof this.hours === 'undefined') { return; } @@ -292,7 +299,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { this.updateView(); } - onDaysChange() { + private onDaysChange() { if (typeof this.days === 'undefined') { return; } diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts index 0c0dd4294c..857d2c7cb7 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts @@ -20,7 +20,6 @@ import { AggregationType, DAY, HistoryWindowType, - QuickTimeInterval, quickTimeIntervalPeriod, RealtimeWindowType, Timewindow, diff --git a/ui-ngx/src/form.scss b/ui-ngx/src/form.scss index e2390c795d..f4d3cdb398 100644 --- a/ui-ngx/src/form.scss +++ b/ui-ngx/src/form.scss @@ -588,9 +588,13 @@ bottom: 0; background: $tb-primary-color; mask-image: url(/assets/home/no_data_folder_bg.svg); + -webkit-mask-image: url(/assets/home/no_data_folder_bg.svg); mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; mask-size: contain; + -webkit-mask-size: contain; mask-position: center; + -webkit-mask-position: center; } }