Browse Source

added debug event entity

pull/12498/head
IrynaMatveieva 1 year ago
parent
commit
c047d5f4f0
  1. 41
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  2. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  3. 13
      application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldExecutionService.java
  4. 23
      common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedField.java
  5. 95
      common/data/src/main/java/org/thingsboard/server/common/data/event/CalculatedFieldDebugEvent.java
  6. 50
      common/data/src/main/java/org/thingsboard/server/common/data/event/CalculatedFieldDebugEventFilter.java
  7. 3
      common/data/src/main/java/org/thingsboard/server/common/data/event/EventType.java
  8. 1
      dao/src/main/java/org/thingsboard/server/dao/cf/BaseCalculatedFieldService.java
  9. 6
      dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java
  10. 5
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  11. 104
      dao/src/main/java/org/thingsboard/server/dao/model/sql/CalculatedFieldDebugEventEntity.java
  12. 7
      dao/src/main/java/org/thingsboard/server/dao/model/sql/CalculatedFieldEntity.java
  13. 135
      dao/src/main/java/org/thingsboard/server/dao/sql/event/CalculatedFieldDebugEventRepository.java
  14. 5
      dao/src/main/java/org/thingsboard/server/dao/sql/event/DedicatedJpaEventDao.java
  15. 29
      dao/src/main/java/org/thingsboard/server/dao/sql/event/EventInsertRepository.java
  16. 47
      dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java
  17. 17
      dao/src/main/resources/sql/schema-entities.sql

41
application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java

@ -42,10 +42,12 @@ import org.thingsboard.script.api.tbel.TbelInvokeService;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.event.CalculatedFieldDebugEvent;
import org.thingsboard.server.common.data.event.ErrorEvent;
import org.thingsboard.server.common.data.event.LifecycleEvent;
import org.thingsboard.server.common.data.event.RuleChainDebugEvent;
import org.thingsboard.server.common.data.event.RuleNodeDebugEvent;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
@ -125,6 +127,7 @@ import org.thingsboard.server.service.transport.TbCoreToTransportService;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledExecutorService;
@ -157,6 +160,18 @@ public class ActorSystemContext {
}
};
private static final FutureCallback<Void> CALCULATED_FIELD_DEBUG_EVENT_ERROR_CALLBACK = new FutureCallback<>() {
@Override
public void onSuccess(@Nullable Void event) {
}
@Override
public void onFailure(Throwable th) {
log.error("Could not save debug Event for Calculated Field", th);
}
};
private final ConcurrentMap<TenantId, DebugTbRateLimits> debugPerTenantLimits = new ConcurrentHashMap<>();
public ConcurrentMap<TenantId, DebugTbRateLimits> getDebugPerTenantLimits() {
@ -723,6 +738,32 @@ public class ActorSystemContext {
}
}
public void persistCalculatedFieldDebugEvent(TenantId tenantId, CalculatedFieldId calculatedFieldId, EntityId entityId, Map<String, String> arguments, TbMsg tbMsg, Throwable error) {
if (checkLimits(tenantId, tbMsg, error)) {
try {
CalculatedFieldDebugEvent.CalculatedFieldDebugEventBuilder event = CalculatedFieldDebugEvent.builder()
.tenantId(tenantId)
.entityId(entityId.getId())
.serviceId(getServiceId())
.calculatedFieldId(calculatedFieldId)
.eventEntity(tbMsg.getOriginator())
.msgId(tbMsg.getId())
.msgType(tbMsg.getType())
.arguments(JacksonUtil.toString(arguments))
.result(tbMsg.getData());
if (error != null) {
event.error(toString(error));
}
ListenableFuture<Void> future = eventService.saveAsync(event.build());
Futures.addCallback(future, CALCULATED_FIELD_DEBUG_EVENT_ERROR_CALLBACK, MoreExecutors.directExecutor());
} catch (IllegalArgumentException ex) {
log.warn("Failed to persist calculated field debug message", ex);
}
}
}
private boolean checkLimits(TenantId tenantId, TbMsg tbMsg, Throwable error) {
if (debugPerTenantEnabled) {
DebugTbRateLimits debugTbRateLimits = debugPerTenantLimits.computeIfAbsent(tenantId, id ->

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

@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import io.netty.channel.EventLoopGroup;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.Arrays;
import org.thingsboard.common.util.DebugModeUtil;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ListeningExecutor;
import org.thingsboard.rule.engine.api.MailService;
@ -64,7 +65,6 @@ import org.thingsboard.server.common.data.msg.TbNodeConnectionType;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.common.util.DebugModeUtil;
import org.thingsboard.server.common.data.rule.RuleNodeState;
import org.thingsboard.server.common.data.script.ScriptLanguage;
import org.thingsboard.server.common.msg.TbActorMsg;

13
application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldExecutionService.java

@ -42,7 +42,6 @@ import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.configuration.Argument;
import org.thingsboard.server.common.data.cf.configuration.ArgumentType;
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.OutputType;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
@ -71,6 +70,7 @@ import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.common.util.ProtoUtils;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.cf.CalculatedFieldService;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtx;
@ -122,6 +122,7 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
private final CalculatedFieldStateService stateService;
private final TbClusterService clusterService;
private final TbelInvokeService tbelInvokeService;
private final EventService eventService;
private ListeningExecutorService calculatedFieldExecutor;
private ListeningExecutorService calculatedFieldCallbackExecutor;
@ -253,7 +254,6 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
CalculatedField cf = calculatedFieldService.findById(tenantId, calculatedFieldId);
if (proto.getUpdated()) {
log.info("Executing onCalculatedFieldUpdate, calculatedFieldId=[{}]", calculatedFieldId);
calculatedFieldCache.updateCalculatedField(tenantId, calculatedFieldId);
boolean shouldReinit = onCalculatedFieldUpdate(cf, callback);
if (!shouldReinit) {
return;
@ -301,6 +301,7 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
if (hasSignificantChanges(oldCalculatedField, updatedCalculatedField)) {
onCalculatedFieldDelete(updatedCalculatedField.getId(), callback);
} else {
calculatedFieldCache.updateCalculatedField(updatedCalculatedField.getTenantId(), updatedCalculatedField.getId());
callback.onSuccess();
shouldReinit = false;
}
@ -329,13 +330,9 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
}
boolean entityIdChanged = !oldCalculatedField.getEntityId().equals(newCalculatedField.getEntityId());
boolean typeChanged = !oldCalculatedField.getType().equals(newCalculatedField.getType());
CalculatedFieldConfiguration oldConfig = oldCalculatedField.getConfiguration();
CalculatedFieldConfiguration newConfig = newCalculatedField.getConfiguration();
boolean argumentsChanged = !oldConfig.getArguments().equals(newConfig.getArguments());
boolean outputTypeChanged = !oldConfig.getOutput().getType().equals(newConfig.getOutput().getType());
boolean expressionChanged = !oldConfig.getExpression().equals(newConfig.getExpression());
boolean argumentsChanged = !oldCalculatedField.getConfiguration().getArguments().equals(newCalculatedField.getConfiguration().getArguments());
return entityIdChanged || typeChanged || argumentsChanged || outputTypeChanged || expressionChanged;
return entityIdChanged || typeChanged || argumentsChanged;
}
@Override

23
common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedField.java

@ -15,6 +15,8 @@
*/
package org.thingsboard.server.common.data.cf;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSetter;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -22,11 +24,13 @@ import lombok.Getter;
import lombok.Setter;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.ExportableEntity;
import org.thingsboard.server.common.data.HasDebugSettings;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.debug.DebugSettings;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
@ -36,7 +40,7 @@ import org.thingsboard.server.common.data.validation.NoXss;
@Schema
@Data
@EqualsAndHashCode(callSuper = true)
public class CalculatedField extends BaseData<CalculatedFieldId> implements HasName, HasTenantId, HasVersion, ExportableEntity<CalculatedFieldId> {
public class CalculatedField extends BaseData<CalculatedFieldId> implements HasName, HasTenantId, HasVersion, ExportableEntity<CalculatedFieldId>, HasDebugSettings {
private static final long serialVersionUID = 4491966747773381420L;
@ -50,6 +54,11 @@ public class CalculatedField extends BaseData<CalculatedFieldId> implements HasN
@Length(fieldName = "name")
@Schema(description = "User defined name of the calculated field.")
private String name;
@Deprecated
@Schema(description = "Enable/disable debug. ", example = "false", deprecated = true)
private boolean debugMode;
@Schema(description = "Debug settings object.")
private DebugSettings debugSettings;
@Schema(description = "Version of calculated field configuration.", example = "0")
private int configurationVersion;
@Schema(implementation = SimpleCalculatedFieldConfiguration.class)
@ -109,4 +118,16 @@ public class CalculatedField extends BaseData<CalculatedFieldId> implements HasN
.toString();
}
// Getter is ignored for serialization
@JsonIgnore
public boolean isDebugMode() {
return debugMode;
}
// Setter is annotated for deserialization
@JsonSetter
public void setDebugMode(boolean debugMode) {
this.debugMode = debugMode;
}
}

95
common/data/src/main/java/org/thingsboard/server/common/data/event/CalculatedFieldDebugEvent.java

@ -0,0 +1,95 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.event;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EventInfo;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.UUID;
@ToString
@EqualsAndHashCode(callSuper = true)
public class CalculatedFieldDebugEvent extends Event {
private static final long serialVersionUID = -7091690784759639853L;
@Builder
private CalculatedFieldDebugEvent(TenantId tenantId, UUID entityId, String serviceId, UUID id, long ts,
CalculatedFieldId calculatedFieldId, EntityId eventEntity, UUID msgId,
String msgType, String arguments, String result, String error) {
super(tenantId, entityId, serviceId, id, ts);
this.calculatedFieldId = calculatedFieldId;
this.eventEntity = eventEntity;
this.msgId = msgId;
this.msgType = msgType;
this.arguments = arguments;
this.result = result;
this.error = error;
}
@Getter
private final CalculatedFieldId calculatedFieldId;
@Getter
private final EntityId eventEntity;
@Getter
private final UUID msgId;
@Getter
private final String msgType;
@Getter
@Setter
private String arguments;
@Getter
@Setter
private String result;
@Getter
@Setter
private String error;
@Override
public EventType getType() {
return EventType.DEBUG_CALCULATED_FIELD;
}
@Override
public EventInfo toInfo(EntityType entityType) {
EventInfo eventInfo = super.toInfo(entityType);
var json = (ObjectNode) eventInfo.getBody();
json.put("calculatedFieldId", calculatedFieldId.toString());
if (eventEntity != null) {
json.put("entityId", eventEntity.getId().toString())
.put("entityType", eventEntity.getEntityType().name());
}
if (msgId != null) {
json.put("msgId", msgId.toString());
}
putNotNull(json, "msgType", msgType);
putNotNull(json, "arguments", arguments);
putNotNull(json, "result", result);
putNotNull(json, "error", error);
return eventInfo;
}
}

50
common/data/src/main/java/org/thingsboard/server/common/data/event/CalculatedFieldDebugEventFilter.java

@ -0,0 +1,50 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.event;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.StringUtils;
@Data
@EqualsAndHashCode(callSuper = true)
@Schema
public class CalculatedFieldDebugEventFilter extends DebugEventFilter {
@Schema(description = "String value representing the calculated field id in the event body", example = "ccbfa2fe-c8f5-45d8-bb37-6b61a6e02833")
protected String calculatedFieldId;
@Schema(description = "String value representing the entity id in the event body", example = "57b6bafe-d600-423c-9267-fe31e5218986")
protected String entityId;
@Schema(description = "String value representing the entity type", allowableValues = "DEVICE")
protected String entityType;
@Schema(description = "String value representing the message id in the rule engine", example = "dcf44612-2ce4-4e5d-b462-ebb9c5628228")
protected String msgId;
@Schema(description = "String value representing the message type", example = "POST_TELEMETRY_REQUEST")
protected String msgType;
@Override
public EventType getEventType() {
return EventType.DEBUG_CALCULATED_FIELD;
}
@Override
public boolean isNotEmpty() {
return super.isNotEmpty() || !StringUtils.isEmpty(calculatedFieldId) || !StringUtils.isEmpty(entityId)
|| !StringUtils.isEmpty(entityType) || !StringUtils.isEmpty(msgId) || !StringUtils.isEmpty(msgType);
}
}

3
common/data/src/main/java/org/thingsboard/server/common/data/event/EventType.java

@ -22,7 +22,8 @@ public enum EventType {
LC_EVENT("lc_event", "LC_EVENT"),
STATS("stats_event", "STATS"),
DEBUG_RULE_NODE("rule_node_debug_event", "DEBUG_RULE_NODE", true),
DEBUG_RULE_CHAIN("rule_chain_debug_event", "DEBUG_RULE_CHAIN", true);
DEBUG_RULE_CHAIN("rule_chain_debug_event", "DEBUG_RULE_CHAIN", true),
DEBUG_CALCULATED_FIELD("cf_debug_event", "DEBUG_CALCULATED_FIELD", true);
@Getter
private final String table;

1
dao/src/main/java/org/thingsboard/server/dao/cf/BaseCalculatedFieldService.java

@ -64,6 +64,7 @@ public class BaseCalculatedFieldService extends AbstractEntityService implements
log.trace("Executing save calculated field, [{}]", calculatedField);
CalculatedField savedCalculatedField = calculatedFieldDao.save(tenantId, calculatedField);
createOrUpdateCalculatedFieldLink(tenantId, savedCalculatedField);
updateDebugSettings(tenantId, calculatedField, System.currentTimeMillis());
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedCalculatedField.getTenantId()).entityId(savedCalculatedField.getId())
.entity(savedCalculatedField).oldEntity(oldCalculatedField).created(calculatedField.getId() == null).build());
return savedCalculatedField;

6
dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java

@ -23,6 +23,7 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EventInfo;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.event.CalculatedFieldDebugEvent;
import org.thingsboard.server.common.data.event.ErrorEvent;
import org.thingsboard.server.common.data.event.Event;
import org.thingsboard.server.common.data.event.EventFilter;
@ -88,6 +89,11 @@ public class BaseEventService implements EventService {
ErrorEvent eEvent = (ErrorEvent) event;
truncateField(eEvent, ErrorEvent::getError, ErrorEvent::setError);
break;
case DEBUG_CALCULATED_FIELD:
CalculatedFieldDebugEvent cfEvent = (CalculatedFieldDebugEvent) event;
truncateField(cfEvent, CalculatedFieldDebugEvent::getArguments, CalculatedFieldDebugEvent::setArguments);
truncateField(cfEvent, CalculatedFieldDebugEvent::getResult, CalculatedFieldDebugEvent::setResult);
truncateField(cfEvent, CalculatedFieldDebugEvent::getError, CalculatedFieldDebugEvent::setError);
}
}

5
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -373,6 +373,7 @@ public class ModelConstants {
public static final String STATS_EVENT_TABLE_NAME = "stats_event";
public static final String RULE_NODE_DEBUG_EVENT_TABLE_NAME = "rule_node_debug_event";
public static final String RULE_CHAIN_DEBUG_EVENT_TABLE_NAME = "rule_chain_debug_event";
public static final String CALCULATED_FIELD_DEBUG_EVENT_TABLE_NAME = "cf_debug_event";
public static final String EVENT_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String EVENT_SERVICE_ID_PROPERTY = "service_id";
@ -397,6 +398,10 @@ public class ModelConstants {
public static final String EVENT_METADATA_COLUMN_NAME = "e_metadata";
public static final String EVENT_MESSAGE_COLUMN_NAME = "e_message";
public static final String EVENT_CALCULATED_FIELD_ID_COLUMN_NAME = "cf_id";
public static final String EVENT_CALCULATED_FIELD_ARGUMENTS_COLUMN_NAME = "e_args";
public static final String EVENT_CALCULATED_FIELD_RESULT_COLUMN_NAME = "e_result";
public static final String DEBUG_MODE = "debug_mode";
public static final String DEBUG_SETTINGS = "debug_settings";
public static final String SINGLETON_MODE = "singleton_mode";

104
dao/src/main/java/org/thingsboard/server/dao/model/sql/CalculatedFieldDebugEventEntity.java

@ -0,0 +1,104 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.model.sql;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.event.CalculatedFieldDebugEvent;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseEntity;
import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.CALCULATED_FIELD_DEBUG_EVENT_TABLE_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.EVENT_CALCULATED_FIELD_ARGUMENTS_COLUMN_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.EVENT_CALCULATED_FIELD_ID_COLUMN_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.EVENT_CALCULATED_FIELD_RESULT_COLUMN_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.EVENT_ENTITY_ID_COLUMN_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.EVENT_ENTITY_TYPE_COLUMN_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.EVENT_ERROR_COLUMN_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.EVENT_MSG_ID_COLUMN_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.EVENT_MSG_TYPE_COLUMN_NAME;
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = CALCULATED_FIELD_DEBUG_EVENT_TABLE_NAME)
@NoArgsConstructor
public class CalculatedFieldDebugEventEntity extends EventEntity<CalculatedFieldDebugEvent> implements BaseEntity<CalculatedFieldDebugEvent> {
@Column(name = EVENT_CALCULATED_FIELD_ID_COLUMN_NAME)
private UUID calculatedFieldId;
@Column(name = EVENT_ENTITY_ID_COLUMN_NAME)
private UUID eventEntityId;
@Column(name = EVENT_ENTITY_TYPE_COLUMN_NAME)
private String eventEntityType;
@Column(name = EVENT_MSG_ID_COLUMN_NAME)
private UUID msgId;
@Column(name = EVENT_MSG_TYPE_COLUMN_NAME)
private String msgType;
@Column(name = EVENT_CALCULATED_FIELD_ARGUMENTS_COLUMN_NAME)
private String arguments;
@Column(name = EVENT_CALCULATED_FIELD_RESULT_COLUMN_NAME)
private String result;
@Column(name = EVENT_ERROR_COLUMN_NAME)
private String error;
public CalculatedFieldDebugEventEntity(CalculatedFieldDebugEvent event) {
super(event);
if (event.getCalculatedFieldId() != null) {
this.calculatedFieldId = event.getCalculatedFieldId().getId();
}
if (event.getEventEntity() != null) {
this.eventEntityId = event.getEventEntity().getId();
this.eventEntityType = event.getEventEntity().getEntityType().name();
}
this.msgId = event.getMsgId();
this.msgType = event.getMsgType();
this.arguments = event.getArguments();
this.result = event.getResult();
this.error = event.getError();
}
@Override
public CalculatedFieldDebugEvent toData() {
var builder = CalculatedFieldDebugEvent.builder()
.id(id)
.tenantId(TenantId.fromUUID(tenantId))
.ts(ts)
.serviceId(serviceId)
.entityId(entityId)
.msgId(msgId)
.msgType(msgType)
.arguments(arguments)
.result(result)
.error(error);
if (calculatedFieldId != null) {
builder.calculatedFieldId(new CalculatedFieldId(calculatedFieldId));
}
if (eventEntityId != null) {
builder.eventEntity(EntityIdFactory.getByTypeAndUuid(eventEntityType, eventEntityId));
}
return builder.build();
}
}

7
dao/src/main/java/org/thingsboard/server/dao/model/sql/CalculatedFieldEntity.java

@ -26,6 +26,7 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.debug.DebugSettings;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
@ -45,6 +46,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.CALCULATED_FIELD_T
import static org.thingsboard.server.dao.model.ModelConstants.CALCULATED_FIELD_TENANT_ID_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.CALCULATED_FIELD_TYPE;
import static org.thingsboard.server.dao.model.ModelConstants.CALCULATED_FIELD_VERSION;
import static org.thingsboard.server.dao.model.ModelConstants.DEBUG_SETTINGS;
@Data
@EqualsAndHashCode(callSuper = true)
@ -77,6 +79,9 @@ public class CalculatedFieldEntity extends BaseSqlEntity<CalculatedField> implem
@Column(name = CALCULATED_FIELD_VERSION)
private Long version;
@Column(name = DEBUG_SETTINGS)
private String debugSettings;
@Column(name = CALCULATED_FIELD_EXTERNAL_ID)
private UUID externalId;
@ -95,6 +100,7 @@ public class CalculatedFieldEntity extends BaseSqlEntity<CalculatedField> implem
this.configurationVersion = calculatedField.getConfigurationVersion();
this.configuration = JacksonUtil.valueToTree(calculatedField.getConfiguration());
this.version = calculatedField.getVersion();
this.debugSettings = JacksonUtil.toString(calculatedField.getDebugSettings());
if (calculatedField.getExternalId() != null) {
this.externalId = calculatedField.getExternalId().getId();
}
@ -111,6 +117,7 @@ public class CalculatedFieldEntity extends BaseSqlEntity<CalculatedField> implem
calculatedField.setConfigurationVersion(configurationVersion);
calculatedField.setConfiguration(JacksonUtil.treeToValue(configuration, CalculatedFieldConfiguration.class));
calculatedField.setVersion(version);
calculatedField.setDebugSettings(JacksonUtil.fromString(debugSettings, DebugSettings.class));
if (externalId != null) {
calculatedField.setExternalId(new CalculatedFieldId(externalId));
}

135
dao/src/main/java/org/thingsboard/server/dao/sql/event/CalculatedFieldDebugEventRepository.java

@ -0,0 +1,135 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.sql.event;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.server.common.data.event.CalculatedFieldDebugEvent;
import org.thingsboard.server.dao.model.sql.CalculatedFieldDebugEventEntity;
import java.util.List;
import java.util.UUID;
public interface CalculatedFieldDebugEventRepository extends EventRepository<CalculatedFieldDebugEventEntity, CalculatedFieldDebugEvent>, JpaRepository<CalculatedFieldDebugEventEntity, UUID> {
@Override
@Query(nativeQuery = true, value = "SELECT * FROM cf_debug_event e WHERE e.tenant_id = :tenantId AND e.entity_id = :entityId ORDER BY e.ts DESC LIMIT :limit")
List<CalculatedFieldDebugEventEntity> findLatestEvents(@Param("tenantId") UUID tenantId, @Param("entityId") UUID entityId, @Param("limit") int limit);
@Override
@Query("SELECT e FROM RuleNodeDebugEventEntity e WHERE " +
"e.tenantId = :tenantId " +
"AND e.entityId = :entityId " +
"AND (:startTime IS NULL OR e.ts >= :startTime) " +
"AND (:endTime IS NULL OR e.ts <= :endTime)"
)
Page<CalculatedFieldDebugEventEntity> findEvents(@Param("tenantId") UUID tenantId,
@Param("entityId") UUID entityId,
@Param("startTime") Long startTime,
@Param("endTime") Long endTime,
Pageable pageable);
@Query(nativeQuery = true,
value = "SELECT * FROM cf_debug_event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_id = :entityId " +
"AND (:startTime IS NULL OR e.ts >= :startTime) " +
"AND (:endTime IS NULL OR e.ts <= :endTime) " +
"AND (:serviceId IS NULL OR e.service_id ILIKE concat('%', :serviceId, '%')) " +
"AND (:calculatedFieldId IS NULL OR e.cf_id = uuid(:calculatedFieldId)) " +
"AND (:eventEntityId IS NULL OR e.e_entity_id = uuid(:eventEntityId)) " +
"AND (:eventEntityType IS NULL OR e.e_entity_type ILIKE concat('%', :eventEntityType, '%')) " +
"AND (:msgId IS NULL OR e.e_msg_id = uuid(:msgId)) " +
"AND (:msgType IS NULL OR e.e_msg_type ILIKE concat('%', :msgType, '%')) " +
"AND ((:isError = FALSE) OR e.e_error IS NOT NULL) " +
"AND (:error IS NULL OR e.e_error ILIKE concat('%', :error, '%'))"
,
countQuery = "SELECT count(*) FROM rule_node_debug_event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_id = :entityId " +
"AND (:startTime IS NULL OR e.ts >= :startTime) " +
"AND (:endTime IS NULL OR e.ts <= :endTime) " +
"AND (:serviceId IS NULL OR e.service_id ILIKE concat('%', :serviceId, '%')) " +
"AND (:calculatedFieldId IS NULL OR e.cf_id = uuid(:calculatedFieldId)) " +
"AND (:eventEntityId IS NULL OR e.e_entity_id = uuid(:eventEntityId)) " +
"AND (:eventEntityType IS NULL OR e.e_entity_type ILIKE concat('%', :eventEntityType, '%')) " +
"AND (:msgId IS NULL OR e.e_msg_id = uuid(:msgId)) " +
"AND (:msgType IS NULL OR e.e_msg_type ILIKE concat('%', :msgType, '%')) " +
"AND ((:isError = FALSE) OR e.e_error IS NOT NULL) " +
"AND (:error IS NULL OR e.e_error ILIKE concat('%', :error, '%'))"
)
Page<CalculatedFieldDebugEventEntity> findEvents(@Param("tenantId") UUID tenantId,
@Param("entityId") UUID entityId,
@Param("startTime") Long startTime,
@Param("endTime") Long endTime,
@Param("serviceId") String server,
@Param("calculatedFieldId") UUID calculatedFieldId,
@Param("eventEntityId") String eventEntityId,
@Param("eventEntityType") String eventEntityType,
@Param("msgId") String eventMsgId,
@Param("msgType") String eventMsgType,
@Param("isError") boolean isError,
@Param("error") String error,
Pageable pageable);
@Transactional
@Modifying
@Query("DELETE FROM CalculatedFieldDebugEventEntity e WHERE " +
"e.tenantId = :tenantId " +
"AND e.entityId = :entityId " +
"AND (:startTime IS NULL OR e.ts >= :startTime) " +
"AND (:endTime IS NULL OR e.ts <= :endTime)"
)
void removeEvents(@Param("tenantId") UUID tenantId,
@Param("entityId") UUID entityId,
@Param("startTime") Long startTime,
@Param("endTime") Long endTime);
@Transactional
@Modifying
@Query(nativeQuery = true,
value = "DELETE FROM cf_debug_event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_id = :entityId " +
"AND (:startTime IS NULL OR e.ts >= :startTime) " +
"AND (:endTime IS NULL OR e.ts <= :endTime) " +
"AND (:serviceId IS NULL OR e.service_id ILIKE concat('%', :serviceId, '%')) " +
"AND (:calculatedFieldId IS NULL OR e.cf_id = uuid(:calculatedFieldId)) " +
"AND (:eventEntityId IS NULL OR e.e_entity_id = uuid(:eventEntityId)) " +
"AND (:eventEntityType IS NULL OR e.e_entity_type ILIKE concat('%', :eventEntityType, '%')) " +
"AND (:msgId IS NULL OR e.e_msg_id = uuid(:msgId)) " +
"AND (:msgType IS NULL OR e.e_msg_type ILIKE concat('%', :msgType, '%')) " +
"AND ((:isError = FALSE) OR e.e_error IS NOT NULL) " +
"AND (:error IS NULL OR e.e_error ILIKE concat('%', :error, '%'))")
void removeEvents(@Param("tenantId") UUID tenantId,
@Param("entityId") UUID entityId,
@Param("startTime") Long startTime,
@Param("endTime") Long endTime,
@Param("serviceId") String server,
@Param("calculatedFieldId") UUID calculatedFieldId,
@Param("eventEntityId") String eventEntityId,
@Param("eventEntityType") String eventEntityType,
@Param("msgId") String eventMsgId,
@Param("msgType") String eventMsgType,
@Param("isError") boolean isError,
@Param("error") String error);
}

5
dao/src/main/java/org/thingsboard/server/dao/sql/event/DedicatedJpaEventDao.java

@ -36,10 +36,11 @@ public class DedicatedJpaEventDao extends JpaBaseEventDao {
RuleNodeDebugEventRepository ruleNodeDebugEventRepository,
RuleChainDebugEventRepository ruleChainDebugEventRepository,
ScheduledLogExecutorComponent logExecutor,
StatsFactory statsFactory) {
StatsFactory statsFactory,
CalculatedFieldDebugEventRepository cfDebugEventRepository) {
super(partitionConfiguration, partitioningRepository, lcEventRepository, statsEventRepository,
errorEventRepository, eventInsertRepository, ruleNodeDebugEventRepository,
ruleChainDebugEventRepository, logExecutor, statsFactory);
ruleChainDebugEventRepository, logExecutor, statsFactory, cfDebugEventRepository);
}
}

29
dao/src/main/java/org/thingsboard/server/dao/sql/event/EventInsertRepository.java

@ -25,6 +25,7 @@ import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.thingsboard.server.common.data.event.CalculatedFieldDebugEvent;
import org.thingsboard.server.common.data.event.ErrorEvent;
import org.thingsboard.server.common.data.event.Event;
import org.thingsboard.server.common.data.event.EventType;
@ -81,6 +82,9 @@ public class EventInsertRepository {
insertStmtMap.put(EventType.DEBUG_RULE_CHAIN, "INSERT INTO " + EventType.DEBUG_RULE_CHAIN.getTable() +
" (id, tenant_id, ts, entity_id, service_id, e_message, e_error) " +
"VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT DO NOTHING;");
insertStmtMap.put(EventType.DEBUG_CALCULATED_FIELD, "INSERT INTO " + EventType.DEBUG_CALCULATED_FIELD.getTable() +
" (id, tenant_id, tsб entity_id, service_id, cf_id, e_entity_id, e_entity_type, e_msg_id, e_msg_type, e_args, e_result, e_error) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT DO NOTHING;");
}
public void save(List<Event> entities) {
@ -107,6 +111,8 @@ public class EventInsertRepository {
return getRuleNodeEventSetter(events);
case DEBUG_RULE_CHAIN:
return getRuleChainEventSetter(events);
case DEBUG_CALCULATED_FIELD:
return getCalculatedFieldEventSetter(events);
default:
throw new RuntimeException(eventType + " support is not implemented!");
}
@ -206,6 +212,29 @@ public class EventInsertRepository {
};
}
private BatchPreparedStatementSetter getCalculatedFieldEventSetter(List<Event> events) {
return new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
CalculatedFieldDebugEvent event = (CalculatedFieldDebugEvent) events.get(i);
setCommonEventFields(ps, event);
safePutUUID(ps, 6, event.getCalculatedFieldId().getId());
safePutUUID(ps, 7, event.getEventEntity() != null ? event.getEventEntity().getId() : null);
safePutString(ps, 8, event.getEventEntity() != null ? event.getEventEntity().getEntityType().name() : null);
safePutUUID(ps, 9, event.getMsgId());
safePutString(ps, 10, event.getMsgType());
safePutString(ps, 11, event.getArguments());
safePutString(ps, 12, event.getResult());
safePutString(ps, 13, event.getError());
}
@Override
public int getBatchSize() {
return events.size();
}
};
}
void safePutString(PreparedStatement ps, int parameterIdx, String value) throws SQLException {
if (value != null) {
ps.setString(parameterIdx, replaceNullChars(value));

47
dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java

@ -24,6 +24,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.event.CalculatedFieldDebugEventFilter;
import org.thingsboard.server.common.data.event.ErrorEventFilter;
import org.thingsboard.server.common.data.event.Event;
import org.thingsboard.server.common.data.event.EventFilter;
@ -72,6 +73,7 @@ public class JpaBaseEventDao implements EventDao {
private final RuleChainDebugEventRepository ruleChainDebugEventRepository;
private final ScheduledLogExecutorComponent logExecutor;
private final StatsFactory statsFactory;
private final CalculatedFieldDebugEventRepository calculatedFieldDebugEventRepository;
@Value("${sql.events.batch_size:10000}")
private int batchSize;
@ -110,6 +112,7 @@ public class JpaBaseEventDao implements EventDao {
repositories.put(EventType.ERROR, errorEventRepository);
repositories.put(EventType.DEBUG_RULE_NODE, ruleNodeDebugEventRepository);
repositories.put(EventType.DEBUG_RULE_CHAIN, ruleChainDebugEventRepository);
repositories.put(EventType.DEBUG_CALCULATED_FIELD, calculatedFieldDebugEventRepository);
}
@PreDestroy
@ -158,6 +161,8 @@ public class JpaBaseEventDao implements EventDao {
return findEventByFilter(tenantId, entityId, (ErrorEventFilter) eventFilter, pageLink);
case STATS:
return findEventByFilter(tenantId, entityId, (StatisticsEventFilter) eventFilter, pageLink);
case DEBUG_CALCULATED_FIELD:
return findEventByFilter(tenantId, entityId, (CalculatedFieldDebugEventFilter) eventFilter, pageLink);
default:
throw new RuntimeException("Not supported event type: " + eventFilter.getEventType());
}
@ -193,6 +198,8 @@ public class JpaBaseEventDao implements EventDao {
case STATS:
removeEventsByFilter(tenantId, entityId, (StatisticsEventFilter) eventFilter, startTime, endTime);
break;
case DEBUG_CALCULATED_FIELD:
removeEventsByFilter(tenantId, entityId, (CalculatedFieldDebugEventFilter) eventFilter, startTime, endTime);
default:
throw new RuntimeException("Not supported event type: " + eventFilter.getEventType());
}
@ -286,6 +293,27 @@ public class JpaBaseEventDao implements EventDao {
);
}
private PageData<? extends Event> findEventByFilter(UUID tenantId, UUID entityId, CalculatedFieldDebugEventFilter eventFilter, TimePageLink pageLink) {
parseUUID(eventFilter.getCalculatedFieldId(), "Calculated Field Id");
parseUUID(eventFilter.getEntityId(), "Entity Id");
parseUUID(eventFilter.getMsgId(), "Message Id");
return DaoUtil.toPageData(
calculatedFieldDebugEventRepository.findEvents(
tenantId,
entityId,
pageLink.getStartTime(),
pageLink.getEndTime(),
eventFilter.getServer(),
UUID.fromString(eventFilter.getCalculatedFieldId()),
eventFilter.getEntityId(),
eventFilter.getEntityType(),
eventFilter.getMsgId(),
eventFilter.getMsgType(),
eventFilter.isError(),
eventFilter.getErrorStr(),
DaoUtil.toPageable(pageLink, EventEntity.eventColumnMap)));
}
private void removeEventsByFilter(UUID tenantId, UUID entityId, RuleChainDebugEventFilter eventFilter, Long startTime, Long endTime) {
ruleChainDebugEventRepository.removeEvents(
tenantId,
@ -360,6 +388,25 @@ public class JpaBaseEventDao implements EventDao {
);
}
private void removeEventsByFilter(UUID tenantId, UUID entityId, CalculatedFieldDebugEventFilter eventFilter, Long startTime, Long endTime) {
parseUUID(eventFilter.getCalculatedFieldId(), "Calculated Field Id");
parseUUID(eventFilter.getEntityId(), "Entity Id");
parseUUID(eventFilter.getMsgId(), "Message Id");
calculatedFieldDebugEventRepository.removeEvents(
tenantId,
entityId,
startTime,
endTime,
eventFilter.getServer(),
UUID.fromString(eventFilter.getCalculatedFieldId()),
eventFilter.getEntityId(),
eventFilter.getEntityType(),
eventFilter.getMsgId(),
eventFilter.getMsgType(),
eventFilter.isError(),
eventFilter.getErrorStr());
}
@Override
public List<? extends Event> findLatestEvents(UUID tenantId, UUID entityId, EventType eventType, int limit) {
return DaoUtil.convertDataList(getEventRepository(eventType).findLatestEvents(tenantId, entityId, limit));

17
dao/src/main/resources/sql/schema-entities.sql

@ -934,6 +934,7 @@ CREATE TABLE IF NOT EXISTS calculated_field (
configuration_version int DEFAULT 0,
configuration varchar(1000000),
version BIGINT DEFAULT 1,
debug_settings varchar(1024),
external_id UUID,
CONSTRAINT calculated_field_unq_key UNIQUE (entity_id, name),
CONSTRAINT calculated_field_external_id_unq_key UNIQUE (tenant_id, external_id)
@ -949,3 +950,19 @@ CREATE TABLE IF NOT EXISTS calculated_field_link (
configuration varchar(10000),
CONSTRAINT fk_calculated_field_id FOREIGN KEY (calculated_field_id) REFERENCES calculated_field(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS cf_debug_event (
id uuid NOT NULL,
tenant_id uuid NOT NULL ,
ts bigint NOT NULL,
entity_id uuid NOT NULL,
service_id varchar,
cf_id uuid NOT NULL,
e_entity_id uuid,
e_entity_type varchar,
e_msg_id uuid,
e_msg_type varchar,
e_args varchar,
e_result varchar,
e_error varchar
) PARTITION BY RANGE (ts);

Loading…
Cancel
Save