Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard into feature/firmware

pull/4465/head
Andrii Shvaika 6 years ago
parent
commit
53bf6af23b
  1. 8
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  2. 4
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
  3. 2
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  4. 2
      application/src/main/java/org/thingsboard/server/controller/CustomerController.java
  5. 2
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  6. 2
      application/src/main/java/org/thingsboard/server/controller/RpcController.java
  7. 3
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  8. 148
      application/src/main/java/org/thingsboard/server/service/apiusage/BaseApiUsageState.java
  9. 30
      application/src/main/java/org/thingsboard/server/service/apiusage/CustomerApiUsageState.java
  10. 253
      application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
  11. 5
      application/src/main/java/org/thingsboard/server/service/apiusage/TbApiUsageStateService.java
  12. 142
      application/src/main/java/org/thingsboard/server/service/apiusage/TenantApiUsageState.java
  13. 4
      application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java
  14. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  15. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceProcessor.java
  16. 20
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryProcessor.java
  17. 2
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  18. 5
      application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
  19. 1
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java
  20. 12
      application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java
  21. 9
      application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java
  22. 7
      application/src/main/java/org/thingsboard/server/service/rpc/TbCoreDeviceRpcService.java
  23. 5
      application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java
  24. 4
      application/src/main/java/org/thingsboard/server/service/script/JsInvokeService.java
  25. 4
      application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java
  26. 5
      application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsService.java
  27. 3
      application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
  28. 2
      application/src/main/java/org/thingsboard/server/service/state/DeviceStateData.java
  29. 10
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
  30. 5
      application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
  31. 1
      application/src/main/resources/thingsboard.yml
  32. 4
      application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java
  33. 7
      common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateService.java
  34. 5
      common/data/src/main/java/org/thingsboard/server/common/data/query/ApiUsageStateFilter.java
  35. 77
      common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java
  36. 3
      common/message/src/main/proto/tbmsg.proto
  37. 116
      common/queue/src/main/java/org/thingsboard/server/queue/usagestats/DefaultTbApiUsageClient.java
  38. 5
      common/queue/src/main/java/org/thingsboard/server/queue/usagestats/TbApiUsageClient.java
  39. 6
      common/queue/src/main/proto/queue.proto
  40. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContextServer.java
  41. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java
  42. 2
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java
  43. 2
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/SessionInfoCreator.java
  44. 2
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/TransportDeviceInfo.java
  45. 56
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java
  46. 5
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
  47. 22
      dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java
  48. 6
      dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/ApiUsageStateRepository.java
  49. 11
      dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/JpaApiUsageStateDao.java
  50. 2
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java
  51. 5
      dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateDao.java
  52. 52
      dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java
  53. 3
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java
  54. 3
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java
  55. 3
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/SmsService.java
  56. 5
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java
  57. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
  58. 10
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java
  59. 8
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java
  60. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java
  61. 7
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java
  62. 15
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java
  63. 8
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java
  64. 8
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java
  65. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/sms/TbSendSmsNode.java
  66. 3
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java
  67. 18
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java

8
application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java

@ -41,6 +41,7 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
@ -269,7 +270,12 @@ class DefaultTbContext implements TbContext {
@Override @Override
public TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) { public TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) {
return TbMsg.newMsg(queueName, type, originator, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId()); return newMsg(queueName, type, originator, null, metaData, data);
}
@Override
public TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) {
return TbMsg.newMsg(queueName, type, originator, customerId, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId());
} }
@Override @Override

4
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java

@ -106,7 +106,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
int ruleNodeCount = tbMsg.getAndIncrementRuleNodeCounter(); int ruleNodeCount = tbMsg.getAndIncrementRuleNodeCounter();
int maxRuleNodeExecutionsPerMessage = getTenantProfileConfiguration().getMaxRuleNodeExecsPerMessage(); int maxRuleNodeExecutionsPerMessage = getTenantProfileConfiguration().getMaxRuleNodeExecsPerMessage();
if (maxRuleNodeExecutionsPerMessage == 0 || ruleNodeCount < maxRuleNodeExecutionsPerMessage) { if (maxRuleNodeExecutionsPerMessage == 0 || ruleNodeCount < maxRuleNodeExecutionsPerMessage) {
apiUsageClient.report(tenantId, ApiUsageRecordKey.RE_EXEC_COUNT); apiUsageClient.report(tenantId, tbMsg.getCustomerId(), ApiUsageRecordKey.RE_EXEC_COUNT);
if (ruleNode.isDebugMode()) { if (ruleNode.isDebugMode()) {
systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), "Self"); systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), "Self");
} }
@ -127,7 +127,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
int ruleNodeCount = tbMsg.getAndIncrementRuleNodeCounter(); int ruleNodeCount = tbMsg.getAndIncrementRuleNodeCounter();
int maxRuleNodeExecutionsPerMessage = getTenantProfileConfiguration().getMaxRuleNodeExecsPerMessage(); int maxRuleNodeExecutionsPerMessage = getTenantProfileConfiguration().getMaxRuleNodeExecsPerMessage();
if (maxRuleNodeExecutionsPerMessage == 0 || ruleNodeCount < maxRuleNodeExecutionsPerMessage) { if (maxRuleNodeExecutionsPerMessage == 0 || ruleNodeCount < maxRuleNodeExecutionsPerMessage) {
apiUsageClient.report(tenantId, ApiUsageRecordKey.RE_EXEC_COUNT); apiUsageClient.report(tenantId, tbMsg.getCustomerId(), ApiUsageRecordKey.RE_EXEC_COUNT);
if (ruleNode.isDebugMode()) { if (ruleNode.isDebugMode()) {
systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), msg.getFromRelationType()); systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), msg.getFromRelationType());
} }

2
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -958,7 +958,7 @@ public abstract class BaseController {
entityNode.put("endTs", extractParameter(Long.class, 2, additionalInfo)); entityNode.put("endTs", extractParameter(Long.class, 2, additionalInfo));
} }
} }
TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode)); TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode));
TenantId tenantId = user.getTenantId(); TenantId tenantId = user.getTenantId();
if (tenantId.isNullUid()) { if (tenantId.isNullUid()) {
if (entity instanceof HasTenantId) { if (entity instanceof HasTenantId) {

2
application/src/main/java/org/thingsboard/server/controller/CustomerController.java

@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.permission.Resource;
@ -148,6 +149,7 @@ public class CustomerController extends BaseController {
ActionType.DELETED, null, strCustomerId); ActionType.DELETED, null, strCustomerId);
sendDeleteNotificationMsg(getTenantId(), customerId, relatedEdgeIds); sendDeleteNotificationMsg(getTenantId(), customerId, relatedEdgeIds);
tbClusterService.onEntityStateChange(getTenantId(), customerId, ComponentLifecycleEvent.DELETED);
} catch (Exception e) { } catch (Exception e) {
logEntityAction(emptyId(EntityType.CUSTOMER), logEntityAction(emptyId(EntityType.CUSTOMER),

2
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -658,7 +658,7 @@ public class DeviceController extends BaseController {
private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) { private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) {
String data = entityToStr(assignedDevice); String data = entityToStr(assignedDevice);
if (data != null) { if (data != null) {
TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_ASSIGNED_FROM_TENANT, assignedDevice.getId(), getMetaDataForAssignedFrom(currentTenant), TbMsgDataType.JSON, data); TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_ASSIGNED_FROM_TENANT, assignedDevice.getId(), assignedDevice.getCustomerId(), getMetaDataForAssignedFrom(currentTenant), TbMsgDataType.JSON, data);
tbClusterService.pushMsgToRuleEngine(newTenantId, assignedDevice.getId(), tbMsg, null); tbClusterService.pushMsgToRuleEngine(newTenantId, assignedDevice.getId(), tbMsg, null);
} }
} }

2
application/src/main/java/org/thingsboard/server/controller/RpcController.java

@ -119,7 +119,7 @@ public class RpcController extends BaseController {
expTime, expTime,
body body
); );
deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse)); deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse), currentUser);
} }
@Override @Override

3
application/src/main/java/org/thingsboard/server/controller/TelemetryController.java

@ -48,6 +48,7 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
@ -449,7 +450,7 @@ public class TelemetryController extends BaseController {
TenantProfile tenantProfile = tenantProfileCache.get(tenantId); TenantProfile tenantProfile = tenantProfileCache.get(tenantId);
tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays()); tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays());
} }
tsSubService.saveAndNotify(tenantId, entityId, entries, tenantTtl, new FutureCallback<Void>() { tsSubService.saveAndNotify(tenantId, user.getCustomerId(), entityId, entries, tenantTtl, new FutureCallback<Void>() {
@Override @Override
public void onSuccess(@Nullable Void tmp) { public void onSuccess(@Nullable Void tmp) {
logTelemetryUpdated(user, entityId, entries, null); logTelemetryUpdated(user, entityId, entries, null);

148
application/src/main/java/org/thingsboard/server/service/apiusage/BaseApiUsageState.java

@ -0,0 +1,148 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.apiusage;
import lombok.Getter;
import org.springframework.data.util.Pair;
import org.thingsboard.server.common.data.ApiFeature;
import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.ApiUsageStateValue;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.tools.SchedulerUtils;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public abstract class BaseApiUsageState {
private final Map<ApiUsageRecordKey, Long> currentCycleValues = new ConcurrentHashMap<>();
private final Map<ApiUsageRecordKey, Long> currentHourValues = new ConcurrentHashMap<>();
@Getter
private final ApiUsageState apiUsageState;
@Getter
private volatile long currentCycleTs;
@Getter
private volatile long nextCycleTs;
@Getter
private volatile long currentHourTs;
public BaseApiUsageState(ApiUsageState apiUsageState) {
this.apiUsageState = apiUsageState;
this.currentCycleTs = SchedulerUtils.getStartOfCurrentMonth();
this.nextCycleTs = SchedulerUtils.getStartOfNextMonth();
this.currentHourTs = SchedulerUtils.getStartOfCurrentHour();
}
public void put(ApiUsageRecordKey key, Long value) {
currentCycleValues.put(key, value);
}
public void putHourly(ApiUsageRecordKey key, Long value) {
currentHourValues.put(key, value);
}
public long add(ApiUsageRecordKey key, long value) {
long result = currentCycleValues.getOrDefault(key, 0L) + value;
currentCycleValues.put(key, result);
return result;
}
public long get(ApiUsageRecordKey key) {
return currentCycleValues.getOrDefault(key, 0L);
}
public long addToHourly(ApiUsageRecordKey key, long value) {
long result = currentHourValues.getOrDefault(key, 0L) + value;
currentHourValues.put(key, result);
return result;
}
public void setHour(long currentHourTs) {
this.currentHourTs = currentHourTs;
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
currentHourValues.put(key, 0L);
}
}
public void setCycles(long currentCycleTs, long nextCycleTs) {
this.currentCycleTs = currentCycleTs;
this.nextCycleTs = nextCycleTs;
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
currentCycleValues.put(key, 0L);
}
}
public ApiUsageStateValue getFeatureValue(ApiFeature feature) {
switch (feature) {
case TRANSPORT:
return apiUsageState.getTransportState();
case RE:
return apiUsageState.getReExecState();
case DB:
return apiUsageState.getDbStorageState();
case JS:
return apiUsageState.getJsExecState();
case EMAIL:
return apiUsageState.getEmailExecState();
case SMS:
return apiUsageState.getSmsExecState();
default:
return ApiUsageStateValue.ENABLED;
}
}
public boolean setFeatureValue(ApiFeature feature, ApiUsageStateValue value) {
ApiUsageStateValue currentValue = getFeatureValue(feature);
switch (feature) {
case TRANSPORT:
apiUsageState.setTransportState(value);
break;
case RE:
apiUsageState.setReExecState(value);
break;
case DB:
apiUsageState.setDbStorageState(value);
break;
case JS:
apiUsageState.setJsExecState(value);
break;
case EMAIL:
apiUsageState.setEmailExecState(value);
break;
case SMS:
apiUsageState.setSmsExecState(value);
break;
}
return !currentValue.equals(value);
}
public abstract EntityType getEntityType();
public TenantId getTenantId() {
return getApiUsageState().getTenantId();
}
public EntityId getEntityId() {
return getApiUsageState().getEntityId();
}
}

30
application/src/main/java/org/thingsboard/server/service/apiusage/CustomerApiUsageState.java

@ -0,0 +1,30 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.apiusage;
import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.EntityType;
public class CustomerApiUsageState extends BaseApiUsageState {
public CustomerApiUsageState(ApiUsageState apiUsageState) {
super(apiUsageState);
}
@Override
public EntityType getEntityType() {
return EntityType.CUSTOMER;
}
}

253
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java

@ -29,10 +29,13 @@ import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.ApiUsageStateMailMessage; import org.thingsboard.server.common.data.ApiUsageStateMailMessage;
import org.thingsboard.server.common.data.ApiUsageStateValue; import org.thingsboard.server.common.data.ApiUsageStateValue;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.ApiUsageStateId; import org.thingsboard.server.common.data.id.ApiUsageStateId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
@ -45,6 +48,7 @@ import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.tools.SchedulerUtils; import org.thingsboard.server.common.msg.tools.SchedulerUtils;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.timeseries.TimeseriesService;
@ -63,6 +67,7 @@ import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -95,6 +100,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
private final TbClusterService clusterService; private final TbClusterService clusterService;
private final PartitionService partitionService; private final PartitionService partitionService;
private final TenantService tenantService; private final TenantService tenantService;
private final CustomerService customerService;
private final TimeseriesService tsService; private final TimeseriesService tsService;
private final ApiUsageStateService apiUsageStateService; private final ApiUsageStateService apiUsageStateService;
private final SchedulerComponent scheduler; private final SchedulerComponent scheduler;
@ -105,10 +111,12 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
@Autowired @Autowired
private InternalTelemetryService tsWsService; private InternalTelemetryService tsWsService;
// Tenants that should be processed on this server // Entities that should be processed on this server
private final Map<TenantId, TenantApiUsageState> myTenantStates = new ConcurrentHashMap<>(); private final Map<EntityId, BaseApiUsageState> myUsageStates = new ConcurrentHashMap<>();
// Tenants that should be processed on other servers // Entities that should be processed on other servers
private final Map<TenantId, ApiUsageState> otherTenantStates = new ConcurrentHashMap<>(); private final Map<EntityId, ApiUsageState> otherUsageStates = new ConcurrentHashMap<>();
private final Set<EntityId> deletedEntities = Collections.newSetFromMap(new ConcurrentHashMap<>());
@Value("${usage.stats.report.enabled:true}") @Value("${usage.stats.report.enabled:true}")
private boolean enabled; private boolean enabled;
@ -123,13 +131,16 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
public DefaultTbApiUsageStateService(TbClusterService clusterService, public DefaultTbApiUsageStateService(TbClusterService clusterService,
PartitionService partitionService, PartitionService partitionService,
TenantService tenantService, TenantService tenantService,
CustomerService customerService,
TimeseriesService tsService, TimeseriesService tsService,
ApiUsageStateService apiUsageStateService, ApiUsageStateService apiUsageStateService,
SchedulerComponent scheduler, SchedulerComponent scheduler,
TbTenantProfileCache tenantProfileCache, MailService mailService) { TbTenantProfileCache tenantProfileCache,
MailService mailService) {
this.clusterService = clusterService; this.clusterService = clusterService;
this.partitionService = partitionService; this.partitionService = partitionService;
this.tenantService = tenantService; this.tenantService = tenantService;
this.customerService = customerService;
this.tsService = tsService; this.tsService = tsService;
this.apiUsageStateService = apiUsageStateService; this.apiUsageStateService = apiUsageStateService;
this.scheduler = scheduler; this.scheduler = scheduler;
@ -150,74 +161,92 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
@Override @Override
public void process(TbProtoQueueMsg<ToUsageStatsServiceMsg> msg, TbCallback callback) { public void process(TbProtoQueueMsg<ToUsageStatsServiceMsg> msg, TbCallback callback) {
ToUsageStatsServiceMsg statsMsg = msg.getValue(); ToUsageStatsServiceMsg statsMsg = msg.getValue();
TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB()));
if (tenantProfileCache.get(tenantId) == null) { TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB()));
return; EntityId entityId;
if (statsMsg.getCustomerIdMSB() != 0 && statsMsg.getCustomerIdLSB() != 0) {
entityId = new CustomerId(new UUID(statsMsg.getCustomerIdMSB(), statsMsg.getCustomerIdLSB()));
} else {
entityId = tenantId;
} }
TenantApiUsageState tenantState; processEntityUsageStats(tenantId, entityId, statsMsg.getValuesList());
callback.onSuccess();
}
private void processEntityUsageStats(TenantId tenantId, EntityId entityId, List<UsageStatsKVProto> values) {
if (deletedEntities.contains(entityId)) return;
BaseApiUsageState usageState;
List<TsKvEntry> updatedEntries; List<TsKvEntry> updatedEntries;
Map<ApiFeature, ApiUsageStateValue> result; Map<ApiFeature, ApiUsageStateValue> result;
updateLock.lock(); updateLock.lock();
try { try {
tenantState = getOrFetchState(tenantId); usageState = getOrFetchState(tenantId, entityId);
long ts = tenantState.getCurrentCycleTs(); long ts = usageState.getCurrentCycleTs();
long hourTs = tenantState.getCurrentHourTs(); long hourTs = usageState.getCurrentHourTs();
long newHourTs = SchedulerUtils.getStartOfCurrentHour(); long newHourTs = SchedulerUtils.getStartOfCurrentHour();
if (newHourTs != hourTs) { if (newHourTs != hourTs) {
tenantState.setHour(newHourTs); usageState.setHour(newHourTs);
} }
updatedEntries = new ArrayList<>(ApiUsageRecordKey.values().length); updatedEntries = new ArrayList<>(ApiUsageRecordKey.values().length);
Set<ApiFeature> apiFeatures = new HashSet<>(); Set<ApiFeature> apiFeatures = new HashSet<>();
for (UsageStatsKVProto kvProto : statsMsg.getValuesList()) { for (UsageStatsKVProto kvProto : values) {
ApiUsageRecordKey recordKey = ApiUsageRecordKey.valueOf(kvProto.getKey()); ApiUsageRecordKey recordKey = ApiUsageRecordKey.valueOf(kvProto.getKey());
long newValue = tenantState.add(recordKey, kvProto.getValue()); long newValue = usageState.add(recordKey, kvProto.getValue());
updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.getApiCountKey(), newValue))); updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.getApiCountKey(), newValue)));
long newHourlyValue = tenantState.addToHourly(recordKey, kvProto.getValue()); long newHourlyValue = usageState.addToHourly(recordKey, kvProto.getValue());
updatedEntries.add(new BasicTsKvEntry(newHourTs, new LongDataEntry(recordKey.getApiCountKey() + HOURLY, newHourlyValue))); updatedEntries.add(new BasicTsKvEntry(newHourTs, new LongDataEntry(recordKey.getApiCountKey() + HOURLY, newHourlyValue)));
apiFeatures.add(recordKey.getApiFeature()); apiFeatures.add(recordKey.getApiFeature());
} }
result = tenantState.checkStateUpdatedDueToThreshold(apiFeatures); if (usageState.getEntityType() == EntityType.TENANT && !usageState.getEntityId().equals(TenantId.SYS_TENANT_ID)) {
result = ((TenantApiUsageState) usageState).checkStateUpdatedDueToThreshold(apiFeatures);
} else {
result = Collections.emptyMap();
}
} finally { } finally {
updateLock.unlock(); updateLock.unlock();
} }
tsWsService.saveAndNotifyInternal(tenantId, tenantState.getApiUsageState().getId(), updatedEntries, VOID_CALLBACK); tsWsService.saveAndNotifyInternal(tenantId, usageState.getApiUsageState().getId(), updatedEntries, VOID_CALLBACK);
if (!result.isEmpty()) { if (!result.isEmpty()) {
persistAndNotify(tenantState, result); persistAndNotify(usageState, result);
} }
callback.onSuccess();
} }
@Override @Override
protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) { protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
if (partitionChangeEvent.getServiceType().equals(ServiceType.TB_CORE)) { if (partitionChangeEvent.getServiceType().equals(ServiceType.TB_CORE)) {
myTenantStates.entrySet().removeIf(entry -> !partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); myUsageStates.entrySet().removeIf(entry -> {
otherTenantStates.entrySet().removeIf(entry -> partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); return !partitionService.resolve(ServiceType.TB_CORE, entry.getValue().getTenantId(), entry.getKey()).isMyPartition();
});
otherUsageStates.entrySet().removeIf(entry -> {
return partitionService.resolve(ServiceType.TB_CORE, entry.getValue().getTenantId(), entry.getKey()).isMyPartition();
});
initStatesFromDataBase(); initStatesFromDataBase();
} }
} }
@Override @Override
public ApiUsageState getApiUsageState(TenantId tenantId) { public ApiUsageState getApiUsageState(TenantId tenantId) {
TenantApiUsageState tenantState = myTenantStates.get(tenantId); TenantApiUsageState tenantState = (TenantApiUsageState) myUsageStates.get(tenantId);
if (tenantState != null) { if (tenantState != null) {
return tenantState.getApiUsageState(); return tenantState.getApiUsageState();
} else { } else {
ApiUsageState state = otherTenantStates.get(tenantId); ApiUsageState state = otherUsageStates.get(tenantId);
if (state != null) { if (state != null) {
return state; return state;
} else { } else {
if (partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) { if (partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) {
return getOrFetchState(tenantId).getApiUsageState(); return getOrFetchState(tenantId, tenantId).getApiUsageState();
} else { } else {
updateLock.lock(); updateLock.lock();
try { try {
state = otherTenantStates.get(tenantId); state = otherUsageStates.get(tenantId);
if (state == null) { if (state == null) {
state = apiUsageStateService.findTenantApiUsageState(tenantId); state = apiUsageStateService.findTenantApiUsageState(tenantId);
if (state != null) { if (state != null) {
otherTenantStates.put(tenantId, state); otherUsageStates.put(tenantId, state);
} }
} }
} finally { } finally {
@ -231,7 +260,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
@Override @Override
public void onApiUsageStateUpdate(TenantId tenantId) { public void onApiUsageStateUpdate(TenantId tenantId) {
otherTenantStates.remove(tenantId); otherUsageStates.remove(tenantId);
} }
@Override @Override
@ -240,11 +269,14 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
TenantProfile tenantProfile = tenantProfileCache.get(tenantProfileId); TenantProfile tenantProfile = tenantProfileCache.get(tenantProfileId);
updateLock.lock(); updateLock.lock();
try { try {
myTenantStates.values().forEach(state -> { myUsageStates.values().stream()
if (tenantProfile.getId().equals(state.getTenantProfileId())) { .filter(state -> state.getEntityType() == EntityType.TENANT)
updateTenantState(state, tenantProfile); .map(state -> (TenantApiUsageState) state)
} .forEach(state -> {
}); if (tenantProfile.getId().equals(state.getTenantProfileId())) {
updateTenantState(state, tenantProfile);
}
});
} finally { } finally {
updateLock.unlock(); updateLock.unlock();
} }
@ -256,7 +288,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
TenantProfile tenantProfile = tenantProfileCache.get(tenantId); TenantProfile tenantProfile = tenantProfileCache.get(tenantId);
updateLock.lock(); updateLock.lock();
try { try {
TenantApiUsageState state = myTenantStates.get(tenantId); TenantApiUsageState state = (TenantApiUsageState) myUsageStates.get(tenantId);
if (state != null && !state.getTenantProfileId().equals(tenantProfile.getId())) { if (state != null && !state.getTenantProfileId().equals(tenantProfile.getId())) {
updateTenantState(state, tenantProfile); updateTenantState(state, tenantProfile);
} }
@ -293,29 +325,42 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
} }
} }
private void persistAndNotify(TenantApiUsageState state, Map<ApiFeature, ApiUsageStateValue> result) { public void onTenantDelete(TenantId tenantId) {
log.info("[{}] Detected update of the API state: {}", state.getTenantId(), result); deletedEntities.add(tenantId);
myUsageStates.remove(tenantId);
otherUsageStates.remove(tenantId);
}
@Override
public void onCustomerDelete(CustomerId customerId) {
deletedEntities.add(customerId);
myUsageStates.remove(customerId);
}
private void persistAndNotify(BaseApiUsageState state, Map<ApiFeature, ApiUsageStateValue> result) {
log.info("[{}] Detected update of the API state for {}: {}", state.getEntityId(), state.getEntityType(), result);
apiUsageStateService.update(state.getApiUsageState()); apiUsageStateService.update(state.getApiUsageState());
clusterService.onApiStateChange(state.getApiUsageState(), null); clusterService.onApiStateChange(state.getApiUsageState(), null);
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
List<TsKvEntry> stateTelemetry = new ArrayList<>(); List<TsKvEntry> stateTelemetry = new ArrayList<>();
result.forEach(((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name()))))); result.forEach((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name()))));
tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK); tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK);
String email = tenantService.findTenantById(state.getTenantId()).getEmail(); if (state.getEntityType() == EntityType.TENANT && !state.getEntityId().equals(TenantId.SYS_TENANT_ID)) {
String email = tenantService.findTenantById(state.getTenantId()).getEmail();
if (StringUtils.isNotEmpty(email)) { if (StringUtils.isNotEmpty(email)) {
result.forEach((apiFeature, stateValue) -> { result.forEach((apiFeature, stateValue) -> {
mailExecutor.submit(() -> { mailExecutor.submit(() -> {
try { try {
mailService.sendApiFeatureStateEmail(apiFeature, stateValue, email, createStateMailMessage(state, apiFeature, stateValue)); mailService.sendApiFeatureStateEmail(apiFeature, stateValue, email, createStateMailMessage((TenantApiUsageState) state, apiFeature, stateValue));
} catch (ThingsboardException e) { } catch (ThingsboardException e) {
log.warn("[{}] Can't send update of the API state to tenant with provided email [{}]", state.getTenantId(), email, e); log.warn("[{}] Can't send update of the API state to tenant with provided email [{}]", state.getTenantId(), email, e);
} }
});
}); });
}); } else {
} else { log.warn("[{}] Can't send update of the API state to tenant with empty email!", state.getTenantId());
log.warn("[{}] Can't send update of the API state to tenant with empty email!", state.getTenantId()); }
} }
} }
@ -350,12 +395,14 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
updateLock.lock(); updateLock.lock();
try { try {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
myTenantStates.values().forEach(state -> { myUsageStates.values().forEach(state -> {
if ((state.getNextCycleTs() < now) && (now - state.getNextCycleTs() < TimeUnit.HOURS.toMillis(1))) { if ((state.getNextCycleTs() < now) && (now - state.getNextCycleTs() < TimeUnit.HOURS.toMillis(1))) {
TenantId tenantId = state.getTenantId();
state.setCycles(state.getNextCycleTs(), SchedulerUtils.getStartOfNextNextMonth()); state.setCycles(state.getNextCycleTs(), SchedulerUtils.getStartOfNextNextMonth());
saveNewCounts(state, Arrays.asList(ApiUsageRecordKey.values())); saveNewCounts(state, Arrays.asList(ApiUsageRecordKey.values()));
updateTenantState(state, tenantProfileCache.get(tenantId)); if (state.getEntityType() == EntityType.TENANT && !state.getEntityId().equals(TenantId.SYS_TENANT_ID)) {
TenantId tenantId = state.getTenantId();
updateTenantState((TenantApiUsageState) state, tenantProfileCache.get(tenantId));
}
} }
}); });
} finally { } finally {
@ -363,7 +410,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
} }
} }
private void saveNewCounts(TenantApiUsageState state, List<ApiUsageRecordKey> keys) { private void saveNewCounts(BaseApiUsageState state, List<ApiUsageRecordKey> keys) {
List<TsKvEntry> counts = keys.stream() List<TsKvEntry> counts = keys.stream()
.map(key -> new BasicTsKvEntry(state.getCurrentCycleTs(), new LongDataEntry(key.getApiCountKey(), 0L))) .map(key -> new BasicTsKvEntry(state.getCurrentCycleTs(), new LongDataEntry(key.getApiCountKey(), 0L)))
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -371,52 +418,66 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), counts, VOID_CALLBACK); tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), counts, VOID_CALLBACK);
} }
private TenantApiUsageState getOrFetchState(TenantId tenantId) { private BaseApiUsageState getOrFetchState(TenantId tenantId, EntityId entityId) {
TenantApiUsageState tenantState = myTenantStates.get(tenantId); if (entityId == null || entityId.isNullUid()) {
if (tenantState == null) { entityId = tenantId;
ApiUsageState dbStateEntity = apiUsageStateService.findTenantApiUsageState(tenantId); }
if (dbStateEntity == null) { BaseApiUsageState state = myUsageStates.get(entityId);
try { if (state != null) {
dbStateEntity = apiUsageStateService.createDefaultApiUsageState(tenantId); return state;
} catch (Exception e) { }
dbStateEntity = apiUsageStateService.findTenantApiUsageState(tenantId);
} ApiUsageState storedState = apiUsageStateService.findApiUsageStateByEntityId(entityId);
} if (storedState == null) {
TenantProfile tenantProfile = tenantProfileCache.get(tenantId);
tenantState = new TenantApiUsageState(tenantProfile, dbStateEntity);
List<ApiUsageRecordKey> newCounts = new ArrayList<>();
try { try {
List<TsKvEntry> dbValues = tsService.findAllLatest(tenantId, dbStateEntity.getId()).get(); storedState = apiUsageStateService.createDefaultApiUsageState(tenantId, entityId);
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { } catch (Exception e) {
boolean cycleEntryFound = false; storedState = apiUsageStateService.findApiUsageStateByEntityId(entityId);
boolean hourlyEntryFound = false; }
for (TsKvEntry tsKvEntry : dbValues) { }
if (tsKvEntry.getKey().equals(key.getApiCountKey())) { if (entityId.getEntityType() == EntityType.TENANT) {
cycleEntryFound = true; if (!entityId.equals(TenantId.SYS_TENANT_ID)) {
state = new TenantApiUsageState(tenantProfileCache.get((TenantId) entityId), storedState);
boolean oldCount = tsKvEntry.getTs() == tenantState.getCurrentCycleTs(); } else {
tenantState.put(key, oldCount ? tsKvEntry.getLongValue().get() : 0L); state = new TenantApiUsageState(storedState);
}
if (!oldCount) { } else {
newCounts.add(key); state = new CustomerApiUsageState(storedState);
} }
} else if (tsKvEntry.getKey().equals(key.getApiCountKey() + HOURLY)) {
hourlyEntryFound = true; List<ApiUsageRecordKey> newCounts = new ArrayList<>();
tenantState.putHourly(key, tsKvEntry.getTs() == tenantState.getCurrentHourTs() ? tsKvEntry.getLongValue().get() : 0L); try {
} List<TsKvEntry> dbValues = tsService.findAllLatest(tenantId, storedState.getId()).get();
if (cycleEntryFound && hourlyEntryFound) { for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
break; boolean cycleEntryFound = false;
boolean hourlyEntryFound = false;
for (TsKvEntry tsKvEntry : dbValues) {
if (tsKvEntry.getKey().equals(key.getApiCountKey())) {
cycleEntryFound = true;
boolean oldCount = tsKvEntry.getTs() == state.getCurrentCycleTs();
state.put(key, oldCount ? tsKvEntry.getLongValue().get() : 0L);
if (!oldCount) {
newCounts.add(key);
} }
} else if (tsKvEntry.getKey().equals(key.getApiCountKey() + HOURLY)) {
hourlyEntryFound = true;
state.putHourly(key, tsKvEntry.getTs() == state.getCurrentHourTs() ? tsKvEntry.getLongValue().get() : 0L);
}
if (cycleEntryFound && hourlyEntryFound) {
break;
} }
} }
log.debug("[{}] Initialized state: {}", tenantId, dbStateEntity);
myTenantStates.put(tenantId, tenantState);
saveNewCounts(tenantState, newCounts);
} catch (InterruptedException | ExecutionException e) {
log.warn("[{}] Failed to fetch api usage state from db.", tenantId, e);
} }
log.debug("[{}] Initialized state: {}", entityId, storedState);
myUsageStates.put(entityId, state);
saveNewCounts(state, newCounts);
} catch (InterruptedException | ExecutionException e) {
log.warn("[{}] Failed to fetch api usage state from db.", tenantId, e);
} }
return tenantState;
return state;
} }
private void initStatesFromDataBase() { private void initStatesFromDataBase() {
@ -429,11 +490,11 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, 1024); PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, 1024);
List<Future<?>> futures = new ArrayList<>(); List<Future<?>> futures = new ArrayList<>();
for (Tenant tenant : tenantIterator) { for (Tenant tenant : tenantIterator) {
if (!myTenantStates.containsKey(tenant.getId()) && partitionService.resolve(ServiceType.TB_CORE, tenant.getId(), tenant.getId()).isMyPartition()) { if (!myUsageStates.containsKey(tenant.getId()) && partitionService.resolve(ServiceType.TB_CORE, tenant.getId(), tenant.getId()).isMyPartition()) {
log.debug("[{}] Initializing tenant state.", tenant.getId()); log.debug("[{}] Initializing tenant state.", tenant.getId());
futures.add(tmpInitExecutor.submit(() -> { futures.add(tmpInitExecutor.submit(() -> {
try { try {
updateTenantState(getOrFetchState(tenant.getId()), tenantProfileCache.get(tenant.getTenantProfileId())); updateTenantState((TenantApiUsageState) getOrFetchState(tenant.getId(), tenant.getId()), tenantProfileCache.get(tenant.getTenantProfileId()));
log.debug("[{}] Initialized tenant state.", tenant.getId()); log.debug("[{}] Initialized tenant state.", tenant.getId());
} catch (Exception e) { } catch (Exception e) {
log.warn("[{}] Failed to initialize tenant API state", tenant.getId(), e); log.warn("[{}] Failed to initialize tenant API state", tenant.getId(), e);

5
application/src/main/java/org/thingsboard/server/service/apiusage/TbApiUsageStateService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.service.apiusage;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
@ -34,5 +35,9 @@ public interface TbApiUsageStateService extends ApplicationListener<PartitionCha
void onTenantUpdate(TenantId tenantId); void onTenantUpdate(TenantId tenantId);
void onTenantDelete(TenantId tenantId);
void onCustomerDelete(CustomerId customerId);
void onApiUsageStateUpdate(TenantId tenantId); void onApiUsageStateUpdate(TenantId tenantId);
} }

142
application/src/main/java/org/thingsboard/server/service/apiusage/TenantApiUsageState.java

@ -22,85 +22,33 @@ import org.thingsboard.server.common.data.ApiFeature;
import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.ApiUsageStateValue; import org.thingsboard.server.common.data.ApiUsageStateValue;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
import org.thingsboard.server.common.msg.tools.SchedulerUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class TenantApiUsageState {
private final Map<ApiUsageRecordKey, Long> currentCycleValues = new ConcurrentHashMap<>();
private final Map<ApiUsageRecordKey, Long> currentHourValues = new ConcurrentHashMap<>();
public class TenantApiUsageState extends BaseApiUsageState {
@Getter @Getter
@Setter @Setter
private TenantProfileId tenantProfileId; private TenantProfileId tenantProfileId;
@Getter @Getter
@Setter @Setter
private TenantProfileData tenantProfileData; private TenantProfileData tenantProfileData;
@Getter
private final ApiUsageState apiUsageState;
@Getter
private volatile long currentCycleTs;
@Getter
private volatile long nextCycleTs;
@Getter
private volatile long currentHourTs;
public TenantApiUsageState(TenantProfile tenantProfile, ApiUsageState apiUsageState) { public TenantApiUsageState(TenantProfile tenantProfile, ApiUsageState apiUsageState) {
super(apiUsageState);
this.tenantProfileId = tenantProfile.getId(); this.tenantProfileId = tenantProfile.getId();
this.tenantProfileData = tenantProfile.getProfileData(); this.tenantProfileData = tenantProfile.getProfileData();
this.apiUsageState = apiUsageState;
this.currentCycleTs = SchedulerUtils.getStartOfCurrentMonth();
this.nextCycleTs = SchedulerUtils.getStartOfNextMonth();
this.currentHourTs = SchedulerUtils.getStartOfCurrentHour();
}
public void put(ApiUsageRecordKey key, Long value) {
currentCycleValues.put(key, value);
} }
public void putHourly(ApiUsageRecordKey key, Long value) { public TenantApiUsageState(ApiUsageState apiUsageState) {
currentHourValues.put(key, value); super(apiUsageState);
}
public long add(ApiUsageRecordKey key, long value) {
long result = currentCycleValues.getOrDefault(key, 0L) + value;
currentCycleValues.put(key, result);
return result;
}
public long get(ApiUsageRecordKey key) {
return currentCycleValues.getOrDefault(key, 0L);
}
public long addToHourly(ApiUsageRecordKey key, long value) {
long result = currentHourValues.getOrDefault(key, 0L) + value;
currentHourValues.put(key, result);
return result;
}
public void setHour(long currentHourTs) {
this.currentHourTs = currentHourTs;
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
currentHourValues.put(key, 0L);
}
}
public void setCycles(long currentCycleTs, long nextCycleTs) {
this.currentCycleTs = currentCycleTs;
this.nextCycleTs = nextCycleTs;
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
currentCycleValues.put(key, 0L);
}
} }
public long getProfileThreshold(ApiUsageRecordKey key) { public long getProfileThreshold(ApiUsageRecordKey key) {
@ -111,53 +59,25 @@ public class TenantApiUsageState {
return tenantProfileData.getConfiguration().getWarnThreshold(key); return tenantProfileData.getConfiguration().getWarnThreshold(key);
} }
public TenantId getTenantId() { private Pair<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThreshold(ApiFeature feature) {
return apiUsageState.getTenantId(); ApiUsageStateValue featureValue = ApiUsageStateValue.ENABLED;
} for (ApiUsageRecordKey recordKey : ApiUsageRecordKey.getKeys(feature)) {
long value = get(recordKey);
public ApiUsageStateValue getFeatureValue(ApiFeature feature) { long threshold = getProfileThreshold(recordKey);
switch (feature) { long warnThreshold = getProfileWarnThreshold(recordKey);
case TRANSPORT: ApiUsageStateValue tmpValue;
return apiUsageState.getTransportState(); if (threshold == 0 || value == 0 || value < warnThreshold) {
case RE: tmpValue = ApiUsageStateValue.ENABLED;
return apiUsageState.getReExecState(); } else if (value < threshold) {
case DB: tmpValue = ApiUsageStateValue.WARNING;
return apiUsageState.getDbStorageState(); } else {
case JS: tmpValue = ApiUsageStateValue.DISABLED;
return apiUsageState.getJsExecState(); }
case EMAIL: featureValue = ApiUsageStateValue.toMoreRestricted(featureValue, tmpValue);
return apiUsageState.getEmailExecState();
case SMS:
return apiUsageState.getSmsExecState();
default:
return ApiUsageStateValue.ENABLED;
} }
return setFeatureValue(feature, featureValue) ? Pair.of(feature, featureValue) : null;
} }
public boolean setFeatureValue(ApiFeature feature, ApiUsageStateValue value) {
ApiUsageStateValue currentValue = getFeatureValue(feature);
switch (feature) {
case TRANSPORT:
apiUsageState.setTransportState(value);
break;
case RE:
apiUsageState.setReExecState(value);
break;
case DB:
apiUsageState.setDbStorageState(value);
break;
case JS:
apiUsageState.setJsExecState(value);
break;
case EMAIL:
apiUsageState.setEmailExecState(value);
break;
case SMS:
apiUsageState.setSmsExecState(value);
break;
}
return !currentValue.equals(value);
}
public Map<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThresholds() { public Map<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThresholds() {
return checkStateUpdatedDueToThreshold(new HashSet<>(Arrays.asList(ApiFeature.values()))); return checkStateUpdatedDueToThreshold(new HashSet<>(Arrays.asList(ApiFeature.values())));
@ -174,23 +94,9 @@ public class TenantApiUsageState {
return result; return result;
} }
public Pair<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThreshold(ApiFeature feature) { @Override
ApiUsageStateValue featureValue = ApiUsageStateValue.ENABLED; public EntityType getEntityType() {
for (ApiUsageRecordKey recordKey : ApiUsageRecordKey.getKeys(feature)) { return EntityType.TENANT;
long value = get(recordKey);
long threshold = getProfileThreshold(recordKey);
long warnThreshold = getProfileWarnThreshold(recordKey);
ApiUsageStateValue tmpValue;
if (threshold == 0 || value == 0 || value < warnThreshold) {
tmpValue = ApiUsageStateValue.ENABLED;
} else if (value < threshold) {
tmpValue = ApiUsageStateValue.WARNING;
} else {
tmpValue = ApiUsageStateValue.DISABLED;
}
featureValue = ApiUsageStateValue.toMoreRestricted(featureValue, tmpValue);
}
return setFeatureValue(feature, featureValue) ? Pair.of(feature, featureValue) : null;
} }
} }

4
application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java

@ -230,7 +230,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, String type) { private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, String type) {
try { try {
JsonNode entityNode = JacksonUtil.valueToTree(request); JsonNode entityNode = JacksonUtil.valueToTree(request);
TbMsg msg = TbMsg.newMsg(type, device.getId(), createTbMsgMetaData(device), JacksonUtil.toString(entityNode)); TbMsg msg = TbMsg.newMsg(type, device.getId(), device.getCustomerId(), createTbMsgMetaData(device), JacksonUtil.toString(entityNode));
sendToRuleEngine(device.getTenantId(), msg, null); sendToRuleEngine(device.getTenantId(), msg, null);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), type, e); log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), type, e);
@ -240,7 +240,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
private void pushDeviceCreatedEventToRuleEngine(Device device) { private void pushDeviceCreatedEventToRuleEngine(Device device) {
try { try {
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device); ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device);
TbMsg msg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, device.getId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); TbMsg msg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, device.getId(), device.getCustomerId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode));
sendToRuleEngine(device.getTenantId(), msg, null); sendToRuleEngine(device.getTenantId(), msg, null);
} catch (JsonProcessingException | IllegalArgumentException e) { } catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), DataConstants.ENTITY_CREATED, e); log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), DataConstants.ENTITY_CREATED, e);

2
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java

@ -920,7 +920,7 @@ public final class EdgeGrpcSession implements Closeable {
try { try {
if (uplinkMsg.getEntityDataCount() > 0) { if (uplinkMsg.getEntityDataCount() > 0) {
for (EntityDataProto entityData : uplinkMsg.getEntityDataList()) { for (EntityDataProto entityData : uplinkMsg.getEntityDataList()) {
result.addAll(ctx.getTelemetryProcessor().onTelemetryUpdate(edge.getTenantId(), entityData)); result.addAll(ctx.getTelemetryProcessor().onTelemetryUpdate(edge.getTenantId(), edge.getCustomerId(), entityData));
} }
} }
if (uplinkMsg.getDeviceUpdateMsgCount() > 0) { if (uplinkMsg.getDeviceUpdateMsgCount() > 0) {

2
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceProcessor.java

@ -231,7 +231,7 @@ public class DeviceProcessor extends BaseProcessor {
try { try {
DeviceId deviceId = device.getId(); DeviceId deviceId = device.getId();
ObjectNode entityNode = mapper.valueToTree(device); ObjectNode entityNode = mapper.valueToTree(device);
TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, device.getCustomerId(),
getActionTbMsgMetaData(edge, device.getCustomerId()), TbMsgDataType.JSON, mapper.writeValueAsString(entityNode)); getActionTbMsgMetaData(edge, device.getCustomerId()), TbMsgDataType.JSON, mapper.writeValueAsString(entityNode));
tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() {
@Override @Override

20
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryProcessor.java

@ -70,7 +70,7 @@ public class TelemetryProcessor extends BaseProcessor {
private final Gson gson = new Gson(); private final Gson gson = new Gson();
public List<ListenableFuture<Void>> onTelemetryUpdate(TenantId tenantId, EntityDataProto entityData) { public List<ListenableFuture<Void>> onTelemetryUpdate(TenantId tenantId, CustomerId customerId, EntityDataProto entityData) {
log.trace("[{}] onTelemetryUpdate [{}]", tenantId, entityData); log.trace("[{}] onTelemetryUpdate [{}]", tenantId, entityData);
List<ListenableFuture<Void>> result = new ArrayList<>(); List<ListenableFuture<Void>> result = new ArrayList<>();
EntityId entityId = constructEntityId(entityData); EntityId entityId = constructEntityId(entityData);
@ -80,14 +80,14 @@ public class TelemetryProcessor extends BaseProcessor {
TbMsgMetaData metaData = new TbMsgMetaData(); TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue(DataConstants.MSG_SOURCE_KEY, DataConstants.EDGE_MSG_SOURCE); metaData.putValue(DataConstants.MSG_SOURCE_KEY, DataConstants.EDGE_MSG_SOURCE);
if (entityData.hasPostAttributesMsg()) { if (entityData.hasPostAttributesMsg()) {
result.add(processPostAttributes(tenantId, entityId, entityData.getPostAttributesMsg(), metaData)); result.add(processPostAttributes(tenantId, customerId, entityId, entityData.getPostAttributesMsg(), metaData));
} }
if (entityData.hasAttributesUpdatedMsg()) { if (entityData.hasAttributesUpdatedMsg()) {
metaData.putValue("scope", entityData.getPostAttributeScope()); metaData.putValue("scope", entityData.getPostAttributeScope());
result.add(processAttributesUpdate(tenantId, entityId, entityData.getAttributesUpdatedMsg(), metaData)); result.add(processAttributesUpdate(tenantId, customerId, entityId, entityData.getAttributesUpdatedMsg(), metaData));
} }
if (entityData.hasPostTelemetryMsg()) { if (entityData.hasPostTelemetryMsg()) {
result.add(processPostTelemetry(tenantId, entityId, entityData.getPostTelemetryMsg(), metaData)); result.add(processPostTelemetry(tenantId, customerId, entityId, entityData.getPostTelemetryMsg(), metaData));
} }
} }
if (entityData.hasAttributeDeleteMsg()) { if (entityData.hasAttributeDeleteMsg()) {
@ -148,7 +148,7 @@ public class TelemetryProcessor extends BaseProcessor {
} }
} }
private ListenableFuture<Void> processPostTelemetry(TenantId tenantId, EntityId entityId, TransportProtos.PostTelemetryMsg msg, TbMsgMetaData metaData) { private ListenableFuture<Void> processPostTelemetry(TenantId tenantId, CustomerId customerId, EntityId entityId, TransportProtos.PostTelemetryMsg msg, TbMsgMetaData metaData) {
SettableFuture<Void> futureToSet = SettableFuture.create(); SettableFuture<Void> futureToSet = SettableFuture.create();
for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) { for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) {
JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList());
@ -156,7 +156,7 @@ public class TelemetryProcessor extends BaseProcessor {
Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
String queueName = defaultQueueAndRuleChain.getKey(); String queueName = defaultQueueAndRuleChain.getKey();
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue(); RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, metaData, gson.toJson(json), ruleChainId, null); TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override @Override
public void onSuccess(TbQueueMsgMetadata metadata) { public void onSuccess(TbQueueMsgMetadata metadata) {
@ -173,13 +173,13 @@ public class TelemetryProcessor extends BaseProcessor {
return futureToSet; return futureToSet;
} }
private ListenableFuture<Void> processPostAttributes(TenantId tenantId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) { private ListenableFuture<Void> processPostAttributes(TenantId tenantId, CustomerId customerId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) {
SettableFuture<Void> futureToSet = SettableFuture.create(); SettableFuture<Void> futureToSet = SettableFuture.create();
JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
String queueName = defaultQueueAndRuleChain.getKey(); String queueName = defaultQueueAndRuleChain.getKey();
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue(); RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, metaData, gson.toJson(json), ruleChainId, null); TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override @Override
public void onSuccess(TbQueueMsgMetadata metadata) { public void onSuccess(TbQueueMsgMetadata metadata) {
@ -195,7 +195,7 @@ public class TelemetryProcessor extends BaseProcessor {
return futureToSet; return futureToSet;
} }
private ListenableFuture<Void> processAttributesUpdate(TenantId tenantId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) { private ListenableFuture<Void> processAttributesUpdate(TenantId tenantId, CustomerId customerId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) {
SettableFuture<Void> futureToSet = SettableFuture.create(); SettableFuture<Void> futureToSet = SettableFuture.create();
JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(json); Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(json);
@ -206,7 +206,7 @@ public class TelemetryProcessor extends BaseProcessor {
Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
String queueName = defaultQueueAndRuleChain.getKey(); String queueName = defaultQueueAndRuleChain.getKey();
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue(); RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
TbMsg tbMsg = TbMsg.newMsg(queueName, DataConstants.ATTRIBUTES_UPDATED, entityId, metaData, gson.toJson(json), ruleChainId, null); TbMsg tbMsg = TbMsg.newMsg(queueName, DataConstants.ATTRIBUTES_UPDATED, entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override @Override
public void onSuccess(TbQueueMsgMetadata metadata) { public void onSuccess(TbQueueMsgMetadata metadata) {

2
application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java

@ -390,7 +390,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
pageData = tenantService.findTenants(pageLink); pageData = tenantService.findTenants(pageLink);
for (Tenant tenant : pageData.getData()) { for (Tenant tenant : pageData.getData()) {
try { try {
apiUsageStateService.createDefaultApiUsageState(tenant.getId()); apiUsageStateService.createDefaultApiUsageState(tenant.getId(), null);
} catch (Exception e) { } catch (Exception e) {
} }
List<EntitySubtype> deviceTypes = deviceService.findDeviceTypesByTenantId(tenant.getId()).get(); List<EntitySubtype> deviceTypes = deviceService.findDeviceTypesByTenantId(tenant.getId()).get();

5
application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java

@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.ApiUsageStateMailMessage;
import org.thingsboard.server.common.data.ApiUsageStateValue; import org.thingsboard.server.common.data.ApiUsageStateValue;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.exception.IncorrectParameterException;
@ -233,7 +234,7 @@ public class DefaultMailService implements MailService {
} }
@Override @Override
public void send(TenantId tenantId, String from, String to, String cc, String bcc, String subject, String body) throws MessagingException { public void send(TenantId tenantId, CustomerId customerId, String from, String to, String cc, String bcc, String subject, String body) throws MessagingException {
if (apiUsageStateService.getApiUsageState(tenantId).isEmailSendEnabled()) { if (apiUsageStateService.getApiUsageState(tenantId).isEmailSendEnabled()) {
MimeMessage mailMsg = mailSender.createMimeMessage(); MimeMessage mailMsg = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mailMsg, "UTF-8"); MimeMessageHelper helper = new MimeMessageHelper(mailMsg, "UTF-8");
@ -248,7 +249,7 @@ public class DefaultMailService implements MailService {
helper.setSubject(subject); helper.setSubject(subject);
helper.setText(body); helper.setText(body);
mailSender.send(helper.getMimeMessage()); mailSender.send(helper.getMimeMessage());
apiUsageClient.report(tenantId, ApiUsageRecordKey.EMAIL_EXEC_COUNT, 1); apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.EMAIL_EXEC_COUNT, 1);
} else { } else {
throw new RuntimeException("Email sending is disabled due to API limits!"); throw new RuntimeException("Email sending is disabled due to API limits!");
} }

1
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java

@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;

12
application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java

@ -18,14 +18,15 @@ package org.thingsboard.server.service.queue.processing;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorMsg;
@ -72,7 +73,8 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
protected final TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer; protected final TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer;
public AbstractConsumerService(ActorSystemContext actorContext, DataDecodingEncodingService encodingService, public AbstractConsumerService(ActorSystemContext actorContext, DataDecodingEncodingService encodingService,
TbTenantProfileCache tenantProfileCache, TbDeviceProfileCache deviceProfileCache, TbApiUsageStateService apiUsageStateService, TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer) { TbTenantProfileCache tenantProfileCache, TbDeviceProfileCache deviceProfileCache,
TbApiUsageStateService apiUsageStateService, TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer) {
this.actorContext = actorContext; this.actorContext = actorContext;
this.encodingService = encodingService; this.encodingService = encodingService;
this.tenantProfileCache = tenantProfileCache; this.tenantProfileCache = tenantProfileCache;
@ -166,6 +168,8 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
tenantProfileCache.evict(componentLifecycleMsg.getTenantId()); tenantProfileCache.evict(componentLifecycleMsg.getTenantId());
if (componentLifecycleMsg.getEvent().equals(ComponentLifecycleEvent.UPDATED)) { if (componentLifecycleMsg.getEvent().equals(ComponentLifecycleEvent.UPDATED)) {
apiUsageStateService.onTenantUpdate(componentLifecycleMsg.getTenantId()); apiUsageStateService.onTenantUpdate(componentLifecycleMsg.getTenantId());
} else if (componentLifecycleMsg.getEvent().equals(ComponentLifecycleEvent.DELETED)) {
apiUsageStateService.onTenantDelete((TenantId) componentLifecycleMsg.getEntityId());
} }
} else if (EntityType.DEVICE_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { } else if (EntityType.DEVICE_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceProfileId(componentLifecycleMsg.getEntityId().getId())); deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceProfileId(componentLifecycleMsg.getEntityId().getId()));
@ -173,6 +177,10 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceId(componentLifecycleMsg.getEntityId().getId())); deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceId(componentLifecycleMsg.getEntityId().getId()));
} else if (EntityType.API_USAGE_STATE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { } else if (EntityType.API_USAGE_STATE.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
apiUsageStateService.onApiUsageStateUpdate(componentLifecycleMsg.getTenantId()); apiUsageStateService.onApiUsageStateUpdate(componentLifecycleMsg.getTenantId());
} else if (EntityType.CUSTOMER.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
if (componentLifecycleMsg.getEvent() == ComponentLifecycleEvent.DELETED) {
apiUsageStateService.onCustomerDelete((CustomerId) componentLifecycleMsg.getEntityId());
}
} }
} }
log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg); log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg);

9
application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java

@ -34,6 +34,7 @@ import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.security.model.SecurityUser;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
@ -95,11 +96,11 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
} }
@Override @Override
public void processRestApiRpcRequest(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer) { public void processRestApiRpcRequest(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer, SecurityUser currentUser) {
log.trace("[{}][{}] Processing REST API call to rule engine [{}]", request.getTenantId(), request.getId(), request.getDeviceId()); log.trace("[{}][{}] Processing REST API call to rule engine [{}]", request.getTenantId(), request.getId(), request.getDeviceId());
UUID requestId = request.getId(); UUID requestId = request.getId();
localToRuleEngineRpcRequests.put(requestId, responseConsumer); localToRuleEngineRpcRequests.put(requestId, responseConsumer);
sendRpcRequestToRuleEngine(request); sendRpcRequestToRuleEngine(request, currentUser);
scheduleToRuleEngineTimeout(request, requestId); scheduleToRuleEngineTimeout(request, requestId);
} }
@ -149,7 +150,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
} }
} }
private void sendRpcRequestToRuleEngine(ToDeviceRpcRequest msg) { private void sendRpcRequestToRuleEngine(ToDeviceRpcRequest msg, SecurityUser currentUser) {
ObjectNode entityNode = json.createObjectNode(); ObjectNode entityNode = json.createObjectNode();
TbMsgMetaData metaData = new TbMsgMetaData(); TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("requestUUID", msg.getId().toString()); metaData.putValue("requestUUID", msg.getId().toString());
@ -167,7 +168,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
entityNode.put("params", msg.getBody().getParams()); entityNode.put("params", msg.getBody().getParams());
try { try {
TbMsg tbMsg = TbMsg.newMsg(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode)); TbMsg tbMsg = TbMsg.newMsg(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), currentUser.getCustomerId(), metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode));
clusterService.pushMsgToRuleEngine(msg.getTenantId(), msg.getDeviceId(), tbMsg, null); clusterService.pushMsgToRuleEngine(msg.getTenantId(), msg.getDeviceId(), tbMsg, null);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new RuntimeException(e); throw new RuntimeException(e);

7
application/src/main/java/org/thingsboard/server/service/rpc/TbCoreDeviceRpcService.java

@ -16,6 +16,7 @@
package org.thingsboard.server.service.rpc; package org.thingsboard.server.service.rpc;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -27,11 +28,11 @@ public interface TbCoreDeviceRpcService {
/** /**
* Handles REST API calls that contain RPC requests to Device and pushes them to Rule Engine. * Handles REST API calls that contain RPC requests to Device and pushes them to Rule Engine.
* Schedules the timeout for the RPC call based on the {@link ToDeviceRpcRequest} * Schedules the timeout for the RPC call based on the {@link ToDeviceRpcRequest}
* * @param request the RPC request
* @param request the RPC request
* @param responseConsumer the consumer of the RPC response * @param responseConsumer the consumer of the RPC response
* @param currentUser
*/ */
void processRestApiRpcRequest(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer); void processRestApiRpcRequest(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer, SecurityUser currentUser);
/** /**
* Handles the RPC response from the Rule Engine. * Handles the RPC response from the Rule Engine.

5
application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java

@ -20,6 +20,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.queue.usagestats.TbApiUsageClient; import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
@ -73,14 +74,14 @@ public abstract class AbstractJsInvokeService implements JsInvokeService {
} }
@Override @Override
public ListenableFuture<Object> invokeFunction(TenantId tenantId, UUID scriptId, Object... args) { public ListenableFuture<Object> invokeFunction(TenantId tenantId, CustomerId customerId, UUID scriptId, Object... args) {
if (apiUsageStateService.getApiUsageState(tenantId).isJsExecEnabled()) { if (apiUsageStateService.getApiUsageState(tenantId).isJsExecEnabled()) {
String functionName = scriptIdToNameMap.get(scriptId); String functionName = scriptIdToNameMap.get(scriptId);
if (functionName == null) { if (functionName == null) {
return Futures.immediateFailedFuture(new RuntimeException("No compiled script found for scriptId: [" + scriptId + "]!")); return Futures.immediateFailedFuture(new RuntimeException("No compiled script found for scriptId: [" + scriptId + "]!"));
} }
if (!isDisabled(scriptId)) { if (!isDisabled(scriptId)) {
apiUsageClient.report(tenantId, ApiUsageRecordKey.JS_EXEC_COUNT, 1); apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.JS_EXEC_COUNT, 1);
return doInvokeFunction(scriptId, functionName, args); return doInvokeFunction(scriptId, functionName, args);
} else { } else {
return Futures.immediateFailedFuture( return Futures.immediateFailedFuture(

4
application/src/main/java/org/thingsboard/server/service/script/JsInvokeService.java

@ -16,7 +16,7 @@
package org.thingsboard.server.service.script; package org.thingsboard.server.service.script;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import java.util.UUID; import java.util.UUID;
@ -25,7 +25,7 @@ public interface JsInvokeService {
ListenableFuture<UUID> eval(TenantId tenantId, JsScriptType scriptType, String scriptBody, String... argNames); ListenableFuture<UUID> eval(TenantId tenantId, JsScriptType scriptType, String scriptBody, String... argNames);
ListenableFuture<Object> invokeFunction(TenantId tenantId, UUID scriptId, Object... args); ListenableFuture<Object> invokeFunction(TenantId tenantId, CustomerId customerId, UUID scriptId, Object... args);
ListenableFuture<Void> release(UUID scriptId); ListenableFuture<Void> release(UUID scriptId);

4
application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java

@ -218,7 +218,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S
private JsonNode executeScript(TbMsg msg) throws ScriptException { private JsonNode executeScript(TbMsg msg) throws ScriptException {
try { try {
String[] inArgs = prepareArgs(msg); String[] inArgs = prepareArgs(msg);
String eval = sandboxService.invokeFunction(tenantId, this.scriptId, inArgs[0], inArgs[1], inArgs[2]).get().toString(); String eval = sandboxService.invokeFunction(tenantId, msg.getCustomerId(), this.scriptId, inArgs[0], inArgs[1], inArgs[2]).get().toString();
return mapper.readTree(eval); return mapper.readTree(eval);
} catch (ExecutionException e) { } catch (ExecutionException e) {
if (e.getCause() instanceof ScriptException) { if (e.getCause() instanceof ScriptException) {
@ -235,7 +235,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S
private ListenableFuture<JsonNode> executeScriptAsync(TbMsg msg) { private ListenableFuture<JsonNode> executeScriptAsync(TbMsg msg) {
String[] inArgs = prepareArgs(msg); String[] inArgs = prepareArgs(msg);
return Futures.transformAsync(sandboxService.invokeFunction(tenantId, this.scriptId, inArgs[0], inArgs[1], inArgs[2]), return Futures.transformAsync(sandboxService.invokeFunction(tenantId, msg.getCustomerId(), this.scriptId, inArgs[0], inArgs[1], inArgs[2]),
o -> { o -> {
try { try {
return Futures.immediateFuture(mapper.readTree(o.toString())); return Futures.immediateFuture(mapper.readTree(o.toString()));

5
application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsService.java

@ -22,6 +22,7 @@ import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.SmsService; import org.thingsboard.rule.engine.api.SmsService;
import org.thingsboard.rule.engine.api.sms.SmsSender; import org.thingsboard.rule.engine.api.sms.SmsSender;
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.sms.config.SmsProviderConfiguration; import org.thingsboard.server.common.data.sms.config.SmsProviderConfiguration;
import org.thingsboard.server.common.data.sms.config.TestSmsRequest; import org.thingsboard.server.common.data.sms.config.TestSmsRequest;
import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.AdminSettings;
@ -94,7 +95,7 @@ public class DefaultSmsService implements SmsService {
} }
@Override @Override
public void sendSms(TenantId tenantId, String[] numbersTo, String message) throws ThingsboardException { public void sendSms(TenantId tenantId, CustomerId customerId, String[] numbersTo, String message) throws ThingsboardException {
if (apiUsageStateService.getApiUsageState(tenantId).isSmsSendEnabled()) { if (apiUsageStateService.getApiUsageState(tenantId).isSmsSendEnabled()) {
int smsCount = 0; int smsCount = 0;
try { try {
@ -103,7 +104,7 @@ public class DefaultSmsService implements SmsService {
} }
} finally { } finally {
if (smsCount > 0) { if (smsCount > 0) {
apiUsageClient.report(tenantId, ApiUsageRecordKey.SMS_EXEC_COUNT, smsCount); apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.SMS_EXEC_COUNT, smsCount);
} }
} }
} else { } else {

3
application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java

@ -468,6 +468,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
md.putValue("deviceName", device.getName()); md.putValue("deviceName", device.getName());
md.putValue("deviceType", device.getType()); md.putValue("deviceType", device.getType());
return DeviceStateData.builder() return DeviceStateData.builder()
.customerId(device.getCustomerId())
.tenantId(device.getTenantId()) .tenantId(device.getTenantId())
.deviceId(device.getId()) .deviceId(device.getId())
.deviceCreationTime(device.getCreatedTime()) .deviceCreationTime(device.getCreatedTime())
@ -507,7 +508,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
if(!persistToTelemetry){ if(!persistToTelemetry){
md.putValue(DataConstants.SCOPE, SERVER_SCOPE); md.putValue(DataConstants.SCOPE, SERVER_SCOPE);
} }
TbMsg tbMsg = TbMsg.newMsg(msgType, stateData.getDeviceId(), md, TbMsgDataType.JSON, data); TbMsg tbMsg = TbMsg.newMsg(msgType, stateData.getDeviceId(), stateData.getCustomerId(), md, TbMsgDataType.JSON, data);
clusterService.pushMsgToRuleEngine(stateData.getTenantId(), stateData.getDeviceId(), tbMsg, null); clusterService.pushMsgToRuleEngine(stateData.getTenantId(), stateData.getDeviceId(), tbMsg, null);
} catch (Exception e) { } catch (Exception e) {
log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e); log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e);

2
application/src/main/java/org/thingsboard/server/service/state/DeviceStateData.java

@ -17,6 +17,7 @@ package org.thingsboard.server.service.state;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgMetaData;
@ -29,6 +30,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
class DeviceStateData { class DeviceStateData {
private final TenantId tenantId; private final TenantId tenantId;
private final CustomerId customerId;
private final DeviceId deviceId; private final DeviceId deviceId;
private final long deviceCreationTime; private final long deviceCreationTime;
private TbMsgMetaData metaData; private TbMsgMetaData metaData;

10
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java

@ -25,7 +25,7 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
@ -35,13 +35,11 @@ import org.thingsboard.server.common.data.kv.DoubleDataEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.PartitionService;
@ -115,11 +113,11 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
@Override @Override
public void saveAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback) { public void saveAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback) {
saveAndNotify(tenantId, entityId, ts, 0L, callback); saveAndNotify(tenantId, null, entityId, ts, 0L, callback);
} }
@Override @Override
public void saveAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback) { public void saveAndNotify(TenantId tenantId, CustomerId customerId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback) {
checkInternalEntity(entityId); checkInternalEntity(entityId);
boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null; boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null;
if (sysTenant || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { if (sysTenant || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) {
@ -127,7 +125,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
@Override @Override
public void onSuccess(Integer result) { public void onSuccess(Integer result) {
if (!sysTenant && result != null && result > 0) { if (!sysTenant && result != null && result > 0) {
apiUsageClient.report(tenantId, ApiUsageRecordKey.STORAGE_DP_COUNT, result); apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.STORAGE_DP_COUNT, result);
} }
callback.onSuccess(null); callback.onSuccess(null);
} }

5
application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java

@ -86,6 +86,7 @@ import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.resource.TbResourceService; import org.thingsboard.server.service.resource.TbResourceService;
import org.thingsboard.server.service.state.DeviceStateService; import org.thingsboard.server.service.state.DeviceStateService;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -284,7 +285,7 @@ public class DefaultTransportApiService implements TransportApiService {
DeviceId deviceId = device.getId(); DeviceId deviceId = device.getId();
ObjectNode entityNode = mapper.valueToTree(device); ObjectNode entityNode = mapper.valueToTree(device);
TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, metaData, TbMsgDataType.JSON, mapper.writeValueAsString(entityNode)); TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, customerId, metaData, TbMsgDataType.JSON, mapper.writeValueAsString(entityNode));
tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, null); tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, null);
} }
GetOrCreateDeviceFromGatewayResponseMsg.Builder builder = GetOrCreateDeviceFromGatewayResponseMsg.newBuilder() GetOrCreateDeviceFromGatewayResponseMsg.Builder builder = GetOrCreateDeviceFromGatewayResponseMsg.newBuilder()
@ -423,6 +424,8 @@ public class DefaultTransportApiService implements TransportApiService {
return DeviceInfoProto.newBuilder() return DeviceInfoProto.newBuilder()
.setTenantIdMSB(device.getTenantId().getId().getMostSignificantBits()) .setTenantIdMSB(device.getTenantId().getId().getMostSignificantBits())
.setTenantIdLSB(device.getTenantId().getId().getLeastSignificantBits()) .setTenantIdLSB(device.getTenantId().getId().getLeastSignificantBits())
.setCustomerIdMSB(Optional.ofNullable(device.getCustomerId()).map(customerId -> customerId.getId().getMostSignificantBits()).orElse(0L))
.setCustomerIdLSB(Optional.ofNullable(device.getCustomerId()).map(customerId -> customerId.getId().getLeastSignificantBits()).orElse(0L))
.setDeviceIdMSB(device.getId().getId().getMostSignificantBits()) .setDeviceIdMSB(device.getId().getId().getMostSignificantBits())
.setDeviceIdLSB(device.getId().getId().getLeastSignificantBits()) .setDeviceIdLSB(device.getId().getId().getLeastSignificantBits())
.setDeviceName(device.getName()) .setDeviceName(device.getName())

1
application/src/main/resources/thingsboard.yml

@ -124,6 +124,7 @@ usage:
stats: stats:
report: report:
enabled: "${USAGE_STATS_REPORT_ENABLED:true}" enabled: "${USAGE_STATS_REPORT_ENABLED:true}"
enabled_per_customer: "${USAGE_STATS_REPORT_PER_CUSTOMER_ENABLED:false}"
interval: "${USAGE_STATS_REPORT_INTERVAL:10}" interval: "${USAGE_STATS_REPORT_INTERVAL:10}"
check: check:
cycle: "${USAGE_STATS_CHECK_CYCLE:60000}" cycle: "${USAGE_STATS_CHECK_CYCLE:60000}"

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

@ -21,12 +21,12 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.kv.Aggregation; import org.thingsboard.server.common.data.kv.Aggregation;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
@ -750,7 +750,7 @@ public class BaseWebsocketApiTest extends AbstractWebsocketTest {
private void sendTelemetry(Device device, List<TsKvEntry> tsData) throws InterruptedException { private void sendTelemetry(Device device, List<TsKvEntry> tsData) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
tsService.saveAndNotify(device.getTenantId(), device.getId(), tsData, 0, new FutureCallback<Void>() { tsService.saveAndNotify(device.getTenantId(), null, device.getId(), tsData, 0, new FutureCallback<Void>() {
@Override @Override
public void onSuccess(@Nullable Void result) { public void onSuccess(@Nullable Void result) {
latch.countDown(); latch.countDown();

7
common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateService.java

@ -17,17 +17,22 @@ package org.thingsboard.server.dao.usagerecord;
import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.id.ApiUsageStateId; import org.thingsboard.server.common.data.id.ApiUsageStateId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
public interface ApiUsageStateService { public interface ApiUsageStateService {
ApiUsageState createDefaultApiUsageState(TenantId id); ApiUsageState createDefaultApiUsageState(TenantId id, EntityId entityId);
ApiUsageState update(ApiUsageState apiUsageState); ApiUsageState update(ApiUsageState apiUsageState);
ApiUsageState findTenantApiUsageState(TenantId tenantId); ApiUsageState findTenantApiUsageState(TenantId tenantId);
ApiUsageState findApiUsageStateByEntityId(EntityId entityId);
void deleteApiUsageStateByTenantId(TenantId tenantId); void deleteApiUsageStateByTenantId(TenantId tenantId);
void deleteApiUsageStateByEntityId(EntityId entityId);
ApiUsageState findApiUsageStateById(TenantId tenantId, ApiUsageStateId id); ApiUsageState findApiUsageStateById(TenantId tenantId, ApiUsageStateId id);
} }

5
common/data/src/main/java/org/thingsboard/server/common/data/query/ApiUsageStateFilter.java

@ -16,10 +16,13 @@
package org.thingsboard.server.common.data.query; package org.thingsboard.server.common.data.query;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.CustomerId;
@Data @Data
public class ApiUsageStateFilter implements EntityFilter { public class ApiUsageStateFilter implements EntityFilter {
private CustomerId customerId;
@Override @Override
public EntityFilterType getType() { public EntityFilterType getType() {
return EntityFilterType.API_USAGE_STATE; return EntityFilterType.API_USAGE_STATE;

77
common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java

@ -19,10 +19,11 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
@ -47,6 +48,7 @@ public final class TbMsg implements Serializable {
private final long ts; private final long ts;
private final String type; private final String type;
private final EntityId originator; private final EntityId originator;
private final CustomerId customerId;
private final TbMsgMetaData metaData; private final TbMsgMetaData metaData;
private final TbMsgDataType dataType; private final TbMsgDataType dataType;
private final String data; private final String data;
@ -55,6 +57,7 @@ public final class TbMsg implements Serializable {
@Getter(value = AccessLevel.NONE) @Getter(value = AccessLevel.NONE)
private final AtomicInteger ruleNodeExecCounter; private final AtomicInteger ruleNodeExecCounter;
public int getAndIncrementRuleNodeCounter() { public int getAndIncrementRuleNodeCounter() {
return ruleNodeExecCounter.getAndIncrement(); return ruleNodeExecCounter.getAndIncrement();
} }
@ -64,60 +67,81 @@ public final class TbMsg implements Serializable {
transient private final TbMsgCallback callback; transient private final TbMsgCallback callback;
public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, return newMsg(queueName, type, originator, null, metaData, data, ruleChainId, ruleNodeId);
}
public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId,
metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, 0, TbMsgCallback.EMPTY); metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, 0, TbMsgCallback.EMPTY);
} }
public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) { public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) {
return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, 0, TbMsgCallback.EMPTY); return newMsg(type, originator, null, metaData, data);
}
public static TbMsg newMsg(String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) {
return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, metaData.copy(), TbMsgDataType.JSON, data, null, null, 0, TbMsgCallback.EMPTY);
} }
// REALLY NEW MSG // REALLY NEW MSG
public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) { public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) {
return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, 0, TbMsgCallback.EMPTY); return newMsg(queueName, type, originator, null, metaData, data);
}
public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) {
return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, metaData.copy(), TbMsgDataType.JSON, data, null, null, 0, TbMsgCallback.EMPTY);
}
public static TbMsg newMsg(String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data) {
return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, metaData.copy(), dataType, data, null, null, 0, TbMsgCallback.EMPTY);
} }
public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) {
return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, null, null, 0, TbMsgCallback.EMPTY); return newMsg(type, originator, null, metaData, dataType, data);
} }
// For Tests only // For Tests only
public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, 0, TbMsgCallback.EMPTY); return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, null, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, 0, TbMsgCallback.EMPTY);
} }
public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) { public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) {
return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, 0, callback); return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, null, metaData.copy(), TbMsgDataType.JSON, data, null, null, 0, callback);
} }
public static TbMsg transformMsg(TbMsg tbMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { public static TbMsg transformMsg(TbMsg tbMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) {
return new TbMsg(tbMsg.getQueueName(), tbMsg.getId(), tbMsg.getTs(), type, originator, metaData.copy(), tbMsg.getDataType(), return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, type, originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType,
data, tbMsg.getRuleChainId(), tbMsg.getRuleNodeId(), tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback()); data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ruleNodeExecCounter.get(), tbMsg.callback);
}
public static TbMsg transformMsg(TbMsg tbMsg, CustomerId customerId) {
return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, customerId, tbMsg.metaData, tbMsg.dataType,
tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback());
} }
public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId) { public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId) {
return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.metaData, tbMsg.dataType, return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType,
tbMsg.data, ruleChainId, null, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback()); tbMsg.data, ruleChainId, null, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback());
} }
public static TbMsg transformMsg(TbMsg tbMsg, String queueName) { public static TbMsg transformMsg(TbMsg tbMsg, String queueName) {
return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.metaData, tbMsg.dataType, return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType,
tbMsg.data, tbMsg.getRuleChainId(), null, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback()); tbMsg.data, tbMsg.getRuleChainId(), null, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback());
} }
public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, String queueName) { public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, String queueName) {
return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.metaData, tbMsg.dataType, return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType,
tbMsg.data, ruleChainId, null, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback()); tbMsg.data, ruleChainId, null, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback());
} }
public static TbMsg newMsg(TbMsg tbMsg, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { public static TbMsg newMsg(TbMsg tbMsg, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
return new TbMsg(tbMsg.getQueueName(), UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(), return new TbMsg(tbMsg.getQueueName(), UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.customerId, tbMsg.getMetaData().copy(),
tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, tbMsg.ruleNodeExecCounter.get(), TbMsgCallback.EMPTY); tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, tbMsg.ruleNodeExecCounter.get(), TbMsgCallback.EMPTY);
} }
private TbMsg(String queueName, UUID id, long ts, String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, private TbMsg(String queueName, UUID id, long ts, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data,
RuleChainId ruleChainId, RuleNodeId ruleNodeId, int ruleNodeExecCounter, TbMsgCallback callback) { RuleChainId ruleChainId, RuleNodeId ruleNodeId, int ruleNodeExecCounter, TbMsgCallback callback) {
this.id = id; this.id = id;
this.queueName = queueName != null ? queueName : ServiceQueue.MAIN; this.queueName = queueName != null ? queueName : ServiceQueue.MAIN;
@ -128,6 +152,15 @@ public final class TbMsg implements Serializable {
} }
this.type = type; this.type = type;
this.originator = originator; this.originator = originator;
if (customerId == null || customerId.isNullUid()) {
if (originator != null && originator.getEntityType() == EntityType.CUSTOMER) {
this.customerId = (CustomerId) originator;
} else {
this.customerId = null;
}
} else {
this.customerId = customerId;
}
this.metaData = metaData; this.metaData = metaData;
this.dataType = dataType; this.dataType = dataType;
this.data = data; this.data = data;
@ -154,6 +187,11 @@ public final class TbMsg implements Serializable {
builder.setEntityIdMSB(msg.getOriginator().getId().getMostSignificantBits()); builder.setEntityIdMSB(msg.getOriginator().getId().getMostSignificantBits());
builder.setEntityIdLSB(msg.getOriginator().getId().getLeastSignificantBits()); builder.setEntityIdLSB(msg.getOriginator().getId().getLeastSignificantBits());
if (msg.getCustomerId() != null) {
builder.setCustomerIdMSB(msg.getCustomerId().getId().getMostSignificantBits());
builder.setCustomerIdLSB(msg.getCustomerId().getId().getLeastSignificantBits());
}
if (msg.getRuleChainId() != null) { if (msg.getRuleChainId() != null) {
builder.setRuleChainIdMSB(msg.getRuleChainId().getId().getMostSignificantBits()); builder.setRuleChainIdMSB(msg.getRuleChainId().getId().getMostSignificantBits());
builder.setRuleChainIdLSB(msg.getRuleChainId().getId().getLeastSignificantBits()); builder.setRuleChainIdLSB(msg.getRuleChainId().getId().getLeastSignificantBits());
@ -179,16 +217,21 @@ public final class TbMsg implements Serializable {
MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(data); MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(data);
TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap()); TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap());
EntityId entityId = EntityIdFactory.getByTypeAndUuid(proto.getEntityType(), new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB())); EntityId entityId = EntityIdFactory.getByTypeAndUuid(proto.getEntityType(), new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB()));
CustomerId customerId = null;
RuleChainId ruleChainId = null; RuleChainId ruleChainId = null;
RuleNodeId ruleNodeId = null; RuleNodeId ruleNodeId = null;
if (proto.getCustomerIdMSB() != 0L && proto.getCustomerIdLSB() != 0L) {
customerId = new CustomerId(new UUID(proto.getCustomerIdMSB(), proto.getCustomerIdLSB()));
}
if (proto.getRuleChainIdMSB() != 0L && proto.getRuleChainIdLSB() != 0L) { if (proto.getRuleChainIdMSB() != 0L && proto.getRuleChainIdLSB() != 0L) {
ruleChainId = new RuleChainId(new UUID(proto.getRuleChainIdMSB(), proto.getRuleChainIdLSB())); ruleChainId = new RuleChainId(new UUID(proto.getRuleChainIdMSB(), proto.getRuleChainIdLSB()));
} }
if (proto.getRuleNodeIdMSB() != 0L && proto.getRuleNodeIdLSB() != 0L) { if (proto.getRuleNodeIdMSB() != 0L && proto.getRuleNodeIdLSB() != 0L) {
ruleNodeId = new RuleNodeId(new UUID(proto.getRuleNodeIdMSB(), proto.getRuleNodeIdLSB())); ruleNodeId = new RuleNodeId(new UUID(proto.getRuleNodeIdMSB(), proto.getRuleNodeIdLSB()));
} }
TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()]; TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()];
return new TbMsg(queueName, UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, proto.getRuleNodeExecCounter(), callback); return new TbMsg(queueName, UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, customerId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, proto.getRuleNodeExecCounter(), callback);
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
throw new IllegalStateException("Could not parse protobuf for TbMsg", e); throw new IllegalStateException("Could not parse protobuf for TbMsg", e);
} }
@ -199,11 +242,11 @@ public final class TbMsg implements Serializable {
} }
public TbMsg copyWithRuleChainId(RuleChainId ruleChainId, UUID msgId) { public TbMsg copyWithRuleChainId(RuleChainId ruleChainId, UUID msgId) {
return new TbMsg(this.queueName, msgId, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, null, this.ruleNodeExecCounter.get(), callback); return new TbMsg(this.queueName, msgId, this.ts, this.type, this.originator, this.customerId, this.metaData, this.dataType, this.data, ruleChainId, null, this.ruleNodeExecCounter.get(), callback);
} }
public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId, UUID msgId) { public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId, UUID msgId) {
return new TbMsg(this.queueName, msgId, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.ruleNodeExecCounter.get(), callback); return new TbMsg(this.queueName, msgId, this.ts, this.type, this.originator, this.customerId, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.ruleNodeExecCounter.get(), callback);
} }
public TbMsgCallback getCallback() { public TbMsgCallback getCallback() {

3
common/message/src/main/proto/tbmsg.proto

@ -46,4 +46,7 @@ message TbMsgProto {
int64 ts = 15; int64 ts = 15;
int32 ruleNodeExecCounter = 16; int32 ruleNodeExecCounter = 16;
int64 customerIdMSB = 17;
int64 customerIdLSB = 18;
} }

116
common/queue/src/main/java/org/thingsboard/server/queue/usagestats/DefaultTbApiUsageClient.java

@ -15,10 +15,14 @@
*/ */
package org.thingsboard.server.queue.usagestats; package org.thingsboard.server.queue.usagestats;
import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
@ -31,6 +35,8 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.queue.scheduler.SchedulerComponent; import org.thingsboard.server.queue.scheduler.SchedulerComponent;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.EnumMap;
import java.util.Optional;
import java.util.Random; import java.util.Random;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -44,11 +50,13 @@ public class DefaultTbApiUsageClient implements TbApiUsageClient {
@Value("${usage.stats.report.enabled:true}") @Value("${usage.stats.report.enabled:true}")
private boolean enabled; private boolean enabled;
@Value("${usage.stats.report.enabled_per_customer:false}")
private boolean enabledPerCustomer;
@Value("${usage.stats.report.interval:10}") @Value("${usage.stats.report.interval:10}")
private int interval; private int interval;
@SuppressWarnings("unchecked") private final EnumMap<ApiUsageRecordKey, ConcurrentMap<OwnerId, AtomicLong>> stats = new EnumMap<>(ApiUsageRecordKey.class);
private final ConcurrentMap<TenantId, AtomicLong>[] values = new ConcurrentMap[ApiUsageRecordKey.values().length];
private final PartitionService partitionService; private final PartitionService partitionService;
private final SchedulerComponent scheduler; private final SchedulerComponent scheduler;
private final TbQueueProducerProvider producerProvider; private final TbQueueProducerProvider producerProvider;
@ -65,55 +73,93 @@ public class DefaultTbApiUsageClient implements TbApiUsageClient {
if (enabled) { if (enabled) {
msgProducer = this.producerProvider.getTbUsageStatsMsgProducer(); msgProducer = this.producerProvider.getTbUsageStatsMsgProducer();
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
values[key.ordinal()] = new ConcurrentHashMap<>(); stats.put(key, new ConcurrentHashMap<>());
} }
scheduler.scheduleWithFixedDelay(this::reportStats, new Random().nextInt(interval), interval, TimeUnit.SECONDS); scheduler.scheduleWithFixedDelay(() -> {
try {
reportStats();
} catch (Exception e) {
log.warn("Failed to report statistics: ", e);
}
}, new Random().nextInt(interval), interval, TimeUnit.SECONDS);
} }
} }
private void reportStats() { private void reportStats() {
try { ConcurrentMap<OwnerId, ToUsageStatsServiceMsg.Builder> report = new ConcurrentHashMap<>();
ConcurrentMap<TenantId, ToUsageStatsServiceMsg.Builder> report = new ConcurrentHashMap<>();
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
values[key.ordinal()].forEach(((tenantId, atomicLong) -> { ConcurrentMap<OwnerId, AtomicLong> statsForKey = stats.get(key);
long value = atomicLong.getAndSet(0); statsForKey.forEach((ownerId, statsValue) -> {
if (value > 0) { long value = statsValue.get();
ToUsageStatsServiceMsg.Builder msgBuilder = report.computeIfAbsent(tenantId, id -> { if (value == 0) return;
ToUsageStatsServiceMsg.Builder msg = ToUsageStatsServiceMsg.newBuilder();
msg.setTenantIdMSB(tenantId.getId().getMostSignificantBits()); ToUsageStatsServiceMsg.Builder statsMsgBuilder = report.computeIfAbsent(ownerId, id -> {
msg.setTenantIdLSB(tenantId.getId().getLeastSignificantBits()); ToUsageStatsServiceMsg.Builder newStatsMsgBuilder = ToUsageStatsServiceMsg.newBuilder();
return msg;
}); TenantId tenantId = ownerId.getTenantId();
msgBuilder.addValues(UsageStatsKVProto.newBuilder().setKey(key.name()).setValue(value).build()); newStatsMsgBuilder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
newStatsMsgBuilder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
EntityId entityId = ownerId.getEntityId();
if (entityId != null && entityId.getEntityType() == EntityType.CUSTOMER) {
newStatsMsgBuilder.setCustomerIdMSB(entityId.getId().getMostSignificantBits());
newStatsMsgBuilder.setCustomerIdLSB(entityId.getId().getLeastSignificantBits());
} }
}));
}
report.forEach(((tenantId, builder) -> { return newStatsMsgBuilder;
//TODO: figure out how to minimize messages into the queue. Maybe group by 100s of messages? });
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).newByTopic(msgProducer.getDefaultTopic());
msgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), builder.build()), null); statsMsgBuilder.addValues(UsageStatsKVProto.newBuilder().setKey(key.name()).setValue(value).build());
})); });
if (!report.isEmpty()) { statsForKey.clear();
log.info("Report statistics for: {} tenants", report.size()); }
}
} catch (Exception e) { report.forEach(((ownerId, statsMsg) -> {
log.warn("Failed to report statistics: ", e); //TODO: figure out how to minimize messages into the queue. Maybe group by 100s of messages?
TenantId tenantId = ownerId.getTenantId();
EntityId entityId = Optional.ofNullable(ownerId.getEntityId()).orElse(tenantId);
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId).newByTopic(msgProducer.getDefaultTopic());
msgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), statsMsg.build()), null);
}));
if (!report.isEmpty()) {
log.info("Reporting API usage statistics for {} tenants and customers", report.size());
} }
} }
@Override @Override
public void report(TenantId tenantId, ApiUsageRecordKey key, long value) { public void report(TenantId tenantId, CustomerId customerId, ApiUsageRecordKey key, long value) {
if (enabled) { if (enabled) {
ConcurrentMap<TenantId, AtomicLong> map = values[key.ordinal()]; ConcurrentMap<OwnerId, AtomicLong> statsForKey = stats.get(key);
AtomicLong atomicValue = map.computeIfAbsent(tenantId, id -> new AtomicLong());
atomicValue.addAndGet(value); statsForKey.computeIfAbsent(new OwnerId(tenantId), id -> new AtomicLong()).addAndGet(value);
statsForKey.computeIfAbsent(new OwnerId(TenantId.SYS_TENANT_ID), id -> new AtomicLong()).addAndGet(value);
if (enabledPerCustomer && customerId != null && !customerId.isNullUid()) {
statsForKey.computeIfAbsent(new OwnerId(tenantId, customerId), id -> new AtomicLong()).addAndGet(value);
}
} }
} }
@Override @Override
public void report(TenantId tenantId, ApiUsageRecordKey key) { public void report(TenantId tenantId, CustomerId customerId, ApiUsageRecordKey key) {
report(tenantId, key, 1L); report(tenantId, customerId, key, 1);
}
@Data
private static class OwnerId {
private TenantId tenantId;
private EntityId entityId;
public OwnerId(TenantId tenantId) {
this.tenantId = tenantId;
}
public OwnerId(TenantId tenantId, EntityId entityId) {
this.tenantId = tenantId;
this.entityId = entityId;
}
} }
} }

5
common/queue/src/main/java/org/thingsboard/server/queue/usagestats/TbApiUsageClient.java

@ -16,12 +16,13 @@
package org.thingsboard.server.queue.usagestats; package org.thingsboard.server.queue.usagestats;
import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
public interface TbApiUsageClient { public interface TbApiUsageClient {
void report(TenantId tenantId, ApiUsageRecordKey key, long value); void report(TenantId tenantId, CustomerId customerId, ApiUsageRecordKey key, long value);
void report(TenantId tenantId, ApiUsageRecordKey key); void report(TenantId tenantId, CustomerId customerId, ApiUsageRecordKey key);
} }

6
common/queue/src/main/proto/queue.proto

@ -53,6 +53,8 @@ message SessionInfoProto {
int64 gwSessionIdLSB = 11; int64 gwSessionIdLSB = 11;
int64 deviceProfileIdMSB = 12; int64 deviceProfileIdMSB = 12;
int64 deviceProfileIdLSB = 13; int64 deviceProfileIdLSB = 13;
int64 customerIdMSB = 14;
int64 customerIdLSB = 15;
} }
enum SessionEvent { enum SessionEvent {
@ -110,6 +112,8 @@ message DeviceInfoProto {
string additionalInfo = 7; string additionalInfo = 7;
int64 deviceProfileIdMSB = 8; int64 deviceProfileIdMSB = 8;
int64 deviceProfileIdLSB = 9; int64 deviceProfileIdLSB = 9;
int64 customerIdMSB = 10;
int64 customerIdLSB = 11;
} }
/** /**
@ -656,4 +660,6 @@ message ToUsageStatsServiceMsg {
int64 entityIdMSB = 3; int64 entityIdMSB = 3;
int64 entityIdLSB = 4; int64 entityIdLSB = 4;
repeated UsageStatsKVProto values = 5; repeated UsageStatsKVProto values = 5;
int64 customerIdMSB = 6;
int64 customerIdLSB = 7;
} }

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContextServer.java

@ -141,6 +141,8 @@ public class LwM2mTransportContextServer extends TransportContext {
.setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB()) .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
.setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB()) .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
.setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB()) .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
.setCustomerIdMSB(msg.getDeviceInfo().getCustomerIdMSB())
.setCustomerIdLSB(msg.getDeviceInfo().getCustomerIdLSB())
.setDeviceName(msg.getDeviceInfo().getDeviceName()) .setDeviceName(msg.getDeviceInfo().getDeviceName())
.setDeviceType(msg.getDeviceInfo().getDeviceType()) .setDeviceType(msg.getDeviceInfo().getDeviceType())
.setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB()) .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java

@ -1177,6 +1177,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
.setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB()) .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
.setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB()) .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
.setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB()) .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
.setCustomerIdMSB(msg.getDeviceInfo().getCustomerIdMSB())
.setCustomerIdLSB(msg.getDeviceInfo().getCustomerIdLSB())
.setDeviceName(msg.getDeviceInfo().getDeviceName()) .setDeviceName(msg.getDeviceInfo().getDeviceName())
.setDeviceType(msg.getDeviceInfo().getDeviceType()) .setDeviceType(msg.getDeviceInfo().getDeviceType())
.setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB()) .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())

2
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java

@ -45,6 +45,8 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple
.setDeviceIdLSB(deviceInfo.getDeviceId().getId().getLeastSignificantBits()) .setDeviceIdLSB(deviceInfo.getDeviceId().getId().getLeastSignificantBits())
.setTenantIdMSB(deviceInfo.getTenantId().getId().getMostSignificantBits()) .setTenantIdMSB(deviceInfo.getTenantId().getId().getMostSignificantBits())
.setTenantIdLSB(deviceInfo.getTenantId().getId().getLeastSignificantBits()) .setTenantIdLSB(deviceInfo.getTenantId().getId().getLeastSignificantBits())
.setCustomerIdMSB(deviceInfo.getCustomerId().getId().getMostSignificantBits())
.setCustomerIdLSB(deviceInfo.getCustomerId().getId().getLeastSignificantBits())
.setDeviceName(deviceInfo.getDeviceName()) .setDeviceName(deviceInfo.getDeviceName())
.setDeviceType(deviceInfo.getDeviceType()) .setDeviceType(deviceInfo.getDeviceType())
.setGwSessionIdMSB(parent.getSessionId().getMostSignificantBits()) .setGwSessionIdMSB(parent.getSessionId().getMostSignificantBits())

2
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/SessionInfoCreator.java

@ -40,6 +40,8 @@ public class SessionInfoCreator {
.setDeviceIdLSB(msg.getDeviceInfo().getDeviceId().getId().getLeastSignificantBits()) .setDeviceIdLSB(msg.getDeviceInfo().getDeviceId().getId().getLeastSignificantBits())
.setTenantIdMSB(msg.getDeviceInfo().getTenantId().getId().getMostSignificantBits()) .setTenantIdMSB(msg.getDeviceInfo().getTenantId().getId().getMostSignificantBits())
.setTenantIdLSB(msg.getDeviceInfo().getTenantId().getId().getLeastSignificantBits()) .setTenantIdLSB(msg.getDeviceInfo().getTenantId().getId().getLeastSignificantBits())
.setCustomerIdMSB(msg.getDeviceInfo().getCustomerId().getId().getMostSignificantBits())
.setCustomerIdLSB(msg.getDeviceInfo().getCustomerId().getId().getLeastSignificantBits())
.setDeviceName(msg.getDeviceInfo().getDeviceName()) .setDeviceName(msg.getDeviceInfo().getDeviceName())
.setDeviceType(msg.getDeviceInfo().getDeviceType()) .setDeviceType(msg.getDeviceInfo().getDeviceType())
.setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileId().getId().getMostSignificantBits()) .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileId().getId().getMostSignificantBits())

2
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/TransportDeviceInfo.java

@ -16,6 +16,7 @@
package org.thingsboard.server.common.transport.auth; package org.thingsboard.server.common.transport.auth;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -24,6 +25,7 @@ import org.thingsboard.server.common.data.id.TenantId;
public class TransportDeviceInfo { public class TransportDeviceInfo {
private TenantId tenantId; private TenantId tenantId;
private CustomerId customerId;
private DeviceProfileId deviceProfileId; private DeviceProfileId deviceProfileId;
private DeviceId deviceId; private DeviceId deviceId;
private String deviceName; private String deviceName;

56
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java

@ -33,8 +33,10 @@ import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.TenantProfileId;
@ -359,6 +361,7 @@ public class DefaultTransportService implements TransportService {
private TransportDeviceInfo getTransportDeviceInfo(TransportProtos.DeviceInfoProto di) { private TransportDeviceInfo getTransportDeviceInfo(TransportProtos.DeviceInfoProto di) {
TransportDeviceInfo tdi = new TransportDeviceInfo(); TransportDeviceInfo tdi = new TransportDeviceInfo();
tdi.setTenantId(new TenantId(new UUID(di.getTenantIdMSB(), di.getTenantIdLSB()))); tdi.setTenantId(new TenantId(new UUID(di.getTenantIdMSB(), di.getTenantIdLSB())));
tdi.setCustomerId(new CustomerId(new UUID(di.getCustomerIdMSB(), di.getCustomerIdLSB())));
tdi.setDeviceId(new DeviceId(new UUID(di.getDeviceIdMSB(), di.getDeviceIdLSB()))); tdi.setDeviceId(new DeviceId(new UUID(di.getDeviceIdMSB(), di.getDeviceIdLSB())));
tdi.setDeviceProfileId(new DeviceProfileId(new UUID(di.getDeviceProfileIdMSB(), di.getDeviceProfileIdLSB()))); tdi.setDeviceProfileId(new DeviceProfileId(new UUID(di.getDeviceProfileIdMSB(), di.getDeviceProfileIdLSB())));
tdi.setAdditionalInfo(di.getAdditionalInfo()); tdi.setAdditionalInfo(di.getAdditionalInfo());
@ -403,17 +406,17 @@ public class DefaultTransportService implements TransportService {
} }
if (checkLimits(sessionInfo, msg, callback, dataPoints)) { if (checkLimits(sessionInfo, msg, callback, dataPoints)) {
reportActivityInternal(sessionInfo); reportActivityInternal(sessionInfo);
TenantId tenantId = new TenantId(new UUID(sessionInfo.getTenantIdMSB(), sessionInfo.getTenantIdLSB())); TenantId tenantId = getTenantId(sessionInfo);
DeviceId deviceId = new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); DeviceId deviceId = new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()));
MsgPackCallback packCallback = new MsgPackCallback(msg.getTsKvListCount(), new ApiStatsProxyCallback<>(tenantId, dataPoints, callback)); CustomerId customerId = getCustomerId(sessionInfo);
MsgPackCallback packCallback = new MsgPackCallback(msg.getTsKvListCount(), new ApiStatsProxyCallback<>(tenantId, customerId, dataPoints, callback));
for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) { for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) {
TbMsgMetaData metaData = new TbMsgMetaData(); TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("deviceName", sessionInfo.getDeviceName()); metaData.putValue("deviceName", sessionInfo.getDeviceName());
metaData.putValue("deviceType", sessionInfo.getDeviceType()); metaData.putValue("deviceType", sessionInfo.getDeviceType());
metaData.putValue("ts", tsKv.getTs() + ""); metaData.putValue("ts", tsKv.getTs() + "");
JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList());
sendToRuleEngine(tenantId, deviceId, sessionInfo, json, metaData, SessionMsgType.POST_TELEMETRY_REQUEST, packCallback); sendToRuleEngine(tenantId, deviceId, customerId, sessionInfo, json, metaData, SessionMsgType.POST_TELEMETRY_REQUEST, packCallback);
} }
} }
} }
@ -422,15 +425,16 @@ public class DefaultTransportService implements TransportService {
public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.PostAttributeMsg msg, TransportServiceCallback<Void> callback) { public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.PostAttributeMsg msg, TransportServiceCallback<Void> callback) {
if (checkLimits(sessionInfo, msg, callback, msg.getKvCount())) { if (checkLimits(sessionInfo, msg, callback, msg.getKvCount())) {
reportActivityInternal(sessionInfo); reportActivityInternal(sessionInfo);
TenantId tenantId = new TenantId(new UUID(sessionInfo.getTenantIdMSB(), sessionInfo.getTenantIdLSB())); TenantId tenantId = getTenantId(sessionInfo);
DeviceId deviceId = new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); DeviceId deviceId = new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()));
JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
TbMsgMetaData metaData = new TbMsgMetaData(); TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("deviceName", sessionInfo.getDeviceName()); metaData.putValue("deviceName", sessionInfo.getDeviceName());
metaData.putValue("deviceType", sessionInfo.getDeviceType()); metaData.putValue("deviceType", sessionInfo.getDeviceType());
metaData.putValue("notifyDevice", "false"); metaData.putValue("notifyDevice", "false");
sendToRuleEngine(tenantId, deviceId, sessionInfo, json, metaData, SessionMsgType.POST_ATTRIBUTES_REQUEST, CustomerId customerId = getCustomerId(sessionInfo);
new TransportTbQueueCallback(new ApiStatsProxyCallback<>(tenantId, msg.getKvList().size(), callback))); sendToRuleEngine(tenantId, deviceId, customerId, sessionInfo, json, metaData, SessionMsgType.POST_ATTRIBUTES_REQUEST,
new TransportTbQueueCallback(new ApiStatsProxyCallback<>(tenantId, customerId, msg.getKvList().size(), callback)));
} }
} }
@ -439,7 +443,7 @@ public class DefaultTransportService implements TransportService {
if (checkLimits(sessionInfo, msg, callback)) { if (checkLimits(sessionInfo, msg, callback)) {
reportActivityInternal(sessionInfo); reportActivityInternal(sessionInfo);
sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
.setGetAttributes(msg).build(), new ApiStatsProxyCallback<>(getTenantId(sessionInfo), 1, callback)); .setGetAttributes(msg).build(), new ApiStatsProxyCallback<>(getTenantId(sessionInfo), getCustomerId(sessionInfo), 1, callback));
} }
} }
@ -448,8 +452,8 @@ public class DefaultTransportService implements TransportService {
if (checkLimits(sessionInfo, msg, callback)) { if (checkLimits(sessionInfo, msg, callback)) {
SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo); SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo);
sessionMetaData.setSubscribedToAttributes(!msg.getUnsubscribe()); sessionMetaData.setSubscribedToAttributes(!msg.getUnsubscribe());
sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToAttributes(msg).build(),
.setSubscribeToAttributes(msg).build(), new ApiStatsProxyCallback<>(getTenantId(sessionInfo), 1, callback)); new ApiStatsProxyCallback<>(getTenantId(sessionInfo), getCustomerId(sessionInfo), 1, callback));
} }
} }
@ -458,8 +462,8 @@ public class DefaultTransportService implements TransportService {
if (checkLimits(sessionInfo, msg, callback)) { if (checkLimits(sessionInfo, msg, callback)) {
SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo); SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo);
sessionMetaData.setSubscribedToRPC(!msg.getUnsubscribe()); sessionMetaData.setSubscribedToRPC(!msg.getUnsubscribe());
sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToRPC(msg).build(),
.setSubscribeToRPC(msg).build(), new ApiStatsProxyCallback<>(getTenantId(sessionInfo), 1, callback)); new ApiStatsProxyCallback<>(getTenantId(sessionInfo), getCustomerId(sessionInfo), 1, callback));
} }
} }
@ -467,8 +471,8 @@ public class DefaultTransportService implements TransportService {
public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback) { public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback) {
if (checkLimits(sessionInfo, msg, callback)) { if (checkLimits(sessionInfo, msg, callback)) {
reportActivityInternal(sessionInfo); reportActivityInternal(sessionInfo);
sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToDeviceRPCCallResponse(msg).build(),
.setToDeviceRPCCallResponse(msg).build(), new ApiStatsProxyCallback<>(getTenantId(sessionInfo), 1, callback)); new ApiStatsProxyCallback<>(getTenantId(sessionInfo), getCustomerId(sessionInfo), 1, callback));
} }
} }
@ -511,7 +515,7 @@ public class DefaultTransportService implements TransportService {
metaData.putValue("requestId", Integer.toString(msg.getRequestId())); metaData.putValue("requestId", Integer.toString(msg.getRequestId()));
metaData.putValue("serviceId", serviceInfoProvider.getServiceId()); metaData.putValue("serviceId", serviceInfoProvider.getServiceId());
metaData.putValue("sessionId", sessionId.toString()); metaData.putValue("sessionId", sessionId.toString());
sendToRuleEngine(tenantId, deviceId, sessionInfo, json, metaData, sendToRuleEngine(tenantId, deviceId, getCustomerId(sessionInfo), sessionInfo, json, metaData,
SessionMsgType.TO_SERVER_RPC_REQUEST, new TransportTbQueueCallback(callback)); SessionMsgType.TO_SERVER_RPC_REQUEST, new TransportTbQueueCallback(callback));
String requestId = sessionId + "-" + msg.getRequestId(); String requestId = sessionId + "-" + msg.getRequestId();
toServerRpcPendingMap.put(requestId, new RpcRequestMetadata(sessionId, msg.getRequestId())); toServerRpcPendingMap.put(requestId, new RpcRequestMetadata(sessionId, msg.getRequestId()));
@ -809,6 +813,16 @@ public class DefaultTransportService implements TransportService {
return new TenantId(new UUID(sessionInfo.getTenantIdMSB(), sessionInfo.getTenantIdLSB())); return new TenantId(new UUID(sessionInfo.getTenantIdMSB(), sessionInfo.getTenantIdLSB()));
} }
protected CustomerId getCustomerId(TransportProtos.SessionInfoProto sessionInfo) {
long msb = sessionInfo.getCustomerIdMSB();
long lsb = sessionInfo.getCustomerIdLSB();
if (msb != 0 && lsb != 0) {
return new CustomerId(new UUID(msb, lsb));
} else {
return new CustomerId(EntityId.NULL_UUID);
}
}
protected DeviceId getDeviceId(TransportProtos.SessionInfoProto sessionInfo) { protected DeviceId getDeviceId(TransportProtos.SessionInfoProto sessionInfo) {
return new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); return new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()));
} }
@ -847,7 +861,7 @@ public class DefaultTransportService implements TransportService {
ruleEngineMsgProducer.send(tpi, new TbProtoQueueMsg<>(tbMsg.getId(), msg), wrappedCallback); ruleEngineMsgProducer.send(tpi, new TbProtoQueueMsg<>(tbMsg.getId(), msg), wrappedCallback);
} }
private void sendToRuleEngine(TenantId tenantId, DeviceId deviceId, TransportProtos.SessionInfoProto sessionInfo, JsonObject json, private void sendToRuleEngine(TenantId tenantId, DeviceId deviceId, CustomerId customerId, TransportProtos.SessionInfoProto sessionInfo, JsonObject json,
TbMsgMetaData metaData, SessionMsgType sessionMsgType, TbQueueCallback callback) { TbMsgMetaData metaData, SessionMsgType sessionMsgType, TbQueueCallback callback) {
DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId); DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId);
@ -864,7 +878,7 @@ public class DefaultTransportService implements TransportService {
queueName = defaultQueueName != null ? defaultQueueName : ServiceQueue.MAIN; queueName = defaultQueueName != null ? defaultQueueName : ServiceQueue.MAIN;
} }
TbMsg tbMsg = TbMsg.newMsg(queueName, sessionMsgType.name(), deviceId, metaData, gson.toJson(json), ruleChainId, null); TbMsg tbMsg = TbMsg.newMsg(queueName, sessionMsgType.name(), deviceId, customerId, metaData, gson.toJson(json), ruleChainId, null);
sendToRuleEngine(tenantId, tbMsg, callback); sendToRuleEngine(tenantId, tbMsg, callback);
} }
@ -934,11 +948,13 @@ public class DefaultTransportService implements TransportService {
private class ApiStatsProxyCallback<T> implements TransportServiceCallback<T> { private class ApiStatsProxyCallback<T> implements TransportServiceCallback<T> {
private final TenantId tenantId; private final TenantId tenantId;
private final CustomerId customerId;
private final int dataPoints; private final int dataPoints;
private final TransportServiceCallback<T> callback; private final TransportServiceCallback<T> callback;
public ApiStatsProxyCallback(TenantId tenantId, int dataPoints, TransportServiceCallback<T> callback) { public ApiStatsProxyCallback(TenantId tenantId, CustomerId customerId, int dataPoints, TransportServiceCallback<T> callback) {
this.tenantId = tenantId; this.tenantId = tenantId;
this.customerId = customerId;
this.dataPoints = dataPoints; this.dataPoints = dataPoints;
this.callback = callback; this.callback = callback;
} }
@ -946,8 +962,8 @@ public class DefaultTransportService implements TransportService {
@Override @Override
public void onSuccess(T msg) { public void onSuccess(T msg) {
try { try {
apiUsageClient.report(tenantId, ApiUsageRecordKey.TRANSPORT_MSG_COUNT, 1); apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.TRANSPORT_MSG_COUNT, 1);
apiUsageClient.report(tenantId, ApiUsageRecordKey.TRANSPORT_DP_COUNT, dataPoints); apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.TRANSPORT_DP_COUNT, dataPoints);
} finally { } finally {
callback.onSuccess(msg); callback.onSuccess(msg);
} }

5
dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java

@ -43,6 +43,7 @@ import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.service.Validator;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.tenant.TenantDao; import org.thingsboard.server.dao.tenant.TenantDao;
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.user.UserService;
import java.io.IOException; import java.io.IOException;
@ -79,6 +80,9 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
@Autowired @Autowired
private DashboardService dashboardService; private DashboardService dashboardService;
@Autowired
private ApiUsageStateService apiUsageStateService;
@Autowired @Autowired
@Lazy @Lazy
private TbTenantProfileCache tenantProfileCache; private TbTenantProfileCache tenantProfileCache;
@ -128,6 +132,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
edgeService.unassignCustomerEdges(customer.getTenantId(), customerId); edgeService.unassignCustomerEdges(customer.getTenantId(), customerId);
userService.deleteCustomerUsers(customer.getTenantId(), customerId); userService.deleteCustomerUsers(customer.getTenantId(), customerId);
deleteEntityRelations(tenantId, customerId); deleteEntityRelations(tenantId, customerId);
apiUsageStateService.deleteApiUsageStateByEntityId(customerId);
customerDao.removeById(tenantId, customerId.getId()); customerDao.removeById(tenantId, customerId.getId());
} }

22
dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java

@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.query.ApiUsageStateFilter;
import org.thingsboard.server.common.data.query.AssetSearchQueryFilter; import org.thingsboard.server.common.data.query.AssetSearchQueryFilter;
import org.thingsboard.server.common.data.query.AssetTypeFilter; import org.thingsboard.server.common.data.query.AssetTypeFilter;
import org.thingsboard.server.common.data.query.DeviceSearchQueryFilter; import org.thingsboard.server.common.data.query.DeviceSearchQueryFilter;
@ -219,8 +220,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
" THEN (select additional_info from edge where id = entity_id)" + " THEN (select additional_info from edge where id = entity_id)" +
" END as additional_info"; " END as additional_info";
private static final String SELECT_API_USAGE_STATE = "(select aus.id, aus.created_time, aus.tenant_id, '13814000-1dd2-11b2-8080-808080808080'::uuid as customer_id, " + private static final String SELECT_API_USAGE_STATE = "(select aus.id, aus.created_time, aus.tenant_id, aus.entity_id, " +
"(select title from tenant where id = aus.tenant_id) as name from api_usage_state as aus)"; "coalesce((select title from tenant where id = aus.entity_id), (select title from customer where id = aus.entity_id)) as name " +
"from api_usage_state as aus)";
static { static {
entityTableMap.put(EntityType.ASSET, "asset"); entityTableMap.put(EntityType.ASSET, "asset");
@ -466,6 +468,22 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
case ENTITY_VIEW_SEARCH_QUERY: case ENTITY_VIEW_SEARCH_QUERY:
case EDGE_SEARCH_QUERY: case EDGE_SEARCH_QUERY:
return this.defaultPermissionQuery(ctx); return this.defaultPermissionQuery(ctx);
case API_USAGE_STATE:
CustomerId filterCustomerId = ((ApiUsageStateFilter) entityFilter).getCustomerId();
if (ctx.getCustomerId() != null && !ctx.getCustomerId().isNullUid()) {
if (filterCustomerId != null && !filterCustomerId.equals(ctx.getCustomerId())) {
throw new SecurityException("Customer is not allowed to query other customer's data");
}
filterCustomerId = ctx.getCustomerId();
}
ctx.addUuidParameter("permissions_tenant_id", ctx.getTenantId().getId());
if (filterCustomerId != null) {
ctx.addUuidParameter("permissions_customer_id", filterCustomerId.getId());
return "e.tenant_id=:permissions_tenant_id and e.entity_id=:permissions_customer_id";
} else {
return "e.tenant_id=:permissions_tenant_id and e.entity_id=:permissions_tenant_id";
}
default: default:
if (ctx.getEntityType() == EntityType.TENANT) { if (ctx.getEntityType() == EntityType.TENANT) {
ctx.addUuidParameter("permissions_tenant_id", ctx.getTenantId().getId()); ctx.addUuidParameter("permissions_tenant_id", ctx.getTenantId().getId());

6
dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/ApiUsageStateRepository.java

@ -33,8 +33,14 @@ public interface ApiUsageStateRepository extends CrudRepository<ApiUsageStateEnt
"AND ur.entityId = :tenantId AND ur.entityType = 'TENANT' ") "AND ur.entityId = :tenantId AND ur.entityType = 'TENANT' ")
ApiUsageStateEntity findByTenantId(@Param("tenantId") UUID tenantId); ApiUsageStateEntity findByTenantId(@Param("tenantId") UUID tenantId);
ApiUsageStateEntity findByEntityIdAndEntityType(UUID entityId, String entityType);
@Transactional @Transactional
@Modifying @Modifying
@Query("DELETE FROM ApiUsageStateEntity ur WHERE ur.tenantId = :tenantId") @Query("DELETE FROM ApiUsageStateEntity ur WHERE ur.tenantId = :tenantId")
void deleteApiUsageStateByTenantId(@Param("tenantId") UUID tenantId); void deleteApiUsageStateByTenantId(@Param("tenantId") UUID tenantId);
@Transactional
@Modifying
void deleteByEntityIdAndEntityType(UUID entityId, String entityType);
} }

11
dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/JpaApiUsageStateDao.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.usagerecord;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.sql.ApiUsageStateEntity; import org.thingsboard.server.dao.model.sql.ApiUsageStateEntity;
@ -53,8 +54,18 @@ public class JpaApiUsageStateDao extends JpaAbstractDao<ApiUsageStateEntity, Api
return DaoUtil.getData(apiUsageStateRepository.findByTenantId(tenantId)); return DaoUtil.getData(apiUsageStateRepository.findByTenantId(tenantId));
} }
@Override
public ApiUsageState findApiUsageStateByEntityId(EntityId entityId) {
return DaoUtil.getData(apiUsageStateRepository.findByEntityIdAndEntityType(entityId.getId(), entityId.getEntityType().name()));
}
@Override @Override
public void deleteApiUsageStateByTenantId(TenantId tenantId) { public void deleteApiUsageStateByTenantId(TenantId tenantId) {
apiUsageStateRepository.deleteApiUsageStateByTenantId(tenantId.getId()); apiUsageStateRepository.deleteApiUsageStateByTenantId(tenantId.getId());
} }
@Override
public void deleteApiUsageStateByEntityId(EntityId entityId) {
apiUsageStateRepository.deleteByEntityIdAndEntityType(entityId.getId(), entityId.getEntityType().name());
}
} }

2
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java

@ -125,7 +125,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe
Tenant savedTenant = tenantDao.save(tenant.getId(), tenant); Tenant savedTenant = tenantDao.save(tenant.getId(), tenant);
if (tenant.getId() == null) { if (tenant.getId() == null) {
deviceProfileService.createDefaultDeviceProfile(savedTenant.getId()); deviceProfileService.createDefaultDeviceProfile(savedTenant.getId());
apiUsageStateService.createDefaultApiUsageState(savedTenant.getId()); apiUsageStateService.createDefaultApiUsageState(savedTenant.getId(), null);
} }
return savedTenant; return savedTenant;
} }

5
dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateDao.java

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.usagerecord; package org.thingsboard.server.dao.usagerecord;
import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.Dao;
@ -39,10 +40,14 @@ public interface ApiUsageStateDao extends Dao<ApiUsageState> {
*/ */
ApiUsageState findTenantApiUsageState(UUID tenantId); ApiUsageState findTenantApiUsageState(UUID tenantId);
ApiUsageState findApiUsageStateByEntityId(EntityId entityId);
/** /**
* Delete usage record by tenantId. * Delete usage record by tenantId.
* *
* @param tenantId the tenantId * @param tenantId the tenantId
*/ */
void deleteApiUsageStateByTenantId(TenantId tenantId); void deleteApiUsageStateByTenantId(TenantId tenantId);
void deleteApiUsageStateByEntityId(EntityId entityId);
} }

52
dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java

@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.ApiUsageStateId; import org.thingsboard.server.common.data.id.ApiUsageStateId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry;
@ -40,6 +41,7 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateId;
@ -68,12 +70,20 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
} }
@Override @Override
public ApiUsageState createDefaultApiUsageState(TenantId tenantId) { public void deleteApiUsageStateByEntityId(EntityId entityId) {
log.trace("Executing createDefaultUsageRecord [{}]", tenantId); log.trace("Executing deleteApiUsageStateByEntityId [{}]", entityId);
validateId(entityId.getId(), "Invalid entity id");
apiUsageStateDao.deleteApiUsageStateByEntityId(entityId);
}
@Override
public ApiUsageState createDefaultApiUsageState(TenantId tenantId, EntityId entityId) {
entityId = Objects.requireNonNullElse(entityId, tenantId);
log.trace("Executing createDefaultUsageRecord [{}]", entityId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
ApiUsageState apiUsageState = new ApiUsageState(); ApiUsageState apiUsageState = new ApiUsageState();
apiUsageState.setTenantId(tenantId); apiUsageState.setTenantId(tenantId);
apiUsageState.setEntityId(tenantId); apiUsageState.setEntityId(entityId);
apiUsageState.setTransportState(ApiUsageStateValue.ENABLED); apiUsageState.setTransportState(ApiUsageStateValue.ENABLED);
apiUsageState.setReExecState(ApiUsageStateValue.ENABLED); apiUsageState.setReExecState(ApiUsageStateValue.ENABLED);
apiUsageState.setJsExecState(ApiUsageStateValue.ENABLED); apiUsageState.setJsExecState(ApiUsageStateValue.ENABLED);
@ -84,9 +94,6 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
ApiUsageState saved = apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState); ApiUsageState saved = apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState);
Tenant tenant = tenantDao.findById(tenantId, tenantId.getId());
TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenant.getTenantProfileId().getId());
TenantProfileConfiguration configuration = tenantProfile.getProfileData().getConfiguration();
List<TsKvEntry> apiUsageStates = new ArrayList<>(); List<TsKvEntry> apiUsageStates = new ArrayList<>();
apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(), apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(),
new StringDataEntry(ApiFeature.TRANSPORT.getApiStateKey(), ApiUsageStateValue.ENABLED.name()))); new StringDataEntry(ApiFeature.TRANSPORT.getApiStateKey(), ApiUsageStateValue.ENABLED.name())));
@ -102,12 +109,19 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
new StringDataEntry(ApiFeature.SMS.getApiStateKey(), ApiUsageStateValue.ENABLED.name()))); new StringDataEntry(ApiFeature.SMS.getApiStateKey(), ApiUsageStateValue.ENABLED.name())));
tsService.save(tenantId, saved.getId(), apiUsageStates, 0L); tsService.save(tenantId, saved.getId(), apiUsageStates, 0L);
List<TsKvEntry> profileThresholds = new ArrayList<>(); if (entityId.getEntityType() == EntityType.TENANT && !entityId.equals(TenantId.SYS_TENANT_ID)) {
tenantId = (TenantId) entityId;
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { Tenant tenant = tenantDao.findById(tenantId, tenantId.getId());
profileThresholds.add(new BasicTsKvEntry(saved.getCreatedTime(), new LongDataEntry(key.getApiLimitKey(), configuration.getProfileThreshold(key)))); TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenant.getTenantProfileId().getId());
TenantProfileConfiguration configuration = tenantProfile.getProfileData().getConfiguration();
List<TsKvEntry> profileThresholds = new ArrayList<>();
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
profileThresholds.add(new BasicTsKvEntry(saved.getCreatedTime(), new LongDataEntry(key.getApiLimitKey(), configuration.getProfileThreshold(key))));
}
tsService.save(tenantId, saved.getId(), profileThresholds, 0L);
} }
tsService.save(tenantId, saved.getId(), profileThresholds, 0L);
return saved; return saved;
} }
@ -126,6 +140,12 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
return apiUsageStateDao.findTenantApiUsageState(tenantId.getId()); return apiUsageStateDao.findTenantApiUsageState(tenantId.getId());
} }
@Override
public ApiUsageState findApiUsageStateByEntityId(EntityId entityId) {
validateId(entityId.getId(), "Invalid entity id");
return apiUsageStateDao.findApiUsageStateByEntityId(entityId);
}
@Override @Override
public ApiUsageState findApiUsageStateById(TenantId tenantId, ApiUsageStateId id) { public ApiUsageState findApiUsageStateById(TenantId tenantId, ApiUsageStateId id) {
log.trace("Executing findApiUsageStateById, tenantId [{}], apiUsageStateId [{}]", tenantId, id); log.trace("Executing findApiUsageStateById, tenantId [{}], apiUsageStateId [{}]", tenantId, id);
@ -142,16 +162,14 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
throw new DataValidationException("ApiUsageState should be assigned to tenant!"); throw new DataValidationException("ApiUsageState should be assigned to tenant!");
} else { } else {
Tenant tenant = tenantDao.findById(requestTenantId, apiUsageState.getTenantId().getId()); Tenant tenant = tenantDao.findById(requestTenantId, apiUsageState.getTenantId().getId());
if (tenant == null) { if (tenant == null && !requestTenantId.equals(TenantId.SYS_TENANT_ID)) {
throw new DataValidationException("Asset is referencing to non-existent tenant!"); throw new DataValidationException("ApiUsageState is referencing to non-existent tenant!");
} }
} }
if (apiUsageState.getEntityId() == null) { if (apiUsageState.getEntityId() == null) {
throw new DataValidationException("UsageRecord should be assigned to entity!"); throw new DataValidationException("UsageRecord should be assigned to entity!");
} else if (!EntityType.TENANT.equals(apiUsageState.getEntityId().getEntityType())) { } else if (apiUsageState.getEntityId().getEntityType() != EntityType.TENANT && apiUsageState.getEntityId().getEntityType() != EntityType.CUSTOMER) {
throw new DataValidationException("Only Tenant Usage Records are supported!"); throw new DataValidationException("Only Tenant and Customer Usage Records are supported!");
} else if (!apiUsageState.getTenantId().getId().equals(apiUsageState.getEntityId().getId())) {
throw new DataValidationException("Can't assign one Usage Record to multiple tenants!");
} }
} }
}; };

3
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java

@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.ApiFeature;
import org.thingsboard.server.common.data.ApiUsageStateMailMessage; import org.thingsboard.server.common.data.ApiUsageStateMailMessage;
import org.thingsboard.server.common.data.ApiUsageStateValue; import org.thingsboard.server.common.data.ApiUsageStateValue;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import javax.mail.MessagingException; import javax.mail.MessagingException;
@ -40,7 +41,7 @@ public interface MailService {
void sendPasswordWasResetEmail(String loginLink, String email) throws ThingsboardException; void sendPasswordWasResetEmail(String loginLink, String email) throws ThingsboardException;
void send(TenantId tenantId, String from, String to, String cc, String bcc, String subject, String body) throws MessagingException; void send(TenantId tenantId, CustomerId customerId, String from, String to, String cc, String bcc, String subject, String body) throws MessagingException;
void sendAccountLockoutEmail( String lockoutEmail, String email, Integer maxFailedLoginAttempts) throws ThingsboardException; void sendAccountLockoutEmail( String lockoutEmail, String email, Integer maxFailedLoginAttempts) throws ThingsboardException;

3
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java

@ -16,6 +16,7 @@
package org.thingsboard.rule.engine.api; package org.thingsboard.rule.engine.api;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
@ -31,7 +32,7 @@ public interface RuleEngineTelemetryService {
void saveAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback); void saveAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback);
void saveAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback); void saveAndNotify(TenantId tenantId, CustomerId id, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback);
void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, FutureCallback<Void> callback); void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, FutureCallback<Void> callback);

3
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/SmsService.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.rule.engine.api; package org.thingsboard.rule.engine.api;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.sms.config.TestSmsRequest; import org.thingsboard.server.common.data.sms.config.TestSmsRequest;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -23,7 +24,7 @@ public interface SmsService {
void updateSmsConfiguration(); void updateSmsConfiguration();
void sendSms(TenantId tenantId, String[] numbersTo, String message) throws ThingsboardException;; void sendSms(TenantId tenantId, CustomerId customerId, String[] numbersTo, String message) throws ThingsboardException;;
void sendTestSms(TestSmsRequest testSmsRequest) throws ThingsboardException; void sendTestSms(TestSmsRequest testSmsRequest) throws ThingsboardException;

5
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java

@ -16,16 +16,15 @@
package org.thingsboard.rule.engine.api; package org.thingsboard.rule.engine.api;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import org.springframework.data.redis.core.RedisTemplate;
import org.thingsboard.common.util.ListeningExecutor; import org.thingsboard.common.util.ListeningExecutor;
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
@ -144,6 +143,8 @@ public interface TbContext {
TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data); TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data);
TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data);
TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data); TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data);
TbMsg customerCreatedMsg(Customer customer, RuleNodeId ruleNodeId); TbMsg customerCreatedMsg(Customer customer, RuleNodeId ruleNodeId);

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java

@ -136,7 +136,7 @@ public class TbCopyAttributesToEntityViewNode implements TbNode {
} }
private void transformAndTellNext(TbContext ctx, TbMsg msg, EntityView entityView) { private void transformAndTellNext(TbContext ctx, TbMsg msg, EntityView entityView) {
ctx.enqueueForTellNext(ctx.newMsg(msg.getQueueName(), msg.getType(), entityView.getId(), msg.getMetaData(), msg.getData()), SUCCESS); ctx.enqueueForTellNext(ctx.newMsg(msg.getQueueName(), msg.getType(), entityView.getId(), msg.getCustomerId(), msg.getMetaData(), msg.getData()), SUCCESS);
} }
private boolean attributeContainsInEntityView(String scope, String attrKey, EntityView entityView) { private boolean attributeContainsInEntityView(String scope, String attrKey, EntityView entityView) {

10
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java

@ -63,7 +63,7 @@ public class TbMsgCountNode implements TbNode {
TbMsgCountNodeConfiguration config = TbNodeUtils.convert(configuration, TbMsgCountNodeConfiguration.class); TbMsgCountNodeConfiguration config = TbNodeUtils.convert(configuration, TbMsgCountNodeConfiguration.class);
this.delay = TimeUnit.SECONDS.toMillis(config.getInterval()); this.delay = TimeUnit.SECONDS.toMillis(config.getInterval());
this.telemetryPrefix = config.getTelemetryPrefix(); this.telemetryPrefix = config.getTelemetryPrefix();
scheduleTickMsg(ctx); scheduleTickMsg(ctx, null);
} }
@ -78,23 +78,23 @@ public class TbMsgCountNode implements TbNode {
TbMsgMetaData metaData = new TbMsgMetaData(); TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("delta", Long.toString(System.currentTimeMillis() - lastScheduledTs + delay)); metaData.putValue("delta", Long.toString(System.currentTimeMillis() - lastScheduledTs + delay));
TbMsg tbMsg = TbMsg.newMsg(msg.getQueueName(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), ctx.getTenantId(), metaData, gson.toJson(telemetryJson)); TbMsg tbMsg = TbMsg.newMsg(msg.getQueueName(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), ctx.getTenantId(), msg.getCustomerId(), metaData, gson.toJson(telemetryJson));
ctx.enqueueForTellNext(tbMsg, SUCCESS); ctx.enqueueForTellNext(tbMsg, SUCCESS);
scheduleTickMsg(ctx); scheduleTickMsg(ctx, tbMsg);
} else { } else {
messagesProcessed.incrementAndGet(); messagesProcessed.incrementAndGet();
ctx.ack(msg); ctx.ack(msg);
} }
} }
private void scheduleTickMsg(TbContext ctx) { private void scheduleTickMsg(TbContext ctx, TbMsg msg) {
long curTs = System.currentTimeMillis(); long curTs = System.currentTimeMillis();
if (lastScheduledTs == 0L) { if (lastScheduledTs == 0L) {
lastScheduledTs = curTs; lastScheduledTs = curTs;
} }
lastScheduledTs = lastScheduledTs + delay; lastScheduledTs = lastScheduledTs + delay;
long curDelay = Math.max(0L, (lastScheduledTs - curTs)); long curDelay = Math.max(0L, (lastScheduledTs - curTs));
TbMsg tickMsg = ctx.newMsg(ServiceQueue.MAIN, TB_MSG_COUNT_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), ""); TbMsg tickMsg = ctx.newMsg(ServiceQueue.MAIN, TB_MSG_COUNT_NODE_MSG, ctx.getSelfId(), msg != null ? msg.getCustomerId() : null, new TbMsgMetaData(), "");
nextTickId = tickMsg.getId(); nextTickId = tickMsg.getId();
ctx.tellSelf(tickMsg, curDelay); ctx.tellSelf(tickMsg, curDelay);
} }

8
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java

@ -100,7 +100,7 @@ public class TbMsgGeneratorNode implements TbNode {
@Override @Override
public void onMsg(TbContext ctx, TbMsg msg) { public void onMsg(TbContext ctx, TbMsg msg) {
if (initialized && msg.getType().equals(TB_MSG_GENERATOR_NODE_MSG) && msg.getId().equals(nextTickId)) { if (initialized && msg.getType().equals(TB_MSG_GENERATOR_NODE_MSG) && msg.getId().equals(nextTickId)) {
withCallback(generate(ctx), withCallback(generate(ctx, msg),
m -> { m -> {
if (initialized && (config.getMsgCount() == TbMsgGeneratorNodeConfiguration.UNLIMITED_MSG_COUNT || currentMsgCount < config.getMsgCount())) { if (initialized && (config.getMsgCount() == TbMsgGeneratorNodeConfiguration.UNLIMITED_MSG_COUNT || currentMsgCount < config.getMsgCount())) {
ctx.enqueueForTellNext(m, SUCCESS); ctx.enqueueForTellNext(m, SUCCESS);
@ -130,16 +130,16 @@ public class TbMsgGeneratorNode implements TbNode {
ctx.tellSelf(tickMsg, curDelay); ctx.tellSelf(tickMsg, curDelay);
} }
private ListenableFuture<TbMsg> generate(TbContext ctx) { private ListenableFuture<TbMsg> generate(TbContext ctx, TbMsg msg) {
return ctx.getJsExecutor().executeAsync(() -> { return ctx.getJsExecutor().executeAsync(() -> {
if (prevMsg == null) { if (prevMsg == null) {
prevMsg = ctx.newMsg(ServiceQueue.MAIN, "", originatorId, new TbMsgMetaData(), "{}"); prevMsg = ctx.newMsg(ServiceQueue.MAIN, "", originatorId, msg.getCustomerId(), new TbMsgMetaData(), "{}");
} }
if (initialized) { if (initialized) {
ctx.logJsEvalRequest(); ctx.logJsEvalRequest();
TbMsg generated = jsEngine.executeGenerate(prevMsg); TbMsg generated = jsEngine.executeGenerate(prevMsg);
ctx.logJsEvalResponse(); ctx.logJsEvalResponse();
prevMsg = ctx.newMsg(ServiceQueue.MAIN, generated.getType(), originatorId, generated.getMetaData(), generated.getData()); prevMsg = ctx.newMsg(ServiceQueue.MAIN, generated.getType(), originatorId, msg.getCustomerId(), generated.getMetaData(), generated.getData());
} }
return prevMsg; return prevMsg;
}); });

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java

@ -70,7 +70,7 @@ public class TbMsgDelayNode implements TbNode {
} else { } else {
if (pendingMsgs.size() < config.getMaxPendingMsgs()) { if (pendingMsgs.size() < config.getMaxPendingMsgs()) {
pendingMsgs.put(msg.getId(), msg); pendingMsgs.put(msg.getId(), msg);
TbMsg tickMsg = ctx.newMsg(ServiceQueue.MAIN, TB_MSG_DELAY_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), msg.getId().toString()); TbMsg tickMsg = ctx.newMsg(ServiceQueue.MAIN, TB_MSG_DELAY_NODE_MSG, ctx.getSelfId(), msg.getCustomerId(), new TbMsgMetaData(), msg.getId().toString());
ctx.tellSelf(tickMsg, getDelay(msg)); ctx.tellSelf(tickMsg, getDelay(msg));
ctx.ack(msg); ctx.ack(msg);
} else { } else {

7
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java

@ -26,7 +26,6 @@ import org.thingsboard.rule.engine.api.TbNode;
import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
@ -76,7 +75,7 @@ public class TbSendEmailNode implements TbNode {
validateType(msg.getType()); validateType(msg.getType());
EmailPojo email = getEmail(msg); EmailPojo email = getEmail(msg);
withCallback(ctx.getMailExecutor().executeAsync(() -> { withCallback(ctx.getMailExecutor().executeAsync(() -> {
sendEmail(ctx, email); sendEmail(ctx, msg, email);
return null; return null;
}), }),
ok -> ctx.tellSuccess(msg), ok -> ctx.tellSuccess(msg),
@ -86,9 +85,9 @@ public class TbSendEmailNode implements TbNode {
} }
} }
private void sendEmail(TbContext ctx, EmailPojo email) throws Exception { private void sendEmail(TbContext ctx, TbMsg msg, EmailPojo email) throws Exception {
if (this.config.isUseSystemSmtpSettings()) { if (this.config.isUseSystemSmtpSettings()) {
ctx.getMailService().send(ctx.getTenantId(), email.getFrom(), email.getTo(), email.getCc(), ctx.getMailService().send(ctx.getTenantId(), msg.getCustomerId(), email.getFrom(), email.getTo(), email.getCc(),
email.getBcc(), email.getSubject(), email.getBody()); email.getBcc(), email.getSubject(), email.getBody());
} else { } else {
MimeMessage mailMsg = mailSender.createMimeMessage(); MimeMessage mailMsg = mailSender.createMimeMessage();

15
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java

@ -74,15 +74,15 @@ class AlarmState {
lastMsgMetaData = msg.getMetaData(); lastMsgMetaData = msg.getMetaData();
lastMsgQueueName = msg.getQueueName(); lastMsgQueueName = msg.getQueueName();
this.dataSnapshot = data; this.dataSnapshot = data;
return createOrClearAlarms(ctx, data, update, AlarmRuleState::eval); return createOrClearAlarms(ctx, msg, data, update, AlarmRuleState::eval);
} }
public boolean process(TbContext ctx, long ts) throws ExecutionException, InterruptedException { public boolean process(TbContext ctx, long ts) throws ExecutionException, InterruptedException {
initCurrentAlarm(ctx); initCurrentAlarm(ctx);
return createOrClearAlarms(ctx, ts, null, AlarmRuleState::eval); return createOrClearAlarms(ctx, null, ts, null, AlarmRuleState::eval);
} }
public <T> boolean createOrClearAlarms(TbContext ctx, T data, SnapshotUpdate update, BiFunction<AlarmRuleState, T, AlarmEvalResult> evalFunction) { public <T> boolean createOrClearAlarms(TbContext ctx, TbMsg msg, T data, SnapshotUpdate update, BiFunction<AlarmRuleState, T, AlarmEvalResult> evalFunction) {
boolean stateUpdate = false; boolean stateUpdate = false;
AlarmRuleState resultState = null; AlarmRuleState resultState = null;
log.debug("[{}] processing update: {}", alarmDefinition.getId(), data); log.debug("[{}] processing update: {}", alarmDefinition.getId(), data);
@ -103,7 +103,7 @@ class AlarmState {
if (resultState != null) { if (resultState != null) {
TbAlarmResult result = calculateAlarmResult(ctx, resultState); TbAlarmResult result = calculateAlarmResult(ctx, resultState);
if (result != null) { if (result != null) {
pushMsg(ctx, result, resultState); pushMsg(ctx, msg, result, resultState);
} }
stateUpdate = clearAlarmState(stateUpdate, clearState); stateUpdate = clearAlarmState(stateUpdate, clearState);
} else if (currentAlarm != null && clearState != null) { } else if (currentAlarm != null && clearState != null) {
@ -122,7 +122,7 @@ class AlarmState {
); );
DonAsynchron.withCallback(alarmClearOperationResult, DonAsynchron.withCallback(alarmClearOperationResult,
result -> { result -> {
pushMsg(ctx, new TbAlarmResult(false, false, true, result.getAlarm()), clearState); pushMsg(ctx, msg, new TbAlarmResult(false, false, true, result.getAlarm()), clearState);
}, },
throwable -> { throwable -> {
throw new RuntimeException(throwable); throw new RuntimeException(throwable);
@ -165,7 +165,7 @@ class AlarmState {
} }
} }
public void pushMsg(TbContext ctx, TbAlarmResult alarmResult, AlarmRuleState ruleState) { public void pushMsg(TbContext ctx, TbMsg msg, TbAlarmResult alarmResult, AlarmRuleState ruleState) {
JsonNode jsonNodes = JacksonUtil.valueToTree(alarmResult.getAlarm()); JsonNode jsonNodes = JacksonUtil.valueToTree(alarmResult.getAlarm());
String data = jsonNodes.toString(); String data = jsonNodes.toString();
TbMsgMetaData metaData = lastMsgMetaData != null ? lastMsgMetaData.copy() : new TbMsgMetaData(); TbMsgMetaData metaData = lastMsgMetaData != null ? lastMsgMetaData.copy() : new TbMsgMetaData();
@ -185,7 +185,8 @@ class AlarmState {
metaData.putValue(DataConstants.IS_CLEARED_ALARM, Boolean.TRUE.toString()); metaData.putValue(DataConstants.IS_CLEARED_ALARM, Boolean.TRUE.toString());
} }
setAlarmConditionMetadata(ruleState, metaData); setAlarmConditionMetadata(ruleState, metaData);
TbMsg newMsg = ctx.newMsg(lastMsgQueueName != null ? lastMsgQueueName : ServiceQueue.MAIN, "ALARM", originator, metaData, data); TbMsg newMsg = ctx.newMsg(lastMsgQueueName != null ? lastMsgQueueName : ServiceQueue.MAIN, "ALARM",
originator, msg != null ? msg.getCustomerId() : null, metaData, data);
ctx.tellNext(newMsg, relationType); ctx.tellNext(newMsg, relationType);
} }

8
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java

@ -74,7 +74,7 @@ public class TbDeviceProfileNode implements TbNode {
this.config = TbNodeUtils.convert(configuration, TbDeviceProfileNodeConfiguration.class); this.config = TbNodeUtils.convert(configuration, TbDeviceProfileNodeConfiguration.class);
this.cache = ctx.getDeviceProfileCache(); this.cache = ctx.getDeviceProfileCache();
this.ctx = ctx; this.ctx = ctx;
scheduleAlarmHarvesting(ctx); scheduleAlarmHarvesting(ctx, null);
ctx.addDeviceProfileListeners(this::onProfileUpdate, this::onDeviceUpdate); ctx.addDeviceProfileListeners(this::onProfileUpdate, this::onDeviceUpdate);
if (config.isFetchAlarmRulesStateOnStart()) { if (config.isFetchAlarmRulesStateOnStart()) {
log.info("[{}] Fetching alarm rule state", ctx.getSelfId()); log.info("[{}] Fetching alarm rule state", ctx.getSelfId());
@ -108,7 +108,7 @@ public class TbDeviceProfileNode implements TbNode {
public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException {
EntityType originatorType = msg.getOriginator().getEntityType(); EntityType originatorType = msg.getOriginator().getEntityType();
if (msg.getType().equals(PERIODIC_MSG_TYPE)) { if (msg.getType().equals(PERIODIC_MSG_TYPE)) {
scheduleAlarmHarvesting(ctx); scheduleAlarmHarvesting(ctx, msg);
harvestAlarms(ctx, System.currentTimeMillis()); harvestAlarms(ctx, System.currentTimeMillis());
} else if (msg.getType().equals(PROFILE_UPDATE_MSG_TYPE)) { } else if (msg.getType().equals(PROFILE_UPDATE_MSG_TYPE)) {
updateProfile(ctx, new DeviceProfileId(UUID.fromString(msg.getData()))); updateProfile(ctx, new DeviceProfileId(UUID.fromString(msg.getData())));
@ -168,8 +168,8 @@ public class TbDeviceProfileNode implements TbNode {
return deviceState; return deviceState;
} }
protected void scheduleAlarmHarvesting(TbContext ctx) { protected void scheduleAlarmHarvesting(TbContext ctx, TbMsg msg) {
TbMsg periodicCheck = TbMsg.newMsg(PERIODIC_MSG_TYPE, ctx.getTenantId(), TbMsgMetaData.EMPTY, "{}"); TbMsg periodicCheck = TbMsg.newMsg(PERIODIC_MSG_TYPE, ctx.getTenantId(), msg != null ? msg.getCustomerId() : null, TbMsgMetaData.EMPTY, "{}");
ctx.tellSelf(periodicCheck, TimeUnit.MINUTES.toMillis(1)); ctx.tellSelf(periodicCheck, TimeUnit.MINUTES.toMillis(1));
} }

8
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java

@ -16,10 +16,6 @@
package org.thingsboard.rule.engine.rpc; package org.thingsboard.rule.engine.rpc;
import com.datastax.oss.driver.api.core.uuid.Uuids; import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@ -116,10 +112,10 @@ public class TbSendRPCRequestNode implements TbNode {
ctx.getRpcService().sendRpcRequestToDevice(request, ruleEngineDeviceRpcResponse -> { ctx.getRpcService().sendRpcRequestToDevice(request, ruleEngineDeviceRpcResponse -> {
if (!ruleEngineDeviceRpcResponse.getError().isPresent()) { if (!ruleEngineDeviceRpcResponse.getError().isPresent()) {
TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().orElse("{}")); TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getCustomerId(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().orElse("{}"));
ctx.enqueueForTellNext(next, TbRelationTypes.SUCCESS); ctx.enqueueForTellNext(next, TbRelationTypes.SUCCESS);
} else { } else {
TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name())); TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getCustomerId(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name()));
ctx.tellFailure(next, new RuntimeException(ruleEngineDeviceRpcResponse.getError().get().name())); ctx.tellFailure(next, new RuntimeException(ruleEngineDeviceRpcResponse.getError().get().name()));
} }
}); });

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/sms/TbSendSmsNode.java

@ -76,7 +76,7 @@ public class TbSendSmsNode implements TbNode {
String message = TbNodeUtils.processPattern(this.config.getSmsMessageTemplate(), msg); String message = TbNodeUtils.processPattern(this.config.getSmsMessageTemplate(), msg);
String[] numbersToList = numbersTo.split(","); String[] numbersToList = numbersTo.split(",");
if (this.config.isUseSystemSmsSettings()) { if (this.config.isUseSystemSmsSettings()) {
ctx.getSmsService().sendSms(ctx.getTenantId(), numbersToList, message); ctx.getSmsService().sendSms(ctx.getTenantId(), msg.getCustomerId(), numbersToList, message);
} else { } else {
for (String numberTo : numbersToList) { for (String numberTo : numbersToList) {
this.smsSender.sendSms(numberTo, message); this.smsSender.sendSms(numberTo, message);

3
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java

@ -25,6 +25,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
@ -93,7 +94,7 @@ public class TbMsgTimeseriesNode implements TbNode {
if (ttl == 0L) { if (ttl == 0L) {
ttl = tenantProfileDefaultStorageTtl; ttl = tenantProfileDefaultStorageTtl;
} }
ctx.getTelemetryService().saveAndNotify(ctx.getTenantId(), msg.getOriginator(), tsKvEntryList, ttl, new TelemetryNodeCallback(ctx, msg)); ctx.getTelemetryService().saveAndNotify(ctx.getTenantId(), msg.getCustomerId(), msg.getOriginator(), tsKvEntryList, ttl, new TelemetryNodeCallback(ctx, msg));
} }
public static long getTs(TbMsg msg) { public static long getTs(TbMsg msg) {

18
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java

@ -183,7 +183,7 @@ public class TbDeviceProfileNodeTest {
Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg());
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyString())).thenReturn(theMsg); Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())).thenReturn(theMsg);
ObjectNode data = mapper.createObjectNode(); ObjectNode data = mapper.createObjectNode();
data.put("temperature", 42); data.put("temperature", 42);
@ -195,7 +195,7 @@ public class TbDeviceProfileNodeTest {
verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any());
TbMsg theMsg2 = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "2"); TbMsg theMsg2 = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "2");
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyString())).thenReturn(theMsg2); Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())).thenReturn(theMsg2);
TbMsg msg2 = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), TbMsg msg2 = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(),
@ -274,7 +274,7 @@ public class TbDeviceProfileNodeTest {
.thenReturn(attrListListenableFuture); .thenReturn(attrListListenableFuture);
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyString())) Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString()))
.thenReturn(theMsg); .thenReturn(theMsg);
ObjectNode data = mapper.createObjectNode(); ObjectNode data = mapper.createObjectNode();
@ -361,7 +361,7 @@ public class TbDeviceProfileNodeTest {
.thenReturn(attrListListenableFuture); .thenReturn(attrListListenableFuture);
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyString())) Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString()))
.thenReturn(theMsg); .thenReturn(theMsg);
ObjectNode data = mapper.createObjectNode(); ObjectNode data = mapper.createObjectNode();
@ -430,7 +430,7 @@ public class TbDeviceProfileNodeTest {
.thenReturn(listListenableFutureWithLess); .thenReturn(listListenableFutureWithLess);
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyString())) Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString()))
.thenReturn(theMsg); .thenReturn(theMsg);
ObjectNode data = mapper.createObjectNode(); ObjectNode data = mapper.createObjectNode();
@ -510,7 +510,7 @@ public class TbDeviceProfileNodeTest {
.thenReturn(optionalListenableFutureWithLess); .thenReturn(optionalListenableFutureWithLess);
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyString())) Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString()))
.thenReturn(theMsg); .thenReturn(theMsg);
ObjectNode data = mapper.createObjectNode(); ObjectNode data = mapper.createObjectNode();
@ -584,7 +584,7 @@ public class TbDeviceProfileNodeTest {
.thenReturn(optionalListenableFutureWithLess); .thenReturn(optionalListenableFutureWithLess);
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyString())) Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString()))
.thenReturn(theMsg); .thenReturn(theMsg);
ObjectNode data = mapper.createObjectNode(); ObjectNode data = mapper.createObjectNode();
@ -668,7 +668,7 @@ public class TbDeviceProfileNodeTest {
.thenReturn(optionalListenableFutureWithLess); .thenReturn(optionalListenableFutureWithLess);
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyString())) Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString()))
.thenReturn(theMsg); .thenReturn(theMsg);
ObjectNode data = mapper.createObjectNode(); ObjectNode data = mapper.createObjectNode();
@ -754,7 +754,7 @@ public class TbDeviceProfileNodeTest {
.thenReturn(optionalListenableFutureWithLess); .thenReturn(optionalListenableFutureWithLess);
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyString())) Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString()))
.thenReturn(theMsg); .thenReturn(theMsg);
ObjectNode data = mapper.createObjectNode(); ObjectNode data = mapper.createObjectNode();

Loading…
Cancel
Save