diff --git a/application/pom.xml b/application/pom.xml
index 6d1e009d00..33b21b3243 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.0-SNAPSHOT
+ 3.4.1-SNAPSHOT
thingsboard
application
diff --git a/application/src/main/data/upgrade/3.3.4/schema_update.sql b/application/src/main/data/upgrade/3.3.4/schema_update.sql
index 9ab6a65b86..c86a4fe1f4 100644
--- a/application/src/main/data/upgrade/3.3.4/schema_update.sql
+++ b/application/src/main/data/upgrade/3.3.4/schema_update.sql
@@ -33,16 +33,7 @@ ALTER TABLE widgets_bundle
ALTER TABLE entity_view
ADD COLUMN IF NOT EXISTS external_id UUID;
-CREATE INDEX IF NOT EXISTS idx_device_external_id ON device(tenant_id, external_id);
-CREATE INDEX IF NOT EXISTS idx_device_profile_external_id ON device_profile(tenant_id, external_id);
-CREATE INDEX IF NOT EXISTS idx_asset_external_id ON asset(tenant_id, external_id);
-CREATE INDEX IF NOT EXISTS idx_rule_chain_external_id ON rule_chain(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_rule_node_external_id ON rule_node(rule_chain_id, external_id);
-CREATE INDEX IF NOT EXISTS idx_dashboard_external_id ON dashboard(tenant_id, external_id);
-CREATE INDEX IF NOT EXISTS idx_customer_external_id ON customer(tenant_id, external_id);
-CREATE INDEX IF NOT EXISTS idx_widgets_bundle_external_id ON widgets_bundle(tenant_id, external_id);
-CREATE INDEX IF NOT EXISTS idx_entity_view_external_id ON entity_view(tenant_id, external_id);
-
CREATE INDEX IF NOT EXISTS idx_rule_node_type ON rule_node(type);
ALTER TABLE admin_settings
@@ -73,3 +64,77 @@ CREATE TABLE IF NOT EXISTS user_auth_settings (
CREATE INDEX IF NOT EXISTS idx_api_usage_state_entity_id ON api_usage_state(entity_id);
ALTER TABLE tenant_profile DROP COLUMN IF EXISTS isolated_tb_core;
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'device_external_id_unq_key') THEN
+ ALTER TABLE device ADD CONSTRAINT device_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'device_profile_external_id_unq_key') THEN
+ ALTER TABLE device_profile ADD CONSTRAINT device_profile_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'asset_external_id_unq_key') THEN
+ ALTER TABLE asset ADD CONSTRAINT asset_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'rule_chain_external_id_unq_key') THEN
+ ALTER TABLE rule_chain ADD CONSTRAINT rule_chain_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'dashboard_external_id_unq_key') THEN
+ ALTER TABLE dashboard ADD CONSTRAINT dashboard_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'customer_external_id_unq_key') THEN
+ ALTER TABLE customer ADD CONSTRAINT customer_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'widgets_bundle_external_id_unq_key') THEN
+ ALTER TABLE widgets_bundle ADD CONSTRAINT widgets_bundle_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'entity_view_external_id_unq_key') THEN
+ ALTER TABLE entity_view ADD CONSTRAINT entity_view_external_id_unq_key UNIQUE (tenant_id, external_id);
+ END IF;
+ END;
+$$;
+
diff --git a/application/src/main/data/upgrade/3.4.0/schema_update.sql b/application/src/main/data/upgrade/3.4.0/schema_update.sql
new file mode 100644
index 0000000000..12a86acebc
--- /dev/null
+++ b/application/src/main/data/upgrade/3.4.0/schema_update.sql
@@ -0,0 +1,230 @@
+--
+-- Copyright © 2016-2022 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.
+--
+
+CREATE TABLE IF NOT EXISTS rule_node_debug_event (
+ id uuid NOT NULL,
+ tenant_id uuid NOT NULL ,
+ ts bigint NOT NULL,
+ entity_id uuid NOT NULL,
+ service_id varchar,
+ e_type varchar,
+ e_entity_id uuid,
+ e_entity_type varchar,
+ e_msg_id uuid,
+ e_msg_type varchar,
+ e_data_type varchar,
+ e_relation_type varchar,
+ e_data varchar,
+ e_metadata varchar,
+ e_error varchar
+) PARTITION BY RANGE (ts);
+
+CREATE TABLE IF NOT EXISTS rule_chain_debug_event (
+ id uuid NOT NULL,
+ tenant_id uuid NOT NULL,
+ ts bigint NOT NULL,
+ entity_id uuid NOT NULL,
+ service_id varchar NOT NULL,
+ e_message varchar,
+ e_error varchar
+) PARTITION BY RANGE (ts);
+
+CREATE TABLE IF NOT EXISTS stats_event (
+ id uuid NOT NULL,
+ tenant_id uuid NOT NULL,
+ ts bigint NOT NULL,
+ entity_id uuid NOT NULL,
+ service_id varchar NOT NULL,
+ e_messages_processed bigint NOT NULL,
+ e_errors_occurred bigint NOT NULL
+) PARTITION BY RANGE (ts);
+
+CREATE TABLE IF NOT EXISTS lc_event (
+ id uuid NOT NULL,
+ tenant_id uuid NOT NULL,
+ ts bigint NOT NULL,
+ entity_id uuid NOT NULL,
+ service_id varchar NOT NULL,
+ e_type varchar NOT NULL,
+ e_success boolean NOT NULL,
+ e_error varchar
+) PARTITION BY RANGE (ts);
+
+CREATE TABLE IF NOT EXISTS error_event (
+ id uuid NOT NULL,
+ tenant_id uuid NOT NULL,
+ ts bigint NOT NULL,
+ entity_id uuid NOT NULL,
+ service_id varchar NOT NULL,
+ e_method varchar NOT NULL,
+ e_error varchar
+) PARTITION BY RANGE (ts);
+
+CREATE INDEX IF NOT EXISTS idx_rule_node_debug_event_main
+ ON rule_node_debug_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
+
+CREATE INDEX IF NOT EXISTS idx_rule_chain_debug_event_main
+ ON rule_chain_debug_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
+
+CREATE INDEX IF NOT EXISTS idx_stats_event_main
+ ON stats_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
+
+CREATE INDEX IF NOT EXISTS idx_lc_event_main
+ ON lc_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
+
+CREATE INDEX IF NOT EXISTS idx_error_event_main
+ ON error_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
+
+CREATE OR REPLACE FUNCTION to_safe_json(p_json text) RETURNS json
+LANGUAGE plpgsql AS
+$$
+BEGIN
+ return REPLACE(p_json, '\u0000', '' )::json;
+EXCEPTION
+ WHEN OTHERS THEN
+ return '{}'::json;
+END;
+$$;
+
+-- Useful to migrate old events to the new table structure;
+CREATE OR REPLACE PROCEDURE migrate_regular_events(IN start_ts_in_ms bigint, IN end_ts_in_ms bigint, IN partition_size_in_hours int)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ partition_size_in_ms bigint;
+ p record;
+ table_name varchar;
+BEGIN
+ partition_size_in_ms = partition_size_in_hours * 3600 * 1000;
+
+ FOR p IN SELECT DISTINCT event_type as event_type, (created_time - created_time % partition_size_in_ms) as partition_ts FROM event e WHERE e.event_type in ('STATS', 'LC_EVENT', 'ERROR') and ts >= start_ts_in_ms and ts < end_ts_in_ms
+ LOOP
+ IF p.event_type = 'STATS' THEN
+ table_name := 'stats_event';
+ ELSEIF p.event_type = 'LC_EVENT' THEN
+ table_name := 'lc_event';
+ ELSEIF p.event_type = 'ERROR' THEN
+ table_name := 'error_event';
+ END IF;
+ RAISE NOTICE '[%] Partition to create : [%-%]', table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms);
+ EXECUTE format('CREATE TABLE IF NOT EXISTS %s_%s PARTITION OF %s FOR VALUES FROM ( %s ) TO ( %s )', table_name, p.partition_ts, table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms));
+ END LOOP;
+
+ INSERT INTO stats_event
+ SELECT id,
+ tenant_id,
+ ts,
+ entity_id,
+ body ->> 'server',
+ (body ->> 'messagesProcessed')::bigint,
+ (body ->> 'errorsOccurred')::bigint
+ FROM
+ (select id, tenant_id, ts, entity_id, to_safe_json(body) as body
+ FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'STATS' AND to_safe_json(body) ->> 'server' IS NOT NULL
+ ) safe_event
+ ON CONFLICT DO NOTHING;
+
+ INSERT INTO lc_event
+ SELECT id,
+ tenant_id,
+ ts,
+ entity_id,
+ body ->> 'server',
+ body ->> 'event',
+ (body ->> 'success')::boolean,
+ body ->> 'error'
+ FROM
+ (select id, tenant_id, ts, entity_id, to_safe_json(body) as body
+ FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'LC_EVENT' AND to_safe_json(body) ->> 'server' IS NOT NULL
+ ) safe_event
+ ON CONFLICT DO NOTHING;
+
+ INSERT INTO error_event
+ SELECT id,
+ tenant_id,
+ ts,
+ entity_id,
+ body ->> 'server',
+ body ->> 'method',
+ body ->> 'error'
+ FROM
+ (select id, tenant_id, ts, entity_id, to_safe_json(body) as body
+ FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'ERROR' AND to_safe_json(body) ->> 'server' IS NOT NULL
+ ) safe_event
+ ON CONFLICT DO NOTHING;
+
+END
+$$;
+
+-- Useful to migrate old debug events to the new table structure;
+CREATE OR REPLACE PROCEDURE migrate_debug_events(IN start_ts_in_ms bigint, IN end_ts_in_ms bigint, IN partition_size_in_hours int)
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ partition_size_in_ms bigint;
+ p record;
+ table_name varchar;
+BEGIN
+ partition_size_in_ms = partition_size_in_hours * 3600 * 1000;
+
+ FOR p IN SELECT DISTINCT event_type as event_type, (created_time - created_time % partition_size_in_ms) as partition_ts FROM event e WHERE e.event_type in ('DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') and ts >= start_ts_in_ms and ts < end_ts_in_ms
+ LOOP
+ IF p.event_type = 'DEBUG_RULE_NODE' THEN
+ table_name := 'rule_node_debug_event';
+ ELSEIF p.event_type = 'DEBUG_RULE_CHAIN' THEN
+ table_name := 'rule_chain_debug_event';
+ END IF;
+ RAISE NOTICE '[%] Partition to create : [%-%]', table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms);
+ EXECUTE format('CREATE TABLE IF NOT EXISTS %s_%s PARTITION OF %s FOR VALUES FROM ( %s ) TO ( %s )', table_name, p.partition_ts, table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms));
+ END LOOP;
+
+ INSERT INTO rule_node_debug_event
+ SELECT id,
+ tenant_id,
+ ts,
+ entity_id,
+ body ->> 'server',
+ body ->> 'type',
+ (body ->> 'entityId')::uuid,
+ body ->> 'entityName',
+ (body ->> 'msgId')::uuid,
+ body ->> 'msgType',
+ body ->> 'dataType',
+ body ->> 'relationType',
+ body ->> 'data',
+ body ->> 'metadata',
+ body ->> 'error'
+ FROM
+ (select id, tenant_id, ts, entity_id, to_safe_json(body) as body
+ FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'DEBUG_RULE_NODE' AND to_safe_json(body) ->> 'server' IS NOT NULL
+ ) safe_event
+ ON CONFLICT DO NOTHING;
+
+ INSERT INTO rule_chain_debug_event
+ SELECT id,
+ tenant_id,
+ ts,
+ entity_id,
+ body ->> 'server',
+ body ->> 'message',
+ body ->> 'error'
+ FROM
+ (select id, tenant_id, ts, entity_id, to_safe_json(body) as body
+ FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'DEBUG_RULE_CHAIN' AND to_safe_json(body) ->> 'server' IS NOT NULL
+ ) safe_event
+ ON CONFLICT DO NOTHING;
+END
+$$;
diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
index a38de7b839..b4c308db86 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
@@ -37,10 +37,11 @@ import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
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.DataConstants;
-import org.thingsboard.server.common.data.Event;
+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.EntityId;
-import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbActorMsg;
@@ -112,6 +113,29 @@ import java.util.concurrent.TimeUnit;
@Component
public class ActorSystemContext {
+ private static final FutureCallback RULE_CHAIN_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 Rule Chain", th);
+ }
+ };
+ private static final FutureCallback RULE_NODE_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 Node", th);
+ }
+ };
+
protected final ObjectMapper mapper = new ObjectMapper();
private final ConcurrentMap debugPerTenantLimits = new ConcurrentHashMap<>();
@@ -462,25 +486,28 @@ public class ActorSystemContext {
}
public void persistError(TenantId tenantId, EntityId entityId, String method, Exception e) {
- Event event = new Event();
- event.setTenantId(tenantId);
- event.setEntityId(entityId);
- event.setType(DataConstants.ERROR);
- event.setBody(toBodyJson(serviceInfoProvider.getServiceInfo().getServiceId(), method, toString(e)));
- persistEvent(event);
+ eventService.saveAsync(ErrorEvent.builder()
+ .tenantId(tenantId)
+ .entityId(entityId.getId())
+ .serviceId(getServiceId())
+ .method(method)
+ .error(toString(e)).build());
}
public void persistLifecycleEvent(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent lcEvent, Exception e) {
- Event event = new Event();
- event.setTenantId(tenantId);
- event.setEntityId(entityId);
- event.setType(DataConstants.LC_EVENT);
- event.setBody(toBodyJson(serviceInfoProvider.getServiceInfo().getServiceId(), lcEvent, Optional.ofNullable(e)));
- persistEvent(event);
- }
+ LifecycleEvent.LifecycleEventBuilder event = LifecycleEvent.builder()
+ .tenantId(tenantId)
+ .entityId(entityId.getId())
+ .serviceId(getServiceId())
+ .lcEventType(lcEvent.name());
+
+ if (e != null) {
+ event.success(false).error(toString(e));
+ } else {
+ event.success(true);
+ }
- private void persistEvent(Event event) {
- eventService.saveAsync(event);
+ eventService.saveAsync(event.build());
}
private String toString(Throwable e) {
@@ -489,21 +516,6 @@ public class ActorSystemContext {
return sw.toString();
}
- private JsonNode toBodyJson(String serviceId, ComponentLifecycleEvent event, Optional e) {
- ObjectNode node = mapper.createObjectNode().put("server", serviceId).put("event", event.name());
- if (e.isPresent()) {
- node = node.put("success", false);
- node = node.put("error", toString(e.get()));
- } else {
- node = node.put("success", true);
- }
- return node;
- }
-
- private JsonNode toBodyJson(String serviceId, String method, String body) {
- return mapper.createObjectNode().put("server", serviceId).put("method", method).put("error", body);
- }
-
public TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId) {
return partitionService.resolve(serviceType, tenantId, entityId);
}
@@ -539,44 +551,27 @@ public class ActorSystemContext {
private void persistDebugAsync(TenantId tenantId, EntityId entityId, String type, TbMsg tbMsg, String relationType, Throwable error, String failureMessage) {
if (checkLimits(tenantId, tbMsg, error)) {
try {
- Event event = new Event();
- event.setTenantId(tenantId);
- event.setEntityId(entityId);
- event.setType(DataConstants.DEBUG_RULE_NODE);
-
- String metadata = mapper.writeValueAsString(tbMsg.getMetaData().getData());
-
- ObjectNode node = mapper.createObjectNode()
- .put("type", type)
- .put("server", getServiceId())
- .put("entityId", tbMsg.getOriginator().getId().toString())
- .put("entityName", tbMsg.getOriginator().getEntityType().name())
- .put("msgId", tbMsg.getId().toString())
- .put("msgType", tbMsg.getType())
- .put("dataType", tbMsg.getDataType().name())
- .put("relationType", relationType)
- .put("data", tbMsg.getData())
- .put("metadata", metadata);
+ RuleNodeDebugEvent.RuleNodeDebugEventBuilder event = RuleNodeDebugEvent.builder()
+ .tenantId(tenantId)
+ .entityId(entityId.getId())
+ .serviceId(getServiceId())
+ .eventType(type)
+ .eventEntity(tbMsg.getOriginator())
+ .msgId(tbMsg.getId())
+ .msgType(tbMsg.getType())
+ .dataType(tbMsg.getDataType().name())
+ .relationType(relationType)
+ .data(tbMsg.getData())
+ .metadata(mapper.writeValueAsString(tbMsg.getMetaData().getData()));
if (error != null) {
- node = node.put("error", toString(error));
+ event.error(toString(error));
} else if (failureMessage != null) {
- node = node.put("error", failureMessage);
+ event.error(failureMessage);
}
- event.setBody(node);
- ListenableFuture future = eventService.saveAsync(event);
- Futures.addCallback(future, new FutureCallback() {
- @Override
- public void onSuccess(@Nullable Void event) {
-
- }
-
- @Override
- public void onFailure(Throwable th) {
- log.error("Could not save debug Event for Node", th);
- }
- }, MoreExecutors.directExecutor());
+ ListenableFuture future = eventService.saveAsync(event.build());
+ Futures.addCallback(future, RULE_NODE_DEBUG_EVENT_ERROR_CALLBACK, MoreExecutors.directExecutor());
} catch (IOException ex) {
log.warn("Failed to persist rule node debug message", ex);
}
@@ -603,33 +598,17 @@ public class ActorSystemContext {
}
private void persistRuleChainDebugModeEvent(TenantId tenantId, EntityId entityId, Throwable error) {
- Event event = new Event();
- event.setTenantId(tenantId);
- event.setEntityId(entityId);
- event.setType(DataConstants.DEBUG_RULE_CHAIN);
-
- ObjectNode node = mapper.createObjectNode()
- //todo: what fields are needed here?
- .put("server", getServiceId())
- .put("message", "Reached debug mode rate limit!");
-
+ RuleChainDebugEvent.RuleChainDebugEventBuilder event = RuleChainDebugEvent.builder()
+ .tenantId(tenantId)
+ .entityId(entityId.getId())
+ .serviceId(getServiceId())
+ .message("Reached debug mode rate limit!");
if (error != null) {
- node = node.put("error", toString(error));
+ event.error(toString(error));
}
- event.setBody(node);
- ListenableFuture future = eventService.saveAsync(event);
- Futures.addCallback(future, new FutureCallback() {
- @Override
- public void onSuccess(@Nullable Void event) {
-
- }
-
- @Override
- public void onFailure(Throwable th) {
- log.error("Could not save debug Event for Rule Chain", th);
- }
- }, MoreExecutors.directExecutor());
+ ListenableFuture future = eventService.saveAsync(event.build());
+ Futures.addCallback(future, RULE_CHAIN_DEBUG_EVENT_ERROR_CALLBACK, MoreExecutors.directExecutor());
}
public static Exception toException(Throwable error) {
diff --git a/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java b/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java
index 8cdf5cec02..247a6c62d0 100644
--- a/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java
@@ -20,13 +20,13 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbActor;
-import org.thingsboard.server.actors.TbActorCtx;
import org.thingsboard.server.actors.TbActorId;
import org.thingsboard.server.actors.TbStringActorId;
import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.common.data.DataConstants;
-import org.thingsboard.server.common.data.Event;
+import org.thingsboard.server.common.data.EventInfo;
+import org.thingsboard.server.common.data.event.StatisticsEvent;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg;
@@ -54,12 +54,14 @@ public class StatsActor extends ContextAwareActor {
if (msg.isEmpty()) {
return;
}
- Event event = new Event();
- event.setEntityId(msg.getEntityId());
- event.setTenantId(msg.getTenantId());
- event.setType(DataConstants.STATS);
- event.setBody(toBodyJson(systemContext.getServiceInfoProvider().getServiceId(), msg.getMessagesProcessed(), msg.getErrorsOccurred()));
- systemContext.getEventService().saveAsync(event);
+ systemContext.getEventService().saveAsync(StatisticsEvent.builder()
+ .tenantId(msg.getTenantId())
+ .entityId(msg.getEntityId().getId())
+ .serviceId(systemContext.getServiceInfoProvider().getServiceId())
+ .messagesProcessed(msg.getMessagesProcessed())
+ .errorsOccurred(msg.getErrorsOccurred())
+ .build()
+ );
}
private JsonNode toBodyJson(String serviceId, long messagesProcessed, long errorsOccurred) {
diff --git a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
index b6ba0088bb..e58b00f9e6 100644
--- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
@@ -18,7 +18,6 @@ package org.thingsboard.server.config;
import com.fasterxml.classmate.TypeResolver;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
-import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -153,7 +152,7 @@ public class SwaggerConfiguration {
}
@Override
- public boolean supports(@NotNull DocumentationType delimiter) {
+ public boolean supports(DocumentationType delimiter) {
return DocumentationType.SWAGGER_2.equals(delimiter) || DocumentationType.OAS_30.equals(delimiter);
}
};
@@ -175,7 +174,7 @@ public class SwaggerConfiguration {
}
@Override
- public boolean supports(@NotNull DocumentationType delimiter) {
+ public boolean supports(DocumentationType delimiter) {
return DocumentationType.SWAGGER_2.equals(delimiter) || DocumentationType.OAS_30.equals(delimiter);
}
};
diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
index 5003aac526..d2e11086c7 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
@@ -126,7 +126,9 @@ public class AlarmController extends BaseController {
"\n\nPlatform also deduplicate the alarms based on the entity id of originator and alarm 'type'. " +
"For example, if the user or system component create the alarm with the type 'HighTemperature' for device 'Device A' the new active alarm is created. " +
"If the user tries to create 'HighTemperature' alarm for the same device again, the previous alarm will be updated (the 'end_ts' will be set to current timestamp). " +
- "If the user clears the alarm (see 'Clear Alarm(clearAlarm)'), than new alarm with the same type and same device may be created. " + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH
+ "If the user clears the alarm (see 'Clear Alarm(clearAlarm)'), than new alarm with the same type and same device may be created. " +
+ "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Alarm entity. " +
+ TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH
, produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm", method = RequestMethod.POST)
diff --git a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
index a425b0968a..d4199006d0 100644
--- a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
+++ b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
@@ -94,7 +94,7 @@ public class ControllerConstants {
protected static final String DEVICE_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, transportType, description, isDefault";
protected static final String ASSET_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, label, customerTitle";
protected static final String ALARM_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, startTs, endTs, type, ackTs, clearTs, severity, status";
- protected static final String EVENT_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, id";
+ protected static final String EVENT_SORT_PROPERTY_ALLOWABLE_VALUES = "ts, id";
protected static final String EDGE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, label, customerTitle";
protected static final String RULE_CHAIN_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, root";
protected static final String WIDGET_BUNDLE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title, tenantId";
@@ -112,6 +112,10 @@ public class ControllerConstants {
protected static final String DEVICE_PROFILE_INFO_DESCRIPTION = "Device Profile Info is a lightweight object that includes main information about Device Profile excluding the heavyweight configuration object. ";
protected static final String QUEUE_SERVICE_TYPE_DESCRIPTION = "Service type (implemented only for the TB-RULE-ENGINE)";
protected static final String QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES = "TB-RULE-ENGINE, TB-CORE, TB-TRANSPORT, JS-EXECUTOR";
+ protected static final String QUEUE_QUEUE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the queue name.";
+ protected static final String QUEUE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, topic";
+ protected static final String QUEUE_ID_PARAM_DESCRIPTION = "A string value representing the queue id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
+ protected static final String QUEUE_NAME_PARAM_DESCRIPTION = "A string value representing the queue id. For example, 'Main'";
protected static final String OTA_PACKAGE_INFO_DESCRIPTION = "OTA Package Info is a lightweight object that includes main information about the OTA Package excluding the heavyweight data. ";
protected static final String OTA_PACKAGE_DESCRIPTION = "OTA Package is a heavyweight object that includes main information about the OTA Package and also data. ";
protected static final String OTA_PACKAGE_CHECKSUM_ALGORITHM_ALLOWABLE_VALUES = "MD5, SHA256, SHA384, SHA512, CRC32, MURMUR3_32, MURMUR3_128";
diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
index 2510974991..591eaa1318 100644
--- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
@@ -138,7 +138,9 @@ public class CustomerController extends BaseController {
notes = "Creates or Updates the Customer. When creating customer, platform generates Customer Id as " + UUID_WIKI_LINK +
"The newly created Customer Id will be present in the response. " +
"Specify existing Customer Id to update the Customer. " +
- "Referencing non-existing Customer Id will cause 'Not Found' error." + TENANT_AUTHORITY_PARAGRAPH)
+ "Referencing non-existing Customer Id will cause 'Not Found' error." +
+ "Remove 'id', 'tenantId' from the request body example (below) to create new Customer entity. " +
+ TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer", method = RequestMethod.POST)
@ResponseBody
diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
index c6ff15c3ac..9ceeb0d14c 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
@@ -172,6 +172,7 @@ public class DashboardController extends BaseController {
"The newly created Dashboard id will be present in the response. " +
"Specify existing Dashboard id to update the dashboard. " +
"Referencing non-existing dashboard Id will cause 'Not Found' error. " +
+ "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Dashboard entity. " +
TENANT_AUTHORITY_PARAGRAPH,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
index 895e711073..ae1558381f 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
@@ -158,8 +158,9 @@ public class DeviceController extends BaseController {
"The newly created device id will be present in the response. " +
"Specify existing Device id to update the device. " +
"Referencing non-existing device Id will cause 'Not Found' error." +
- "\n\nDevice name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the device names and non-unique 'label' field for user-friendly visualization purposes."
- + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
+ "\n\nDevice name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the device names and non-unique 'label' field for user-friendly visualization purposes." +
+ "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Device entity. " +
+ TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device", method = RequestMethod.POST)
@ResponseBody
@@ -181,6 +182,7 @@ public class DeviceController extends BaseController {
"Requires to provide the Device Credentials object as well. Useful to create device and credentials in one request. " +
"You may find the example of LwM2M device and RPK credentials below: \n\n" +
DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_DESCRIPTION_MARKDOWN +
+ "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Device entity. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device-with-credentials", method = RequestMethod.POST)
diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
index 08568a28ce..e45a195a2d 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
@@ -191,6 +191,7 @@ public class DeviceProfileController extends BaseController {
"Specify existing device profile id to update the device profile. " +
"Referencing non-existing device profile Id will cause 'Not Found' error. " + NEW_LINE +
"Device profile name is unique in the scope of tenant. Only one 'default' device profile may exist in scope of tenant." + DEVICE_PROFILE_DATA +
+ "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Device Profile entity. " +
TENANT_AUTHORITY_PARAGRAPH,
produces = "application/json",
consumes = "application/json")
diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
index 5b2c6a6a9d..59c9eb39c1 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
@@ -141,8 +141,9 @@ public class EdgeController extends BaseController {
"The newly created edge id will be present in the response. " +
"Specify existing Edge id to update the edge. " +
"Referencing non-existing Edge Id will cause 'Not Found' error." +
- "\n\nEdge name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the edge names and non-unique 'label' field for user-friendly visualization purposes."
- + TENANT_AUTHORITY_PARAGRAPH,
+ "\n\nEdge name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the edge names and non-unique 'label' field for user-friendly visualization purposes." +
+ "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Edge entity. " +
+ TENANT_AUTHORITY_PARAGRAPH,
produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge", method = RequestMethod.POST)
diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
index 7d5de91890..0f299fefae 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
@@ -132,7 +132,9 @@ public class EntityViewController extends BaseController {
}
@ApiOperation(value = "Save or update entity view (saveEntityView)",
- notes = ENTITY_VIEW_DESCRIPTION + MODEL_DESCRIPTION,
+ notes = ENTITY_VIEW_DESCRIPTION + MODEL_DESCRIPTION +
+ "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Entity View entity." +
+ TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/entityView", method = RequestMethod.POST)
diff --git a/application/src/main/java/org/thingsboard/server/controller/EventController.java b/application/src/main/java/org/thingsboard/server/controller/EventController.java
index ac56978536..7dd82471c6 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EventController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EventController.java
@@ -29,8 +29,11 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
-import org.thingsboard.server.common.data.Event;
+import org.thingsboard.server.common.data.DataConstants;
+import org.thingsboard.server.common.data.EventInfo;
import org.thingsboard.server.common.data.event.EventFilter;
+import org.thingsboard.server.common.data.event.EventType;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
@@ -42,6 +45,8 @@ import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
+import java.util.Locale;
+
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_ID;
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_TYPE;
@@ -110,7 +115,7 @@ public class EventController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}/{eventType}", method = RequestMethod.GET)
@ResponseBody
- public PageData getEvents(
+ public PageData getEvents(
@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_TYPE) String strEntityType,
@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@@ -135,16 +140,12 @@ public class EventController extends BaseController {
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
- try {
- TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId));
+ TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId));
- EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
- checkEntityId(entityId, Operation.READ);
- TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
- return checkNotNull(eventService.findEvents(tenantId, entityId, eventType, pageLink));
- } catch (Exception e) {
- throw handleException(e);
- }
+ EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
+ checkEntityId(entityId, Operation.READ);
+ TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
+ return checkNotNull(eventService.findEvents(tenantId, entityId, resolveEventType(eventType), pageLink));
}
@ApiOperation(value = "Get Events (getEvents)",
@@ -153,7 +154,7 @@ public class EventController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.GET)
@ResponseBody
- public PageData getEvents(
+ public PageData getEvents(
@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_TYPE) String strEntityType,
@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@@ -176,18 +177,14 @@ public class EventController extends BaseController {
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
- try {
- TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId));
+ TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId));
- EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
- checkEntityId(entityId, Operation.READ);
+ EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
+ checkEntityId(entityId, Operation.READ);
- TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
+ TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
- return checkNotNull(eventService.findEvents(tenantId, entityId, pageLink));
- } catch (Exception e) {
- throw handleException(e);
- }
+ return checkNotNull(eventService.findEvents(tenantId, entityId, EventType.LC_EVENT, pageLink));
}
@ApiOperation(value = "Get Events by event filter (getEvents)",
@@ -198,7 +195,7 @@ public class EventController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.POST)
@ResponseBody
- public PageData getEvents(
+ public PageData getEvents(
@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_TYPE) String strEntityType,
@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@@ -223,21 +220,13 @@ public class EventController extends BaseController {
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
- try {
- TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId));
-
- EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
- checkEntityId(entityId, Operation.READ);
+ TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId));
- if (sortProperty != null && sortProperty.equals("createdTime") && eventFilter.hasFilterForJsonBody()) {
- sortProperty = ModelConstants.CREATED_TIME_PROPERTY;
- }
+ EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
+ checkEntityId(entityId, Operation.READ);
- TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
- return checkNotNull(eventService.findEventsByFilter(tenantId, entityId, eventFilter, pageLink));
- } catch (Exception e) {
- throw handleException(e);
- }
+ TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
+ return checkNotNull(eventService.findEventsByFilter(tenantId, entityId, eventFilter, pageLink));
}
@ApiOperation(value = "Clear Events (clearEvents)", notes = "Clears events by filter for specified entity.")
@@ -266,4 +255,13 @@ public class EventController extends BaseController {
}
}
+ private static EventType resolveEventType(String eventType) throws ThingsboardException {
+ for (var et : EventType.values()) {
+ if (et.name().equalsIgnoreCase(eventType) || et.getOldName().equalsIgnoreCase(eventType)) {
+ return et;
+ }
+ }
+ throw new ThingsboardException("Event type: '" + eventType + "' is not supported!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
+ }
+
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/QueueController.java b/application/src/main/java/org/thingsboard/server/controller/QueueController.java
index 0f29ced288..3b37e6727f 100644
--- a/application/src/main/java/org/thingsboard/server/controller/QueueController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/QueueController.java
@@ -15,6 +15,8 @@
*/
package org.thingsboard.server.controller;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
@@ -37,6 +39,22 @@ import org.thingsboard.server.service.security.permission.Resource;
import java.util.UUID;
+import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
+import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
+import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
+import static org.thingsboard.server.controller.ControllerConstants.QUEUE_ID_PARAM_DESCRIPTION;
+import static org.thingsboard.server.controller.ControllerConstants.QUEUE_NAME_PARAM_DESCRIPTION;
+import static org.thingsboard.server.controller.ControllerConstants.QUEUE_QUEUE_TEXT_SEARCH_DESCRIPTION;
+import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES;
+import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SERVICE_TYPE_DESCRIPTION;
+import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SORT_PROPERTY_ALLOWABLE_VALUES;
+import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
+import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
+import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
+import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH;
+import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH;
+import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK;
+
@RestController
@TbCoreComponent
@RequestMapping("/api")
@@ -45,14 +63,23 @@ public class QueueController extends BaseController {
private final TbQueueService tbQueueService;
+ @ApiOperation(value = "Get Queues (getTenantQueuesByServiceType)",
+ notes = "Returns a page of queues registered in the platform. " +
+ PAGE_DATA_PARAMETERS + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/queues", params = {"serviceType", "pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
- public PageData getTenantQueuesByServiceType(@RequestParam String serviceType,
+ public PageData getTenantQueuesByServiceType(@ApiParam(value = QUEUE_SERVICE_TYPE_DESCRIPTION, allowableValues = QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES, required = true)
+ @RequestParam String serviceType,
+ @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
+ @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
+ @ApiParam(value = QUEUE_QUEUE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
+ @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = QUEUE_SORT_PROPERTY_ALLOWABLE_VALUES)
@RequestParam(required = false) String sortProperty,
+ @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("serviceType", serviceType);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -65,28 +92,44 @@ public class QueueController extends BaseController {
}
}
+ @ApiOperation(value = "Get Queue (getQueueById)",
+ notes = "Fetch the Queue object based on the provided Queue Id. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/queues/{queueId}", method = RequestMethod.GET)
@ResponseBody
- public Queue getQueueById(@PathVariable("queueId") String queueIdStr) throws ThingsboardException {
+ public Queue getQueueById(@ApiParam(value = QUEUE_ID_PARAM_DESCRIPTION)
+ @PathVariable("queueId") String queueIdStr) throws ThingsboardException {
checkParameter("queueId", queueIdStr);
QueueId queueId = new QueueId(UUID.fromString(queueIdStr));
checkQueueId(queueId, Operation.READ);
return checkNotNull(queueService.findQueueById(getTenantId(), queueId));
}
+ @ApiOperation(value = "Get Queue (getQueueByName)",
+ notes = "Fetch the Queue object based on the provided Queue name. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/queues/name/{queueName}", method = RequestMethod.GET)
@ResponseBody
- public Queue getQueueByName(@PathVariable("queueName") String queueName) throws ThingsboardException {
+ public Queue getQueueByName(@ApiParam(value = QUEUE_NAME_PARAM_DESCRIPTION)
+ @PathVariable("queueName") String queueName) throws ThingsboardException {
checkParameter("queueName", queueName);
return checkNotNull(queueService.findQueueByTenantIdAndName(getTenantId(), queueName));
}
+ @ApiOperation(value = "Create Or Update Queue (saveQueue)",
+ notes = "Create or update the Queue. When creating queue, platform generates Queue Id as " + UUID_WIKI_LINK +
+ "Specify existing Queue id to update the queue. " +
+ "Referencing non-existing Queue Id will cause 'Not Found' error." +
+ "\n\nQueue name is unique in the scope of sysadmin. " +
+ "Remove 'id', 'tenantId' from the request body example (below) to create new Queue entity. " +
+ SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/queues", params = {"serviceType"}, method = RequestMethod.POST)
@ResponseBody
- public Queue saveQueue(@RequestBody Queue queue,
+
+ public Queue saveQueue(@ApiParam(value = "A JSON value representing the queue.")
+ @RequestBody Queue queue,
+ @ApiParam(value = QUEUE_SERVICE_TYPE_DESCRIPTION, allowableValues = QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES, required = true)
@RequestParam String serviceType) throws ThingsboardException {
checkParameter("serviceType", serviceType);
queue.setTenantId(getCurrentUser().getTenantId());
@@ -105,10 +148,12 @@ public class QueueController extends BaseController {
}
}
+ @ApiOperation(value = "Delete Queue (deleteQueue)", notes = "Deletes the Queue. " + SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/queues/{queueId}", method = RequestMethod.DELETE)
@ResponseBody
- public void deleteQueue(@PathVariable("queueId") String queueIdStr) throws ThingsboardException {
+ public void deleteQueue(@ApiParam(value = QUEUE_ID_PARAM_DESCRIPTION)
+ @PathVariable("queueId") String queueIdStr) throws ThingsboardException {
checkParameter("queueId", queueIdStr);
QueueId queueId = new QueueId(toUUID(queueIdStr));
checkQueueId(queueId, Operation.DELETE);
diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
index 38e7f5bb7d..5c79f9eca7 100644
--- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
@@ -39,10 +39,10 @@ import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.rule.engine.api.ScriptEngine;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
-import org.thingsboard.server.common.data.DataConstants;
-import org.thingsboard.server.common.data.Event;
+import org.thingsboard.server.common.data.EventInfo;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.edge.Edge;
+import org.thingsboard.server.common.data.event.EventType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
@@ -70,7 +70,6 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -227,7 +226,9 @@ public class RuleChainController extends BaseController {
"The newly created Rule Chain Id will be present in the response. " +
"Specify existing Rule Chain id to update the rule chain. " +
"Referencing non-existing rule chain Id will cause 'Not Found' error." +
- "\n\n" + RULE_CHAIN_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
+ "\n\n" + RULE_CHAIN_DESCRIPTION +
+ "Remove 'id', 'tenantId' from the request body example (below) to create new Rule Chain entity." +
+ TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain", method = RequestMethod.POST)
@ResponseBody
@@ -351,10 +352,10 @@ public class RuleChainController extends BaseController {
RuleNodeId ruleNodeId = new RuleNodeId(toUUID(strRuleNodeId));
checkRuleNode(ruleNodeId, Operation.READ);
TenantId tenantId = getCurrentUser().getTenantId();
- List events = eventService.findLatestEvents(tenantId, ruleNodeId, DataConstants.DEBUG_RULE_NODE, 2);
+ List events = eventService.findLatestEvents(tenantId, ruleNodeId, EventType.DEBUG_RULE_NODE, 2);
JsonNode result = null;
if (events != null) {
- for (Event event : events) {
+ for (EventInfo event : events) {
JsonNode body = event.getBody();
if (body.has("type") && body.get("type").asText().equals("IN")) {
result = body;
diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
index 4342a647c5..ec0b530117 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
@@ -139,7 +139,9 @@ public class TbResourceController extends BaseController {
"The newly created Resource id will be present in the response. " +
"Specify existing Resource id to update the Resource. " +
"Referencing non-existing Resource Id will cause 'Not Found' error. " +
- "\n\nResource combination of the title with the key is unique in the scope of tenant. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH,
+ "\n\nResource combination of the title with the key is unique in the scope of tenant. " +
+ "Remove 'id', 'tenantId' from the request body example (below) to create new Resource entity." +
+ SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH,
produces = "application/json",
consumes = "application/json")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantController.java b/application/src/main/java/org/thingsboard/server/controller/TenantController.java
index ef3c141131..b44c5327cd 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TenantController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TenantController.java
@@ -115,6 +115,7 @@ public class TenantController extends BaseController {
"The newly created Tenant Id will be present in the response. " +
"Specify existing Tenant Id id to update the Tenant. " +
"Referencing non-existing Tenant Id will cause 'Not Found' error." +
+ "Remove 'id', 'tenantId' from the request body example (below) to create new Tenant entity." +
SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/tenant", method = RequestMethod.POST)
diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java
index 9bbfcce351..e5a33da948 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java
@@ -164,6 +164,7 @@ public class TenantProfileController extends BaseController {
" \"default\": true\n" +
"}" +
MARKDOWN_CODE_BLOCK_END +
+ "Remove 'id', from the request body example (below) to create new Tenant Profile entity." +
SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/tenantProfile", method = RequestMethod.POST)
diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java
index e9375665bb..e96a73b65c 100644
--- a/application/src/main/java/org/thingsboard/server/controller/UserController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java
@@ -176,7 +176,9 @@ public class UserController extends BaseController {
"The newly created User Id will be present in the response. " +
"Specify existing User Id to update the device. " +
"Referencing non-existing User Id will cause 'Not Found' error." +
- "\n\nDevice email is unique for entire platform setup.")
+ "\n\nDevice email is unique for entire platform setup." +
+ "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new User entity." +
+ "\n\nAvailable for users with 'SYS_ADMIN', 'TENANT_ADMIN' or 'CUSTOMER_USER' authority.")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/user", method = RequestMethod.POST)
@ResponseBody
diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
index 71ea889c03..725d8ca2b4 100644
--- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
@@ -85,8 +85,9 @@ public class WidgetTypeController extends AutoCommitController {
"Specify existing Widget Type id to update the Widget Type. " +
"Referencing non-existing Widget Type Id will cause 'Not Found' error." +
"\n\nWidget Type alias is unique in the scope of Widget Bundle. " +
- "Special Tenant Id '13814000-1dd2-11b2-8080-808080808080' is automatically used if the create request is sent by user with 'SYS_ADMIN' authority."
- + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
+ "Special Tenant Id '13814000-1dd2-11b2-8080-808080808080' is automatically used if the create request is sent by user with 'SYS_ADMIN' authority." +
+ "Remove 'id', 'tenantId' rom the request body example (below) to create new Widget Type entity." +
+ SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetType", method = RequestMethod.POST)
@ResponseBody
diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
index 2d51c3dee8..23ec498d0e 100644
--- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
@@ -89,8 +89,9 @@ public class WidgetsBundleController extends BaseController {
"Specify existing Widget Bundle id to update the Widget Bundle. " +
"Referencing non-existing Widget Bundle Id will cause 'Not Found' error." +
"\n\nWidget Bundle alias is unique in the scope of tenant. " +
- "Special Tenant Id '13814000-1dd2-11b2-8080-808080808080' is automatically used if the create bundle request is sent by user with 'SYS_ADMIN' authority."
- + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
+ "Special Tenant Id '13814000-1dd2-11b2-8080-808080808080' is automatically used if the create bundle request is sent by user with 'SYS_ADMIN' authority." +
+ "Remove 'id', 'tenantId' from the request body example (below) to create new Widgets Bundle entity." +
+ SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetsBundle", method = RequestMethod.POST)
@ResponseBody
diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java
index 578bfa5662..3565af1ab0 100644
--- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java
+++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java
@@ -143,7 +143,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
externalSessionMap.put(externalSessionId, internalSessionId);
processInWebSocketService(sessionRef, SessionEvent.onEstablished());
- log.info("[{}][{}][{}] Session is opened", sessionRef.getSecurityCtx().getTenantId(), externalSessionId, session.getId());
+ log.info("[{}][{}][{}] Session is opened from address: {}", sessionRef.getSecurityCtx().getTenantId(), externalSessionId, session.getId(), session.getRemoteAddress());
} catch (InvalidParameterException e) {
log.warn("[{}] Failed to start session", session.getId(), e);
session.close(CloseStatus.BAD_DATA.withReason(e.getMessage()));
@@ -173,8 +173,10 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
cleanupLimits(session, sessionMd.sessionRef);
externalSessionMap.remove(sessionMd.sessionRef.getSessionId());
processInWebSocketService(sessionMd.sessionRef, SessionEvent.onClosed());
+ log.info("[{}][{}][{}] Session is closed", sessionMd.sessionRef.getSecurityCtx().getTenantId(), sessionMd.sessionRef.getSessionId(), session.getId());
+ } else {
+ log.info("[{}] Session is closed", session.getId());
}
- log.info("[{}] Session is closed", session.getId());
}
private void processInWebSocketService(TelemetryWebSocketSessionRef sessionRef, SessionEvent event) {
diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardCredentialsExpiredResponse.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardCredentialsExpiredResponse.java
index b02f85f84a..208fdead93 100644
--- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardCredentialsExpiredResponse.java
+++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardCredentialsExpiredResponse.java
@@ -34,7 +34,7 @@ public class ThingsboardCredentialsExpiredResponse extends ThingsboardErrorRespo
return new ThingsboardCredentialsExpiredResponse(message, resetToken);
}
- @ApiModelProperty(position = 5, value = "Password reset token", readOnly = true)
+ @ApiModelProperty(position = 5, value = "Password reset token", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public String getResetToken() {
return resetToken;
}
diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java
index 09fce98985..7cf2ca87f1 100644
--- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java
+++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java
@@ -46,12 +46,12 @@ public class ThingsboardErrorResponse {
return new ThingsboardErrorResponse(message, errorCode, status);
}
- @ApiModelProperty(position = 1, value = "HTTP Response Status Code", example = "401", readOnly = true)
+ @ApiModelProperty(position = 1, value = "HTTP Response Status Code", example = "401", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public Integer getStatus() {
return status.value();
}
- @ApiModelProperty(position = 2, value = "Error message", example = "Authentication failed", readOnly = true)
+ @ApiModelProperty(position = 2, value = "Error message", example = "Authentication failed", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public String getMessage() {
return message;
}
@@ -69,12 +69,12 @@ public class ThingsboardErrorResponse {
"\n\n* `34` - Too many updates (Too many updates over Websocket session)" +
"\n\n* `40` - Subscription violation (HTTP: 403 - Forbidden)",
example = "10", dataType = "integer",
- readOnly = true)
+ accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public ThingsboardErrorCode getErrorCode() {
return errorCode;
}
- @ApiModelProperty(position = 4, value = "Timestamp", readOnly = true)
+ @ApiModelProperty(position = 4, value = "Timestamp", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public Date getTimestamp() {
return timestamp;
}
diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java
index 785b3c4e98..30e934f386 100644
--- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java
+++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java
@@ -17,7 +17,6 @@ package org.thingsboard.server.exception;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
-import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
@@ -139,12 +138,11 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand
}
}
- @NotNull
@Override
protected ResponseEntity