diff --git a/application/pom.xml b/application/pom.xml
index 9c837aa96e..f3c042b46f 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 2.5.0-SNAPSHOT
+ 2.5.1-SNAPSHOT
thingsboard
application
@@ -34,10 +34,15 @@
UTF-8
${basedir}/..
+ java
+ false
+ process-resources
+ package
thingsboard
- /var/log/${pkg.name}
- /usr/share/${pkg.name}
${project.build.directory}/windows
+ true
+ ThingsBoard
+ org.thingsboard.server.ThingsboardServerApplication
@@ -311,6 +316,10 @@
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
org.apache.maven.plugins
maven-surefire-plugin
@@ -327,313 +336,30 @@
org.apache.maven.plugins
maven-resources-plugin
-
-
- copy-conf
- process-resources
-
- copy-resources
-
-
- ${project.build.directory}/conf
-
-
- src/main/resources
-
- logback.xml
-
- false
-
-
-
-
-
- copy-service-conf
- process-resources
-
- copy-resources
-
-
- ${project.build.directory}/conf
-
-
- src/main/conf
- true
-
-
-
- src/main/filters/unix.properties
-
-
-
-
- copy-win-conf
- process-resources
-
- copy-resources
-
-
- ${pkg.win.dist}/conf
-
-
- src/main/resources
-
- logback.xml
-
- false
-
-
- src/main/conf
-
- thingsboard.conf
-
- true
-
-
-
- src/main/filters/windows.properties
-
-
-
-
- copy-control
- process-resources
-
- copy-resources
-
-
- ${project.build.directory}/control
-
-
- src/main/scripts/control
- true
-
-
-
- src/main/filters/unix.properties
-
-
-
-
- copy-install
- process-resources
-
- copy-resources
-
-
- ${project.build.directory}/bin/install
-
-
- src/main/scripts/install
-
- **/*.sh
- **/*.xml
-
- true
-
-
-
- src/main/filters/unix.properties
-
-
-
-
- copy-windows-control
- process-resources
-
- copy-resources
-
-
- ${pkg.win.dist}
-
-
- src/main/scripts/windows
- true
-
-
-
- src/main/filters/windows.properties
-
-
-
-
- copy-windows-install
- process-resources
-
- copy-resources
-
-
- ${pkg.win.dist}/install
-
-
- src/main/scripts/install
-
- logback.xml
-
- true
-
-
-
- src/main/filters/windows.properties
-
-
-
-
- copy-data
- process-resources
-
- copy-resources
-
-
- ${project.build.directory}/data
-
-
- src/main/data
-
-
- ../dao/src/main/resources
-
- **/*.cql
- **/*.sql
-
- false
-
-
-
-
-
org.apache.maven.plugins
maven-dependency-plugin
-
-
- copy-winsw-service
- package
-
- copy
-
-
-
-
- com.sun.winsw
- winsw
- bin
- exe
- service.exe
-
-
- ${pkg.win.dist}
-
-
-
org.apache.maven.plugins
maven-jar-plugin
-
-
- **/logback.xml
-
-
-
- ThingsBoard
- ${project.version}
-
-
-
org.springframework.boot
spring-boot-maven-plugin
-
- org.thingsboard.server.ThingsboardServerApplication
- boot
- ZIP
- true
- true
-
- ${pkg.installFolder}/conf
- ${pkg.unixLogFolder}
- ${pkg.name}.out
- ${pkg.name}
-
-
-
-
-
- repackage
-
-
-
org.thingsboard
gradle-maven-plugin
-
-
- build
- buildDeb
- buildRpm
-
-
- -PprojectBuildDir=${project.build.directory}
- -PprojectVersion=${project.version}
- -PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}
-
- -PpkgName=${pkg.name}
- -PpkgInstallFolder=${pkg.installFolder}
- -PpkgLogFolder=${pkg.unixLogFolder}
-
-
-
-
- package
-
- invoke
-
-
-
org.apache.maven.plugins
maven-assembly-plugin
-
- ${pkg.name}
-
- src/main/assembly/windows.xml
-
-
-
-
- assembly
- package
-
- single
-
-
-
org.apache.maven.plugins
maven-install-plugin
-
- ${project.build.directory}/${pkg.name}.deb
- ${project.artifactId}
- ${project.groupId}
- ${project.version}
- deb
- deb
-
-
-
- install-deb
- package
-
- install-file
-
-
-
org.xolstice.maven.plugins
diff --git a/application/src/main/data/upgrade/2.4.3/schema_update_psql_ts.sql b/application/src/main/data/upgrade/2.4.3/schema_update_psql_ts.sql
index 671d39aae5..49823fb5af 100644
--- a/application/src/main/data/upgrade/2.4.3/schema_update_psql_ts.sql
+++ b/application/src/main/data/upgrade/2.4.3/schema_update_psql_ts.sql
@@ -16,50 +16,85 @@
-- call create_partition_ts_kv_table();
-CREATE OR REPLACE PROCEDURE create_partition_ts_kv_table() LANGUAGE plpgsql AS $$
+CREATE OR REPLACE PROCEDURE create_partition_ts_kv_table()
+ LANGUAGE plpgsql AS
+$$
BEGIN
- ALTER TABLE ts_kv
- RENAME TO ts_kv_old;
- ALTER TABLE ts_kv_old
- RENAME CONSTRAINT ts_kv_pkey TO ts_kv_pkey_old;
- CREATE TABLE IF NOT EXISTS ts_kv
- (
- LIKE ts_kv_old
- )
- PARTITION BY RANGE (ts);
- ALTER TABLE ts_kv
- DROP COLUMN entity_type;
- ALTER TABLE ts_kv
- ALTER COLUMN entity_id TYPE uuid USING entity_id::uuid;
- ALTER TABLE ts_kv
- ALTER COLUMN key TYPE integer USING key::integer;
- ALTER TABLE ts_kv
- ADD CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts);
+ ALTER TABLE ts_kv
+ DROP CONSTRAINT IF EXISTS ts_kv_unq_key;
+ ALTER TABLE ts_kv
+ DROP CONSTRAINT IF EXISTS ts_kv_pkey;
+ ALTER TABLE ts_kv
+ ADD CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_type, entity_id, key, ts);
+ ALTER TABLE ts_kv
+ RENAME TO ts_kv_old;
+ ALTER TABLE ts_kv_old
+ RENAME CONSTRAINT ts_kv_pkey TO ts_kv_pkey_old;
+ CREATE TABLE IF NOT EXISTS ts_kv
+ (
+ LIKE ts_kv_old
+ )
+ PARTITION BY RANGE (ts);
+ ALTER TABLE ts_kv
+ DROP COLUMN entity_type;
+ ALTER TABLE ts_kv
+ ALTER COLUMN entity_id TYPE uuid USING entity_id::uuid;
+ ALTER TABLE ts_kv
+ ALTER COLUMN key TYPE integer USING key::integer;
+ ALTER TABLE ts_kv
+ ADD CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts);
+ CREATE TABLE IF NOT EXISTS ts_kv_indefinite PARTITION OF ts_kv DEFAULT;
END;
$$;
-- call create_new_ts_kv_latest_table();
-CREATE OR REPLACE PROCEDURE create_new_ts_kv_latest_table() LANGUAGE plpgsql AS $$
+CREATE OR REPLACE PROCEDURE create_new_ts_kv_latest_table()
+ LANGUAGE plpgsql AS
+$$
BEGIN
- ALTER TABLE ts_kv_latest
- RENAME TO ts_kv_latest_old;
- ALTER TABLE ts_kv_latest_old
- RENAME CONSTRAINT ts_kv_latest_pkey TO ts_kv_latest_pkey_old;
- CREATE TABLE IF NOT EXISTS ts_kv_latest
- (
- LIKE ts_kv_latest_old
- );
- ALTER TABLE ts_kv_latest
- DROP COLUMN entity_type;
- ALTER TABLE ts_kv_latest
- ALTER COLUMN entity_id TYPE uuid USING entity_id::uuid;
- ALTER TABLE ts_kv_latest
- ALTER COLUMN key TYPE integer USING key::integer;
- ALTER TABLE ts_kv_latest
- ADD CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key);
+ IF NOT EXISTS(SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = 'ts_kv_latest_old') THEN
+ ALTER TABLE ts_kv_latest
+ DROP CONSTRAINT IF EXISTS ts_kv_latest_unq_key;
+ ALTER TABLE ts_kv_latest
+ DROP CONSTRAINT IF EXISTS ts_kv_latest_pkey;
+ ALTER TABLE ts_kv_latest
+ ADD CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_type, entity_id, key);
+ ALTER TABLE ts_kv_latest
+ RENAME TO ts_kv_latest_old;
+ ALTER TABLE ts_kv_latest_old
+ RENAME CONSTRAINT ts_kv_latest_pkey TO ts_kv_latest_pkey_old;
+ CREATE TABLE IF NOT EXISTS ts_kv_latest
+ (
+ LIKE ts_kv_latest_old
+ );
+ ALTER TABLE ts_kv_latest
+ DROP COLUMN entity_type;
+ ALTER TABLE ts_kv_latest
+ ALTER COLUMN entity_id TYPE uuid USING entity_id::uuid;
+ ALTER TABLE ts_kv_latest
+ ALTER COLUMN key TYPE integer USING key::integer;
+ ALTER TABLE ts_kv_latest
+ ADD CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key);
+ ELSE
+ RAISE NOTICE 'ts_kv_latest_old table already exists!';
+ IF NOT EXISTS(SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = 'ts_kv_latest') THEN
+ CREATE TABLE IF NOT EXISTS ts_kv_latest
+ (
+ entity_id uuid NOT NULL,
+ key int NOT NULL,
+ ts bigint NOT NULL,
+ bool_v boolean,
+ str_v varchar(10000000),
+ long_v bigint,
+ dbl_v double precision,
+ json_v json,
+ CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)
+ );
+ END IF;
+ END IF;
END;
$$;
@@ -93,8 +128,9 @@ BEGIN
RETURN QUERY SELECT SUBSTRING(year_date.year, 1, 4) AS partition_date,
(extract(epoch from (year_date.year)::timestamp) * 1000)::bigint AS from_ts,
(extract(epoch from (year_date.year::date + INTERVAL '1 YEAR')::timestamp) *
- 1000)::bigint AS to_ts
- FROM (SELECT DISTINCT TO_CHAR(TO_TIMESTAMP(ts / 1000), 'YYYY_01_01') AS year FROM ts_kv_old) AS year_date;
+ 1000)::bigint AS to_ts
+ FROM (SELECT DISTINCT TO_CHAR(TO_TIMESTAMP(ts / 1000), 'YYYY_01_01') AS year
+ FROM ts_kv_old) AS year_date;
ELSE
RAISE EXCEPTION 'Failed to parse partitioning property: % !', partition_type;
END CASE;
@@ -103,13 +139,16 @@ $$ LANGUAGE plpgsql;
-- call create_partitions();
-CREATE OR REPLACE PROCEDURE create_partitions(IN partition_type varchar) LANGUAGE plpgsql AS $$
+CREATE OR REPLACE PROCEDURE create_partitions(IN partition_type varchar)
+ LANGUAGE plpgsql AS
+$$
DECLARE
partition_date varchar;
from_ts bigint;
to_ts bigint;
- partitions_cursor CURSOR FOR SELECT * FROM get_partitions_data(partition_type);
+ partitions_cursor CURSOR FOR SELECT *
+ FROM get_partitions_data(partition_type);
BEGIN
OPEN partitions_cursor;
LOOP
@@ -127,21 +166,25 @@ $$;
-- call create_ts_kv_dictionary_table();
-CREATE OR REPLACE PROCEDURE create_ts_kv_dictionary_table() LANGUAGE plpgsql AS $$
+CREATE OR REPLACE PROCEDURE create_ts_kv_dictionary_table()
+ LANGUAGE plpgsql AS
+$$
BEGIN
- CREATE TABLE IF NOT EXISTS ts_kv_dictionary
- (
- key varchar(255) NOT NULL,
- key_id serial UNIQUE,
- CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
- );
+ CREATE TABLE IF NOT EXISTS ts_kv_dictionary
+ (
+ key varchar(255) NOT NULL,
+ key_id serial UNIQUE,
+ CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
+ );
END;
$$;
-- call insert_into_dictionary();
-CREATE OR REPLACE PROCEDURE insert_into_dictionary() LANGUAGE plpgsql AS $$
+CREATE OR REPLACE PROCEDURE insert_into_dictionary()
+ LANGUAGE plpgsql AS
+$$
DECLARE
insert_record RECORD;
@@ -164,31 +207,89 @@ BEGIN
END;
$$;
--- call insert_into_ts_kv();
+CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS
+$$
+BEGIN
+ uuid_id := substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) ||
+ '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12);
+END;
+$$ LANGUAGE plpgsql;
-CREATE OR REPLACE PROCEDURE insert_into_ts_kv() LANGUAGE plpgsql AS $$
-DECLARE
- insert_size CONSTANT integer := 10000;
- insert_counter integer DEFAULT 0;
- insert_record RECORD;
- insert_cursor CURSOR FOR SELECT CONCAT(entity_id_uuid_first_part, '-', entity_id_uuid_second_part, '-1', entity_id_uuid_third_part, '-', entity_id_uuid_fourth_part, '-', entity_id_uuid_fifth_part)::uuid AS entity_id,
+CREATE OR REPLACE PROCEDURE insert_into_ts_kv(IN path_to_file varchar)
+ LANGUAGE plpgsql AS
+$$
+BEGIN
+ EXECUTE format('COPY (SELECT to_uuid(entity_id) AS entity_id,
ts_kv_records.key AS key,
ts_kv_records.ts AS ts,
ts_kv_records.bool_v AS bool_v,
ts_kv_records.str_v AS str_v,
ts_kv_records.long_v AS long_v,
ts_kv_records.dbl_v AS dbl_v
- FROM (SELECT SUBSTRING(entity_id, 8, 8) AS entity_id_uuid_first_part,
- SUBSTRING(entity_id, 4, 4) AS entity_id_uuid_second_part,
- SUBSTRING(entity_id, 1, 3) AS entity_id_uuid_third_part,
- SUBSTRING(entity_id, 16, 4) AS entity_id_uuid_fourth_part,
- SUBSTRING(entity_id, 20) AS entity_id_uuid_fifth_part,
+ FROM (SELECT entity_id AS entity_id,
+ key_id AS key,
+ ts,
+ bool_v,
+ str_v,
+ long_v,
+ dbl_v
+ FROM ts_kv_old
+ INNER JOIN ts_kv_dictionary ON (ts_kv_old.key = ts_kv_dictionary.key)) AS ts_kv_records) TO %L;',
+ path_to_file);
+ EXECUTE format('COPY ts_kv FROM %L', path_to_file);
+END
+$$;
+
+-- call insert_into_ts_kv_latest();
+
+CREATE OR REPLACE PROCEDURE insert_into_ts_kv_latest(IN path_to_file varchar)
+ LANGUAGE plpgsql AS
+$$
+BEGIN
+ EXECUTE format('COPY (SELECT to_uuid(entity_id) AS entity_id,
+ ts_kv_latest_records.key AS key,
+ ts_kv_latest_records.ts AS ts,
+ ts_kv_latest_records.bool_v AS bool_v,
+ ts_kv_latest_records.str_v AS str_v,
+ ts_kv_latest_records.long_v AS long_v,
+ ts_kv_latest_records.dbl_v AS dbl_v
+ FROM (SELECT entity_id AS entity_id,
key_id AS key,
ts,
bool_v,
str_v,
long_v,
dbl_v
+ FROM ts_kv_latest_old
+ INNER JOIN ts_kv_dictionary ON (ts_kv_latest_old.key = ts_kv_dictionary.key)) AS ts_kv_latest_records) TO %L;',
+ path_to_file);
+ EXECUTE format('COPY ts_kv_latest FROM %L', path_to_file);
+END;
+$$;
+
+-- call insert_into_ts_kv_cursor();
+
+CREATE OR REPLACE PROCEDURE insert_into_ts_kv_cursor()
+ LANGUAGE plpgsql AS
+$$
+DECLARE
+ insert_size CONSTANT integer := 10000;
+ insert_counter integer DEFAULT 0;
+ insert_record RECORD;
+ insert_cursor CURSOR FOR SELECT to_uuid(entity_id) AS entity_id,
+ ts_kv_records.key AS key,
+ ts_kv_records.ts AS ts,
+ ts_kv_records.bool_v AS bool_v,
+ ts_kv_records.str_v AS str_v,
+ ts_kv_records.long_v AS long_v,
+ ts_kv_records.dbl_v AS dbl_v
+ FROM (SELECT entity_id AS entity_id,
+ key_id AS key,
+ ts,
+ bool_v,
+ str_v,
+ long_v,
+ dbl_v
FROM ts_kv_old
INNER JOIN ts_kv_dictionary ON (ts_kv_old.key = ts_kv_dictionary.key)) AS ts_kv_records;
BEGIN
@@ -211,26 +312,24 @@ BEGIN
END;
$$;
--- call insert_into_ts_kv_latest();
+-- call insert_into_ts_kv_latest_cursor();
-CREATE OR REPLACE PROCEDURE insert_into_ts_kv_latest() LANGUAGE plpgsql AS $$
+CREATE OR REPLACE PROCEDURE insert_into_ts_kv_latest_cursor()
+ LANGUAGE plpgsql AS
+$$
DECLARE
insert_size CONSTANT integer := 10000;
insert_counter integer DEFAULT 0;
insert_record RECORD;
- insert_cursor CURSOR FOR SELECT CONCAT(entity_id_uuid_first_part, '-', entity_id_uuid_second_part, '-1', entity_id_uuid_third_part, '-', entity_id_uuid_fourth_part, '-', entity_id_uuid_fifth_part)::uuid AS entity_id,
- ts_kv_latest_records.key AS key,
- ts_kv_latest_records.ts AS ts,
- ts_kv_latest_records.bool_v AS bool_v,
- ts_kv_latest_records.str_v AS str_v,
- ts_kv_latest_records.long_v AS long_v,
- ts_kv_latest_records.dbl_v AS dbl_v
- FROM (SELECT SUBSTRING(entity_id, 8, 8) AS entity_id_uuid_first_part,
- SUBSTRING(entity_id, 4, 4) AS entity_id_uuid_second_part,
- SUBSTRING(entity_id, 1, 3) AS entity_id_uuid_third_part,
- SUBSTRING(entity_id, 16, 4) AS entity_id_uuid_fourth_part,
- SUBSTRING(entity_id, 20) AS entity_id_uuid_fifth_part,
- key_id AS key,
+ insert_cursor CURSOR FOR SELECT to_uuid(entity_id) AS entity_id,
+ ts_kv_latest_records.key AS key,
+ ts_kv_latest_records.ts AS ts,
+ ts_kv_latest_records.bool_v AS bool_v,
+ ts_kv_latest_records.str_v AS str_v,
+ ts_kv_latest_records.long_v AS long_v,
+ ts_kv_latest_records.dbl_v AS dbl_v
+ FROM (SELECT entity_id AS entity_id,
+ key_id AS key,
ts,
bool_v,
str_v,
@@ -258,4 +357,3 @@ BEGIN
END;
$$;
-
diff --git a/application/src/main/data/upgrade/2.4.3/schema_update_timescale_ts.sql b/application/src/main/data/upgrade/2.4.3/schema_update_timescale_ts.sql
index 982ec9d85f..6124910efd 100644
--- a/application/src/main/data/upgrade/2.4.3/schema_update_timescale_ts.sql
+++ b/application/src/main/data/upgrade/2.4.3/schema_update_timescale_ts.sql
@@ -96,51 +96,36 @@ BEGIN
END;
$$;
--- call insert_into_ts_kv();
+CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS
+$$
+BEGIN
+ uuid_id := substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) ||
+ '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12);
+END;
+$$ LANGUAGE plpgsql;
-CREATE OR REPLACE PROCEDURE insert_into_ts_kv() LANGUAGE plpgsql AS $$
+-- call insert_into_ts_kv();
-DECLARE
- insert_size CONSTANT integer := 10000;
- insert_counter integer DEFAULT 0;
- insert_record RECORD;
- insert_cursor CURSOR FOR SELECT CONCAT(entity_id_uuid_first_part, '-', entity_id_uuid_second_part, '-1', entity_id_uuid_third_part, '-', entity_id_uuid_fourth_part, '-', entity_id_uuid_fifth_part)::uuid AS entity_id,
- new_ts_kv_records.key AS key,
- new_ts_kv_records.ts AS ts,
- new_ts_kv_records.bool_v AS bool_v,
- new_ts_kv_records.str_v AS str_v,
- new_ts_kv_records.long_v AS long_v,
- new_ts_kv_records.dbl_v AS dbl_v
- FROM (SELECT SUBSTRING(entity_id, 8, 8) AS entity_id_uuid_first_part,
- SUBSTRING(entity_id, 4, 4) AS entity_id_uuid_second_part,
- SUBSTRING(entity_id, 1, 3) AS entity_id_uuid_third_part,
- SUBSTRING(entity_id, 16, 4) AS entity_id_uuid_fourth_part,
- SUBSTRING(entity_id, 20) AS entity_id_uuid_fifth_part,
- key_id AS key,
- ts,
- bool_v,
- str_v,
- long_v,
- dbl_v
- FROM tenant_ts_kv_old
- INNER JOIN ts_kv_dictionary ON (tenant_ts_kv_old.key = ts_kv_dictionary.key)) AS new_ts_kv_records;
+CREATE OR REPLACE PROCEDURE insert_into_ts_kv(IN path_to_file varchar) LANGUAGE plpgsql AS $$
BEGIN
- OPEN insert_cursor;
- LOOP
- insert_counter := insert_counter + 1;
- FETCH insert_cursor INTO insert_record;
- IF NOT FOUND THEN
- RAISE NOTICE '% records have been inserted into the new ts_kv table!',insert_counter - 1;
- EXIT;
- END IF;
- INSERT INTO ts_kv(entity_id, key, ts, bool_v, str_v, long_v, dbl_v)
- VALUES (insert_record.entity_id, insert_record.key, insert_record.ts, insert_record.bool_v, insert_record.str_v,
- insert_record.long_v, insert_record.dbl_v);
- IF MOD(insert_counter, insert_size) = 0 THEN
- RAISE NOTICE '% records have been inserted into the new ts_kv table!',insert_counter;
- END IF;
- END LOOP;
- CLOSE insert_cursor;
+
+ EXECUTE format ('COPY (SELECT to_uuid(entity_id) AS entity_id,
+ new_ts_kv_records.key AS key,
+ new_ts_kv_records.ts AS ts,
+ new_ts_kv_records.bool_v AS bool_v,
+ new_ts_kv_records.str_v AS str_v,
+ new_ts_kv_records.long_v AS long_v,
+ new_ts_kv_records.dbl_v AS dbl_v
+ FROM (SELECT entity_id AS entity_id,
+ key_id AS key,
+ ts,
+ bool_v,
+ str_v,
+ long_v,
+ dbl_v
+ FROM tenant_ts_kv_old
+ INNER JOIN ts_kv_dictionary ON (tenant_ts_kv_old.key = ts_kv_dictionary.key)) AS new_ts_kv_records) TO %L;', path_to_file);
+ EXECUTE format ('COPY ts_kv FROM %L', path_to_file);
END;
$$;
@@ -177,3 +162,47 @@ BEGIN
CLOSE insert_cursor;
END;
$$;
+
+-- call insert_into_ts_kv_cursor();
+
+CREATE OR REPLACE PROCEDURE insert_into_ts_kv_cursor() LANGUAGE plpgsql AS $$
+
+DECLARE
+ insert_size CONSTANT integer := 10000;
+ insert_counter integer DEFAULT 0;
+ insert_record RECORD;
+ insert_cursor CURSOR FOR SELECT to_uuid(entity_id) AS entity_id,
+ new_ts_kv_records.key AS key,
+ new_ts_kv_records.ts AS ts,
+ new_ts_kv_records.bool_v AS bool_v,
+ new_ts_kv_records.str_v AS str_v,
+ new_ts_kv_records.long_v AS long_v,
+ new_ts_kv_records.dbl_v AS dbl_v
+ FROM (SELECT entity_id AS entity_id,
+ key_id AS key,
+ ts,
+ bool_v,
+ str_v,
+ long_v,
+ dbl_v
+ FROM tenant_ts_kv_old
+ INNER JOIN ts_kv_dictionary ON (tenant_ts_kv_old.key = ts_kv_dictionary.key)) AS new_ts_kv_records;
+BEGIN
+ OPEN insert_cursor;
+ LOOP
+ insert_counter := insert_counter + 1;
+ FETCH insert_cursor INTO insert_record;
+ IF NOT FOUND THEN
+ RAISE NOTICE '% records have been inserted into the new ts_kv table!',insert_counter - 1;
+ EXIT;
+ END IF;
+ INSERT INTO ts_kv(entity_id, key, ts, bool_v, str_v, long_v, dbl_v)
+ VALUES (insert_record.entity_id, insert_record.key, insert_record.ts, insert_record.bool_v, insert_record.str_v,
+ insert_record.long_v, insert_record.dbl_v);
+ IF MOD(insert_counter, insert_size) = 0 THEN
+ RAISE NOTICE '% records have been inserted into the new ts_kv table!',insert_counter;
+ END IF;
+ END LOOP;
+ CLOSE insert_cursor;
+END;
+$$;
\ No newline at end of file
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 9614b1a00a..cf26a5aff8 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
@@ -56,6 +56,7 @@ import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.cassandra.CassandraCluster;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
+import org.thingsboard.server.dao.device.ClaimDevicesService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entityview.EntityViewService;
@@ -219,6 +220,10 @@ public class ActorSystemContext {
@Getter
private MailService mailService;
+ @Autowired
+ @Getter
+ private ClaimDevicesService claimDevicesService;
+
//TODO: separate context for TbCore and TbRuleEngine
@Autowired(required = false)
@Getter
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
index 437ed456e9..d908258cc9 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
@@ -39,6 +39,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
+import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry;
import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
@@ -232,9 +233,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
if (msg.hasSubscriptionInfo()) {
handleSessionActivity(context, msg.getSessionInfo(), msg.getSubscriptionInfo());
}
+ if (msg.hasClaimDevice()) {
+ handleClaimDeviceMsg(context, msg.getSessionInfo(), msg.getClaimDevice());
+ }
callback.onSuccess();
}
+ private void handleClaimDeviceMsg(ActorContext context, SessionInfoProto sessionInfo, TransportProtos.ClaimDeviceMsg msg) {
+ DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
+ systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs());
+ }
+
private void reportSessionOpen() {
systemContext.getDeviceStateService().onDeviceConnect(deviceId);
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
index 1b0e4c3837..900a3a88c3 100644
--- a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
@@ -206,7 +206,7 @@ public class TenantActor extends RuleChainManagerActor {
if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) {
RuleChain ruleChain = systemContext.getRuleChainService().
findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId()));
- if (ruleChain.getType().equals(RuleChainType.SYSTEM)) {
+ if (ruleChain != null && ruleChain.getType().equals(RuleChainType.SYSTEM)) {
visit(ruleChain, target);
}
}
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 2132675f97..7553448c09 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
@@ -28,7 +28,6 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.alarm.Alarm;
-import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQuery;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
@@ -37,6 +36,7 @@ import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.page.TimePageData;
@@ -84,8 +84,9 @@ public class AlarmController extends BaseController {
public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException {
try {
alarm.setTenantId(getCurrentUser().getTenantId());
- Operation operation = alarm.getId() == null ? Operation.CREATE : Operation.WRITE;
- accessControlService.checkPermission(getCurrentUser(), Resource.ALARM, operation, alarm.getId(), alarm);
+
+ checkEntity(alarm.getId(), alarm, Resource.ALARM);
+
Alarm savedAlarm = checkNotNull(alarmService.createOrUpdateAlarm(alarm));
logEntityAction(savedAlarm.getId(), savedAlarm,
getCurrentUser().getCustomerId(),
diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java
index d50c25af74..dcd6bca3a2 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java
@@ -40,6 +40,8 @@ import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.page.TimePageData;
+import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent;
@@ -51,6 +53,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
+import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
+
@RestController
@TbCoreComponent
@RequestMapping("/api")
@@ -78,18 +82,15 @@ public class AssetController extends BaseController {
try {
asset.setTenantId(getCurrentUser().getTenantId());
- Operation operation = asset.getId() == null ? Operation.CREATE : Operation.WRITE;
-
- accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, operation,
- asset.getId(), asset);
+ checkEntity(asset.getId(), asset, Resource.ASSET);
- Asset savedAsset = checkNotNull(assetService.saveAsset(asset));
+ Asset savedAsset = checkNotNull(assetService.saveAsset(asset));
logEntityAction(savedAsset.getId(), savedAsset,
savedAsset.getCustomerId(),
asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
- return savedAsset;
+ return savedAsset;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ASSET), asset,
null, asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
@@ -140,7 +141,7 @@ public class AssetController extends BaseController {
savedAsset.getCustomerId(),
ActionType.ASSIGNED_TO_CUSTOMER, null, strAssetId, strCustomerId, customer.getName());
- return savedAsset;
+ return savedAsset;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ASSET), null,
@@ -220,7 +221,7 @@ public class AssetController extends BaseController {
try {
TenantId tenantId = getCurrentUser().getTenantId();
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
- if (type != null && type.trim().length()>0) {
+ if (type != null && type.trim().length() > 0) {
return checkNotNull(assetService.findAssetsByTenantIdAndType(tenantId, type, pageLink));
} else {
return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink));
@@ -259,7 +260,7 @@ public class AssetController extends BaseController {
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId, Operation.READ);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
- if (type != null && type.trim().length()>0) {
+ if (type != null && type.trim().length() > 0) {
return checkNotNull(assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
} else {
return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink));
@@ -336,9 +337,9 @@ public class AssetController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.POST)
@ResponseBody
- public Asset assignAssetToEdge(@PathVariable("edgeId") String strEdgeId,
+ public Asset assignAssetToEdge(@PathVariable(EDGE_ID) String strEdgeId,
@PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
- checkParameter("edgeId", strEdgeId);
+ checkParameter(EDGE_ID, strEdgeId);
checkParameter(ASSET_ID, strAssetId);
try {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
@@ -365,20 +366,20 @@ public class AssetController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
- @RequestMapping(value = "/edge/asset/{assetId}", method = RequestMethod.DELETE)
+ @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.DELETE)
@ResponseBody
- public Asset unassignAssetFromEdge(@PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
+ public Asset unassignAssetFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
+ @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
+ checkParameter(EDGE_ID, strEdgeId);
checkParameter(ASSET_ID, strAssetId);
try {
+ EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
+ Edge edge = checkEdgeId(edgeId, Operation.READ);
+
AssetId assetId = new AssetId(toUUID(strAssetId));
Asset asset = checkAssetId(assetId, Operation.UNASSIGN_FROM_EDGE);
- if (asset.getEdgeId() == null || asset.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) {
- throw new IncorrectParameterException("Asset isn't assigned to any edge!");
- }
-
- Edge edge = checkEdgeId(asset.getEdgeId(), Operation.READ);
- Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(getTenantId(), assetId));
+ Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(getTenantId(), assetId, edgeId));
logEntityAction(assetId, asset,
asset.getCustomerId(),
@@ -398,24 +399,20 @@ public class AssetController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/assets", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
- public TextPageData getEdgeAssets(
- @PathVariable("edgeId") String strEdgeId,
+ public TimePageData getEdgeAssets(
+ @PathVariable(EDGE_ID) String strEdgeId,
@RequestParam int limit,
- @RequestParam(required = false) String type,
- @RequestParam(required = false) String textSearch,
- @RequestParam(required = false) String idOffset,
- @RequestParam(required = false) String textOffset) throws ThingsboardException {
- checkParameter("edgeId", strEdgeId);
+ @RequestParam(required = false) Long startTime,
+ @RequestParam(required = false) Long endTime,
+ @RequestParam(required = false, defaultValue = "false") boolean ascOrder,
+ @RequestParam(required = false) String offset) throws ThingsboardException {
+ checkParameter(EDGE_ID, strEdgeId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
checkEdgeId(edgeId, Operation.READ);
- TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
- if (type != null && type.trim().length()>0) {
- return checkNotNull(assetService.findAssetsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink));
- } else {
- return checkNotNull(assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
- }
+ TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
+ return checkNotNull(assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get());
} catch (Exception e) {
throw handleException(e);
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
index 5bf1706dbd..e3f0c381d1 100644
--- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
@@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
@@ -107,7 +108,6 @@ import org.thingsboard.server.service.state.DeviceStateService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import javax.mail.MessagingException;
-import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Optional;
@@ -337,11 +337,23 @@ public abstract class BaseController {
}
}
+ protected void checkEntity(I entityId, T entity, Resource resource) throws ThingsboardException {
+ if (entityId == null) {
+ accessControlService
+ .checkPermission(getCurrentUser(), resource, Operation.CREATE, null, entity);
+ } else {
+ checkEntityId(entityId, Operation.WRITE);
+ }
+ }
+
protected void checkEntityId(EntityId entityId, Operation operation) throws ThingsboardException {
try {
checkNotNull(entityId);
validateId(entityId.getId(), "Incorrect entityId " + entityId);
switch (entityId.getEntityType()) {
+ case ALARM:
+ checkAlarmId(new AlarmId(entityId.getId()), operation);
+ return;
case DEVICE:
checkDeviceId(new DeviceId(entityId.getId()), operation);
return;
@@ -372,6 +384,12 @@ public abstract class BaseController {
case EDGE:
checkEdgeId(new EdgeId(entityId.getId()), operation);
return;
+ case WIDGETS_BUNDLE:
+ checkWidgetsBundleId(new WidgetsBundleId(entityId.getId()), operation);
+ return;
+ case WIDGET_TYPE:
+ checkWidgetTypeId(new WidgetTypeId(entityId.getId()), operation);
+ return;
default:
throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType());
}
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 18fcfc2ab5..75dee42ff8 100644
--- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
@@ -100,8 +100,7 @@ public class CustomerController extends BaseController {
try {
customer.setTenantId(getCurrentUser().getTenantId());
- Operation operation = customer.getId() == null ? Operation.CREATE : Operation.WRITE;
- accessControlService.checkPermission(getCurrentUser(), Resource.CUSTOMER, operation, customer.getId(), customer);
+ checkEntity(customer.getId(), customer, Resource.CUSTOMER);
Customer savedCustomer = checkNotNull(customerService.saveCustomer(customer));
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 eaa9c97ec8..5012e775ec 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
@@ -103,15 +103,12 @@ public class DashboardController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard", method = RequestMethod.POST)
- @ResponseBody
+ @ResponseBody
public Dashboard saveDashboard(@RequestBody Dashboard dashboard) throws ThingsboardException {
try {
dashboard.setTenantId(getCurrentUser().getTenantId());
- Operation operation = dashboard.getId() == null ? Operation.CREATE : Operation.WRITE;
-
- accessControlService.checkPermission(getCurrentUser(), Resource.DASHBOARD, operation,
- dashboard.getId(), dashboard);
+ checkEntity(dashboard.getId(), dashboard, Resource.DASHBOARD);
Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard));
@@ -155,9 +152,9 @@ public class DashboardController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/{customerId}/dashboard/{dashboardId}", method = RequestMethod.POST)
- @ResponseBody
+ @ResponseBody
public Dashboard assignDashboardToCustomer(@PathVariable("customerId") String strCustomerId,
- @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
+ @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
checkParameter(DASHBOARD_ID, strDashboardId);
try {
@@ -166,7 +163,7 @@ public class DashboardController extends BaseController {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
-
+
Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
logEntityAction(dashboardId, savedDashboard,
@@ -187,7 +184,7 @@ public class DashboardController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/{customerId}/dashboard/{dashboardId}", method = RequestMethod.DELETE)
- @ResponseBody
+ @ResponseBody
public Dashboard unassignDashboardFromCustomer(@PathVariable("customerId") String strCustomerId,
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
@@ -421,7 +418,7 @@ public class DashboardController extends BaseController {
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
- @RequestMapping(value = "/tenant/{tenantId}/dashboards", params = { "limit" }, method = RequestMethod.GET)
+ @RequestMapping(value = "/tenant/{tenantId}/dashboards", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TextPageData getTenantDashboards(
@PathVariable("tenantId") String strTenantId,
@@ -440,7 +437,7 @@ public class DashboardController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
- @RequestMapping(value = "/tenant/dashboards", params = { "limit" }, method = RequestMethod.GET)
+ @RequestMapping(value = "/tenant/dashboards", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TextPageData getTenantDashboards(
@RequestParam int limit,
@@ -457,7 +454,7 @@ public class DashboardController extends BaseController {
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
- @RequestMapping(value = "/customer/{customerId}/dashboards", params = { "limit" }, method = RequestMethod.GET)
+ @RequestMapping(value = "/customer/{customerId}/dashboards", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TimePageData getCustomerDashboards(
@PathVariable("customerId") String strCustomerId,
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 fc38153425..0218ebf47e 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
@@ -49,6 +49,9 @@ import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.page.TimePageData;
+import org.thingsboard.server.common.data.page.TimePageLink;
+import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.device.claim.ClaimResponse;
import org.thingsboard.server.dao.device.claim.ClaimResult;
@@ -96,10 +99,7 @@ public class DeviceController extends BaseController {
try {
device.setTenantId(getCurrentUser().getTenantId());
- Operation operation = device.getId() == null ? Operation.CREATE : Operation.WRITE;
-
- accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, operation,
- device.getId(), device);
+ checkEntity(device.getId(), device, Resource.DEVICE);
Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken));
@@ -519,19 +519,20 @@ public class DeviceController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
- @RequestMapping(value = "/edge/device/{deviceId}", method = RequestMethod.DELETE)
+ @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.DELETE)
@ResponseBody
- public Device unassignDeviceFromEdge(@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
+ public Device unassignDeviceFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
+ @PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
+ checkParameter(EDGE_ID, strEdgeId);
checkParameter(DEVICE_ID, strDeviceId);
try {
+ EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
+ Edge edge = checkEdgeId(edgeId, Operation.READ);
+
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId, Operation.UNASSIGN_FROM_EDGE);
- if (device.getEdgeId() == null || device.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) {
- throw new IncorrectParameterException("Device isn't assigned to any edge!");
- }
- Edge edge = checkEdgeId(device.getEdgeId(), Operation.READ);
- Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(getCurrentUser().getTenantId(), deviceId));
+ Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(getCurrentUser().getTenantId(), deviceId, edgeId));
logEntityAction(deviceId, device,
device.getCustomerId(),
@@ -549,24 +550,20 @@ public class DeviceController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/devices", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
- public TextPageData getEdgeDevices(
- @PathVariable("edgeId") String strEdgeId,
+ public TimePageData getEdgeDevices(
+ @PathVariable(EDGE_ID) String strEdgeId,
@RequestParam int limit,
- @RequestParam(required = false) String type,
- @RequestParam(required = false) String textSearch,
- @RequestParam(required = false) String idOffset,
- @RequestParam(required = false) String textOffset) throws ThingsboardException {
- checkParameter("edgeId", strEdgeId);
+ @RequestParam(required = false) Long startTime,
+ @RequestParam(required = false) Long endTime,
+ @RequestParam(required = false, defaultValue = "false") boolean ascOrder,
+ @RequestParam(required = false) String offset) throws ThingsboardException {
+ checkParameter(EDGE_ID, strEdgeId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
checkEdgeId(edgeId, Operation.READ);
- TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
- if (type != null && type.trim().length()>0) {
- return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink));
- } else {
- return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
- }
+ TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
+ return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get());
} catch (Exception e) {
throw handleException(e);
}
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 bb9106719d..d263497527 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
@@ -15,6 +15,7 @@
*/
package org.thingsboard.server.controller;
+import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -92,15 +93,16 @@ public class EdgeController extends BaseController {
accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation,
edge.getId(), edge);
- Edge result = checkNotNull(edgeService.saveEdge(edge));
+ Edge savedEdge = checkNotNull(edgeService.saveEdge(edge));
if (created) {
- ruleChainService.assignRuleChainToEdge(tenantId, defaultRootEdgeRuleChain.getId(), result.getId());
- edgeService.setEdgeRootRuleChain(tenantId, result, defaultRootEdgeRuleChain.getId());
+ ruleChainService.assignRuleChainToEdge(tenantId, defaultRootEdgeRuleChain.getId(), savedEdge.getId());
+ edgeService.setEdgeRootRuleChain(tenantId, savedEdge, defaultRootEdgeRuleChain.getId());
+ edgeService.assignDefaultRuleChainsToEdge(tenantId, savedEdge.getId());
}
- logEntityAction(result.getId(), result, null, created ? ActionType.ADDED : ActionType.UPDATED, null);
- return result;
+ logEntityAction(savedEdge.getId(), savedEdge, null, created ? ActionType.ADDED : ActionType.UPDATED, null);
+ return savedEdge;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.EDGE), edge,
null, edge.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
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 27dfb64189..3318ff8e79 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
@@ -48,6 +48,8 @@ import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.page.TimePageData;
+import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent;
@@ -95,10 +97,7 @@ public class EntityViewController extends BaseController {
try {
entityView.setTenantId(getCurrentUser().getTenantId());
- Operation operation = entityView.getId() == null ? Operation.CREATE : Operation.WRITE;
-
- accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, operation,
- entityView.getId(), entityView);
+ checkEntity(entityView.getId(), entityView, Resource.ENTITY_VIEW);
EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView));
List>> futures = new ArrayList<>();
@@ -400,18 +399,20 @@ public class EntityViewController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
- @RequestMapping(value = "/edge/entityView/{entityViewId}", method = RequestMethod.DELETE)
+ @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.DELETE)
@ResponseBody
- public EntityView unassignEntityViewFromEdge(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
+ public EntityView unassignEntityViewFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
+ @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
+ checkParameter(EDGE_ID, strEdgeId);
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
try {
+ EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
+ Edge edge = checkEdgeId(edgeId, Operation.READ);
+
EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
EntityView entityView = checkEntityViewId(entityViewId, Operation.UNASSIGN_FROM_EDGE);
- if (entityView.getEdgeId() == null || entityView.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) {
- throw new IncorrectParameterException("Entity View isn't assigned to any edge!");
- }
- Edge edge = checkEdgeId(entityView.getEdgeId(), Operation.READ);
- EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(getTenantId(), entityViewId));
+
+ EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(getTenantId(), entityViewId, edgeId));
logEntityAction(entityViewId, entityView,
entityView.getCustomerId(),
ActionType.UNASSIGNED_FROM_EDGE, null, strEntityViewId, edge.getId().toString(), edge.getName());
@@ -428,24 +429,20 @@ public class EntityViewController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/entityViews", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
- public TextPageData getEdgeEntityViews(
- @PathVariable("edgeId") String strEdgeId,
+ public TimePageData getEdgeEntityViews(
+ @PathVariable(EDGE_ID) String strEdgeId,
@RequestParam int limit,
- @RequestParam(required = false) String type,
- @RequestParam(required = false) String textSearch,
- @RequestParam(required = false) String idOffset,
- @RequestParam(required = false) String textOffset) throws ThingsboardException {
- checkParameter("edgeId", strEdgeId);
+ @RequestParam(required = false) Long startTime,
+ @RequestParam(required = false) Long endTime,
+ @RequestParam(required = false, defaultValue = "false") boolean ascOrder,
+ @RequestParam(required = false) String offset) throws ThingsboardException {
+ checkParameter(EDGE_ID, strEdgeId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
checkEdgeId(edgeId, Operation.READ);
- TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
- if (type != null && type.trim().length()>0) {
- return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink));
- } else {
- return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
- }
+ TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
+ return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get());
} catch (Exception e) {
throw handleException(e);
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/RpcController.java b/application/src/main/java/org/thingsboard/server/controller/RpcController.java
index b88c3f3695..7cba22b836 100644
--- a/application/src/main/java/org/thingsboard/server/controller/RpcController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/RpcController.java
@@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.FutureCallback;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -65,7 +66,6 @@ import java.util.UUID;
@Slf4j
public class RpcController extends BaseController {
- public static final int DEFAULT_TIMEOUT = 10000;
protected final ObjectMapper jsonMapper = new ObjectMapper();
@Autowired
@@ -74,6 +74,12 @@ public class RpcController extends BaseController {
@Autowired
private AccessValidator accessValidator;
+ @Value("${server.rest.server_side_rpc.min_timeout:5000}")
+ private long minTimeout;
+
+ @Value("${server.rest.server_side_rpc.default_timeout:10000}")
+ private long defaultTimeout;
+
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
@ResponseBody
@@ -100,7 +106,8 @@ public class RpcController extends BaseController {
SecurityUser currentUser = getCurrentUser();
TenantId tenantId = currentUser.getTenantId();
final DeferredResult response = new DeferredResult<>();
- long timeout = System.currentTimeMillis() + (cmd.getTimeout() != null ? cmd.getTimeout() : DEFAULT_TIMEOUT);
+ long timeout = cmd.getTimeout() != null ? cmd.getTimeout() : defaultTimeout;
+ long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout);
ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(cmd.getMethodName(), cmd.getRequestData());
accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback>() {
@Override
@@ -109,7 +116,7 @@ public class RpcController extends BaseController {
tenantId,
deviceId,
oneWay,
- timeout,
+ expTime,
body
);
deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse));
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 3d6b1f3349..604d3b62dd 100644
--- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
@@ -130,15 +130,14 @@ public class RuleChainController extends BaseController {
boolean created = ruleChain.getId() == null;
ruleChain.setTenantId(getCurrentUser().getTenantId());
- Operation operation = created ? Operation.CREATE : Operation.WRITE;
-
- accessControlService.checkPermission(getCurrentUser(), Resource.RULE_CHAIN, operation,
- ruleChain.getId(), ruleChain);
+ checkEntity(ruleChain.getId(), ruleChain, Resource.RULE_CHAIN);
RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain));
- tbClusterService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(),
- created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
+ if (RuleChainType.SYSTEM.equals(savedRuleChain.getType())) {
+ tbClusterService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(),
+ created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
+ }
logEntityAction(savedRuleChain.getId(), savedRuleChain,
null,
@@ -210,7 +209,9 @@ public class RuleChainController extends BaseController {
RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE);
RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData));
- tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
+ if (RuleChainType.SYSTEM.equals(ruleChain.getType())) {
+ tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
+ }
logEntityAction(ruleChain.getId(), ruleChain,
null,
@@ -266,10 +267,12 @@ public class RuleChainController extends BaseController {
referencingRuleChainIds.remove(ruleChain.getId());
- referencingRuleChainIds.forEach(referencingRuleChainId ->
- tbClusterService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
+ if (RuleChainType.SYSTEM.equals(ruleChain.getType())) {
+ referencingRuleChainIds.forEach(referencingRuleChainId ->
+ tbClusterService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
- tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
+ tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
+ }
logEntityAction(ruleChainId, ruleChain,
null,
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 9def943e88..ca87e08c96 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TenantController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TenantController.java
@@ -72,10 +72,8 @@ public class TenantController extends BaseController {
try {
boolean newTenant = tenant.getId() == null;
- Operation operation = newTenant ? Operation.CREATE : Operation.WRITE;
+ checkEntity(tenant.getId(), tenant, Resource.TENANT);
- accessControlService.checkPermission(getCurrentUser(), Resource.TENANT, operation,
- tenant.getId(), tenant);
tenant = checkNotNull(tenantService.saveTenant(tenant));
if (newTenant) {
installScripts.createDefaultRuleChains(tenant.getId());
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 bf64ad7d6f..b3d66adc53 100644
--- a/application/src/main/java/org/thingsboard/server/controller/UserController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java
@@ -132,17 +132,13 @@ public class UserController extends BaseController {
@ResponseBody
public User saveUser(@RequestBody User user,
@RequestParam(required = false, defaultValue = "true") boolean sendActivationMail,
- HttpServletRequest request) throws ThingsboardException {
+ HttpServletRequest request) throws ThingsboardException {
try {
-
if (getCurrentUser().getAuthority() == Authority.TENANT_ADMIN) {
user.setTenantId(getCurrentUser().getTenantId());
}
- Operation operation = user.getId() == null ? Operation.CREATE : Operation.WRITE;
-
- accessControlService.checkPermission(getCurrentUser(), Resource.USER, operation,
- user.getId(), user);
+ checkEntity(user.getId(), user, Resource.USER);
boolean sendEmail = user.getId() == null && sendActivationMail;
User savedUser = checkNotNull(userService.saveUser(user));
@@ -250,7 +246,7 @@ public class UserController extends BaseController {
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
- @RequestMapping(value = "/tenant/{tenantId}/users", params = { "limit" }, method = RequestMethod.GET)
+ @RequestMapping(value = "/tenant/{tenantId}/users", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TextPageData getTenantAdmins(
@PathVariable("tenantId") String strTenantId,
@@ -269,7 +265,7 @@ public class UserController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
- @RequestMapping(value = "/customer/{customerId}/users", params = { "limit" }, method = RequestMethod.GET)
+ @RequestMapping(value = "/customer/{customerId}/users", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TextPageData getCustomerUsers(
@PathVariable("customerId") String strCustomerId,
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 debe49b018..ffff90536f 100644
--- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
@@ -66,10 +66,7 @@ public class WidgetTypeController extends BaseController {
widgetType.setTenantId(getCurrentUser().getTenantId());
}
- Operation operation = widgetType.getId() == null ? Operation.CREATE : Operation.WRITE;
-
- accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, operation,
- widgetType.getId(), widgetType);
+ checkEntity(widgetType.getId(), widgetType, Resource.WIDGET_TYPE);
return checkNotNull(widgetTypeService.saveWidgetType(widgetType));
} catch (Exception e) {
@@ -92,7 +89,7 @@ public class WidgetTypeController extends BaseController {
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
- @RequestMapping(value = "/widgetTypes", params = { "isSystem", "bundleAlias"}, method = RequestMethod.GET)
+ @RequestMapping(value = "/widgetTypes", params = {"isSystem", "bundleAlias"}, method = RequestMethod.GET)
@ResponseBody
public List getBundleWidgetTypes(
@RequestParam boolean isSystem,
@@ -111,7 +108,7 @@ public class WidgetTypeController extends BaseController {
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
- @RequestMapping(value = "/widgetType", params = { "isSystem", "bundleAlias", "alias" }, method = RequestMethod.GET)
+ @RequestMapping(value = "/widgetType", params = {"isSystem", "bundleAlias", "alias"}, method = RequestMethod.GET)
@ResponseBody
public WidgetType getWidgetType(
@RequestParam boolean isSystem,
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 3d5cd22400..1b6b371297 100644
--- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
@@ -67,11 +67,7 @@ public class WidgetsBundleController extends BaseController {
widgetsBundle.setTenantId(getCurrentUser().getTenantId());
}
- Operation operation = widgetsBundle.getId() == null ? Operation.CREATE : Operation.WRITE;
-
- accessControlService.checkPermission(getCurrentUser(), Resource.WIDGETS_BUNDLE, operation,
- widgetsBundle.getId(), widgetsBundle);
-
+ checkEntity(widgetsBundle.getId(), widgetsBundle, Resource.WIDGETS_BUNDLE);
return checkNotNull(widgetsBundleService.saveWidgetsBundle(widgetsBundle));
} catch (Exception e) {
throw handleException(e);
@@ -93,7 +89,7 @@ public class WidgetsBundleController extends BaseController {
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
- @RequestMapping(value = "/widgetsBundles", params = { "limit" }, method = RequestMethod.GET)
+ @RequestMapping(value = "/widgetsBundles", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TextPageData getWidgetsBundles(
@RequestParam int limit,
diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
index 8dd45bc41b..e281c0958e 100644
--- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
+++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
@@ -133,13 +133,20 @@ public class ThingsboardInstallService {
databaseEntitiesUpgradeService.upgradeDatabase("2.4.2");
case "2.4.3":
- log.info("Upgrading ThingsBoard from version 2.4.3 to 2.5 ...");
+ log.info("Upgrading ThingsBoard from version 2.4.3 to 2.5.0 ...");
if (databaseTsUpgradeService != null) {
databaseTsUpgradeService.upgradeDatabase("2.4.3");
}
databaseEntitiesUpgradeService.upgradeDatabase("2.4.3");
+ case "2.5.0":
+ log.info("Upgrading ThingsBoard from version 2.5.0 to 2.5.1 ...");
+ if (databaseTsUpgradeService != null) {
+ databaseTsUpgradeService.upgradeDatabase("2.5.0");
+ }
+
+
log.info("Updating system data...");
systemDataLoaderService.deleteSystemWidgetBundle("charts");
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
index f701b6506b..c1a94d99d6 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
@@ -554,7 +554,7 @@ public final class EdgeGrpcSession implements Closeable {
case ENTITY_DELETED_RPC_MESSAGE:
Device device = ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), deviceName);
if (device != null) {
- ctx.getDeviceService().unassignDeviceFromEdge(edge.getTenantId(), device.getId());
+ ctx.getDeviceService().unassignDeviceFromEdge(edge.getTenantId(), device.getId(), edge.getId());
}
break;
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java
index 848b67be84..192fb8395b 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java
@@ -15,6 +15,7 @@
*/
package org.thingsboard.server.service.edge.rpc.init;
+import com.google.common.util.concurrent.Futures;
import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -54,6 +55,7 @@ import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgCo
import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor;
import java.util.UUID;
+import java.util.concurrent.Future;
@Service
@Slf4j
@@ -100,10 +102,10 @@ public class DefaultInitEdgeService implements InitEdgeService {
private void initDevices(Edge edge, StreamObserver outputStream) {
try {
- TextPageLink pageLink = new TextPageLink(100);
- TextPageData pageData;
+ TimePageLink pageLink = new TimePageLink(100);
+ TimePageData pageData;
do {
- pageData = deviceService.findDevicesByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink);
+ pageData = deviceService.findDevicesByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get();
if (!pageData.getData().isEmpty()) {
log.trace("[{}] [{}] device(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (Device device : pageData.getData()) {
@@ -130,10 +132,10 @@ public class DefaultInitEdgeService implements InitEdgeService {
private void initAssets(Edge edge, StreamObserver outputStream) {
try {
- TextPageLink pageLink = new TextPageLink(100);
- TextPageData pageData;
+ TimePageLink pageLink = new TimePageLink(100);
+ TimePageData pageData;
do {
- pageData = assetService.findAssetsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink);
+ pageData = assetService.findAssetsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get();
if (!pageData.getData().isEmpty()) {
log.trace("[{}] [{}] asset(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (Asset asset : pageData.getData()) {
@@ -160,10 +162,10 @@ public class DefaultInitEdgeService implements InitEdgeService {
private void initEntityViews(Edge edge, StreamObserver outputStream) {
try {
- TextPageLink pageLink = new TextPageLink(100);
- TextPageData pageData;
+ TimePageLink pageLink = new TimePageLink(100);
+ TimePageData pageData;
do {
- pageData = entityViewService.findEntityViewsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink);
+ pageData = entityViewService.findEntityViewsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get();
if (!pageData.getData().isEmpty()) {
log.trace("[{}] [{}] entity view(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (EntityView entityView : pageData.getData()) {
diff --git a/application/src/main/java/org/thingsboard/server/service/install/AbstractSqlTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/AbstractSqlTsDatabaseUpgradeService.java
index 7afa422460..5f084c7a9d 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/AbstractSqlTsDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/AbstractSqlTsDatabaseUpgradeService.java
@@ -34,6 +34,9 @@ public abstract class AbstractSqlTsDatabaseUpgradeService {
protected static final String CALL_REGEX = "call ";
protected static final String DROP_TABLE = "DROP TABLE ";
protected static final String DROP_PROCEDURE_IF_EXISTS = "DROP PROCEDURE IF EXISTS ";
+ protected static final String TS_KV_SQL = "ts_kv.sql";
+ protected static final String PATH_TO_USERS_PUBLIC_FOLDER = "C:\\Users\\Public";
+ protected static final String THINGSBOARD_WINDOWS_UPGRADE_DIR = "THINGSBOARD_WINDOWS_UPGRADE_DIR";
@Value("${spring.datasource.url}")
protected String dbUrl;
diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
index 5227efcfa3..29d698d0a4 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
@@ -311,18 +311,6 @@ public class CassandraDatabaseUpgradeService extends AbstractCassandraDatabaseUp
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.6.0", SCHEMA_UPDATE_CQL);
loadCql(schemaUpdateFile);
- try {
- cluster.getSession().execute("alter table asset add edge_id text");
- Thread.sleep(2500);
- } catch (InvalidQueryException e) {}
- try {
- cluster.getSession().execute("alter table device add edge_id text");
- Thread.sleep(2500);
- } catch (InvalidQueryException e) {}
- try {
- cluster.getSession().execute("alter table entity_view add edge_id text");
- Thread.sleep(2500);
- } catch (InvalidQueryException e) {}
try {
cluster.getSession().execute("alter table rule_chain add type text");
Thread.sleep(2500);
diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java
index 4bc68e92bc..103e8090d9 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java
@@ -48,6 +48,8 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase
}
log.info("Schema updated.");
break;
+ case "2.5.0":
+ break;
default:
throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
index 329dc37d3f..95eb966a1a 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
@@ -131,6 +131,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
node.put("username", "");
node.put("password", "");
node.put("tlsVersion", "TLSv1.2");//NOSONAR, key used to identify password field (not password value itself)
+ node.put("enableProxy", false);
mailSettings.setJsonValue(node);
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, mailSettings);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseSchemaService.java
index bcc3d9bb81..1f2d2f55a8 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseSchemaService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseSchemaService.java
@@ -37,8 +37,6 @@ public class PsqlTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaServic
@Override
public void createDatabaseSchema() throws Exception {
super.createDatabaseSchema();
- if (partitionType.equals("INDEFINITE")) {
- executeQuery("CREATE TABLE ts_kv_indefinite PARTITION OF ts_kv DEFAULT;");
- }
+ executeQuery("CREATE TABLE IF NOT EXISTS ts_kv_indefinite PARTITION OF ts_kv DEFAULT;");
}
}
\ No newline at end of file
diff --git a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java
index a06ef0fe05..7a8174af16 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java
@@ -16,12 +16,17 @@
package org.thingsboard.server.service.install;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.SystemUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.util.PsqlDao;
import org.thingsboard.server.dao.util.SqlTsDao;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
@@ -37,6 +42,7 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
@Value("${sql.postgres.ts_key_value_partitioning:MONTHS}")
private String partitionType;
+ private static final String TS_KV_LATEST_SQL = "ts_kv_latest.sql";
private static final String LOAD_FUNCTIONS_SQL = "schema_update_psql_ts.sql";
private static final String LOAD_TTL_FUNCTIONS_SQL = "schema_update_ttl.sql";
private static final String LOAD_DROP_PARTITIONS_FUNCTIONS_SQL = "schema_update_psql_drop_partitions.sql";
@@ -49,15 +55,17 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
private static final String CREATE_PARTITIONS = "create_partitions(IN partition_type varchar)";
private static final String CREATE_TS_KV_DICTIONARY_TABLE = "create_ts_kv_dictionary_table()";
private static final String INSERT_INTO_DICTIONARY = "insert_into_dictionary()";
- private static final String INSERT_INTO_TS_KV = "insert_into_ts_kv()";
- private static final String INSERT_INTO_TS_KV_LATEST = "insert_into_ts_kv_latest()";
+ private static final String INSERT_INTO_TS_KV = "insert_into_ts_kv(IN path_to_file varchar)";
+ private static final String INSERT_INTO_TS_KV_LATEST = "insert_into_ts_kv_latest(IN path_to_file varchar)";
+ private static final String INSERT_INTO_TS_KV_CURSOR = "insert_into_ts_kv_cursor()";
+ private static final String INSERT_INTO_TS_KV_LATEST_CURSOR = "insert_into_ts_kv_latest_cursor()";
private static final String CALL_CREATE_PARTITION_TS_KV_TABLE = CALL_REGEX + CREATE_PARTITION_TS_KV_TABLE;
private static final String CALL_CREATE_NEW_TS_KV_LATEST_TABLE = CALL_REGEX + CREATE_NEW_TS_KV_LATEST_TABLE;
private static final String CALL_CREATE_TS_KV_DICTIONARY_TABLE = CALL_REGEX + CREATE_TS_KV_DICTIONARY_TABLE;
private static final String CALL_INSERT_INTO_DICTIONARY = CALL_REGEX + INSERT_INTO_DICTIONARY;
- private static final String CALL_INSERT_INTO_TS_KV = CALL_REGEX + INSERT_INTO_TS_KV;
- private static final String CALL_INSERT_INTO_TS_KV_LATEST = CALL_REGEX + INSERT_INTO_TS_KV_LATEST;
+ private static final String CALL_INSERT_INTO_TS_KV_CURSOR = CALL_REGEX + INSERT_INTO_TS_KV_CURSOR;
+ private static final String CALL_INSERT_INTO_TS_KV_LATEST_CURSOR = CALL_REGEX + INSERT_INTO_TS_KV_LATEST_CURSOR;
private static final String DROP_TABLE_TS_KV_OLD = DROP_TABLE + TS_KV_OLD;
private static final String DROP_TABLE_TS_KV_LATEST_OLD = DROP_TABLE + TS_KV_LATEST_OLD;
@@ -69,6 +77,8 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
private static final String DROP_PROCEDURE_INSERT_INTO_DICTIONARY = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_DICTIONARY;
private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV;
private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV_LATEST;
+ private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV_CURSOR = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV_CURSOR;
+ private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST_CURSOR = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV_LATEST_CURSOR;
private static final String DROP_FUNCTION_GET_PARTITION_DATA = "DROP FUNCTION IF EXISTS get_partitions_data;";
@Override
@@ -89,14 +99,63 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
executeQuery(conn, CALL_CREATE_PARTITION_TS_KV_TABLE);
if (!partitionType.equals("INDEFINITE")) {
executeQuery(conn, "call create_partitions('" + partitionType + "')");
- } else {
- executeQuery(conn, "CREATE TABLE IF NOT EXISTS ts_kv_indefinite PARTITION OF ts_kv DEFAULT;");
}
executeQuery(conn, CALL_CREATE_TS_KV_DICTIONARY_TABLE);
executeQuery(conn, CALL_INSERT_INTO_DICTIONARY);
- executeQuery(conn, CALL_INSERT_INTO_TS_KV);
- executeQuery(conn, CALL_CREATE_NEW_TS_KV_LATEST_TABLE);
- executeQuery(conn, CALL_INSERT_INTO_TS_KV_LATEST);
+
+ Path pathToTempTsKvFile = null;
+ Path pathToTempTsKvLatestFile = null;
+ if (SystemUtils.IS_OS_WINDOWS) {
+ log.info("Lookup for environment variable: {} ...", THINGSBOARD_WINDOWS_UPGRADE_DIR);
+ Path pathToDir;
+ String thingsboardWindowsUpgradeDir = System.getenv("THINGSBOARD_WINDOWS_UPGRADE_DIR");
+ if (StringUtils.isNotEmpty(thingsboardWindowsUpgradeDir)) {
+ log.info("Environment variable: {} was found!", THINGSBOARD_WINDOWS_UPGRADE_DIR);
+ pathToDir = Paths.get(thingsboardWindowsUpgradeDir);
+ } else {
+ log.info("Failed to lookup environment variable: {}", THINGSBOARD_WINDOWS_UPGRADE_DIR);
+ pathToDir = Paths.get(PATH_TO_USERS_PUBLIC_FOLDER);
+ }
+ log.info("Directory: {} will be used for creation temporary upgrade files!", pathToDir);
+ try {
+ Path tsKvFile = Files.createTempFile(pathToDir, "ts_kv", ".sql");
+ Path tsKvLatestFile = Files.createTempFile(pathToDir, "ts_kv_latest", ".sql");
+ pathToTempTsKvFile = tsKvFile.toAbsolutePath();
+ pathToTempTsKvLatestFile = tsKvLatestFile.toAbsolutePath();
+ try {
+ copyTimeseries(conn, pathToTempTsKvFile, pathToTempTsKvLatestFile);
+ } catch (Exception e) {
+ insertTimeseries(conn);
+ }
+ } catch (IOException | SecurityException e) {
+ log.warn("Failed to create time-series upgrade files due to: {}", e.getMessage());
+ insertTimeseries(conn);
+ }
+ } else {
+ try {
+ Path tempDirPath = Files.createTempDirectory("ts_kv");
+ File tempDirAsFile = tempDirPath.toFile();
+ boolean writable = tempDirAsFile.setWritable(true, false);
+ boolean readable = tempDirAsFile.setReadable(true, false);
+ boolean executable = tempDirAsFile.setExecutable(true, false);
+ pathToTempTsKvFile = tempDirPath.resolve(TS_KV_SQL).toAbsolutePath();
+ pathToTempTsKvLatestFile = tempDirPath.resolve(TS_KV_LATEST_SQL).toAbsolutePath();
+ try {
+ if (writable && readable && executable) {
+ copyTimeseries(conn, pathToTempTsKvFile, pathToTempTsKvLatestFile);
+ } else {
+ throw new RuntimeException("Failed to grant write permissions for the: " + tempDirPath + "folder!");
+ }
+ } catch (Exception e) {
+ insertTimeseries(conn);
+ }
+ } catch (IOException | SecurityException e) {
+ log.warn("Failed to create time-series upgrade files due to: {}", e.getMessage());
+ insertTimeseries(conn);
+ }
+ }
+
+ removeUpgradeFiles(pathToTempTsKvFile, pathToTempTsKvLatestFile);
executeQuery(conn, DROP_TABLE_TS_KV_OLD);
executeQuery(conn, DROP_TABLE_TS_KV_LATEST_OLD);
@@ -108,6 +167,8 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV);
executeQuery(conn, DROP_PROCEDURE_CREATE_NEW_TS_KV_LATEST_TABLE);
executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST);
+ executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV_CURSOR);
+ executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST_CURSOR);
executeQuery(conn, DROP_FUNCTION_GET_PARTITION_DATA);
executeQuery(conn, "ALTER TABLE ts_kv ADD COLUMN IF NOT EXISTS json_v json;");
@@ -128,11 +189,46 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
}
}
break;
+ case "2.5.0":
+ try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
+ executeQuery(conn, "CREATE TABLE IF NOT EXISTS ts_kv_indefinite PARTITION OF ts_kv DEFAULT;");
+ executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001");
+ }
+ break;
default:
throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
}
}
+ private void removeUpgradeFiles(Path pathToTempTsKvFile, Path pathToTempTsKvLatestFile) {
+ if (pathToTempTsKvFile != null && pathToTempTsKvFile.toFile().exists()) {
+ boolean deleteTsKvFile = pathToTempTsKvFile.toFile().delete();
+ if (deleteTsKvFile) {
+ log.info("Successfully deleted the temp file for ts_kv table upgrade!");
+ }
+ }
+ if (pathToTempTsKvLatestFile != null && pathToTempTsKvLatestFile.toFile().exists()) {
+ boolean deleteTsKvLatestFile = pathToTempTsKvLatestFile.toFile().delete();
+ if (deleteTsKvLatestFile) {
+ log.info("Successfully deleted the temp file for ts_kv_latest table upgrade!");
+ }
+ }
+ }
+
+ private void copyTimeseries(Connection conn, Path pathToTempTsKvFile, Path pathToTempTsKvLatestFile) {
+ executeQuery(conn, "call insert_into_ts_kv('" + pathToTempTsKvFile + "')");
+ executeQuery(conn, CALL_CREATE_NEW_TS_KV_LATEST_TABLE);
+ executeQuery(conn, "call insert_into_ts_kv_latest('" + pathToTempTsKvLatestFile + "')");
+ }
+
+ private void insertTimeseries(Connection conn) {
+ log.warn("Upgrade script failed using the copy to/from files strategy!" +
+ " Trying to perfrom the upgrade using Inserts strategy ...");
+ executeQuery(conn, CALL_INSERT_INTO_TS_KV_CURSOR);
+ executeQuery(conn, CALL_CREATE_NEW_TS_KV_LATEST_TABLE);
+ executeQuery(conn, CALL_INSERT_INTO_TS_KV_LATEST_CURSOR);
+ }
+
@Override
protected void loadSql(Connection conn, String fileName) {
Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.4.3", fileName);
diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
index c9966d0cbb..f9c5e53fd5 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
@@ -238,15 +238,6 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
log.info("Updating schema ...");
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.6.0", SCHEMA_UPDATE_SQL);
loadSql(schemaUpdateFile, conn);
- try {
- conn.createStatement().execute("ALTER TABLE asset ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
- } catch (Exception e) {}
- try {
- conn.createStatement().execute("ALTER TABLE device ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
- } catch (Exception e) {}
- try {
- conn.createStatement().execute("ALTER TABLE entity_view ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
- } catch (Exception e) {}
try {
conn.createStatement().execute("ALTER TABLE rule_chain ADD type varchar(255) DEFAULT 'SYSTEM'"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
} catch (Exception e) {}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java
index 21bdb500d9..d8f7ea61f9 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.service.install;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.SystemUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
@@ -23,6 +25,9 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.util.PsqlDao;
import org.thingsboard.server.dao.util.TimescaleDBTsDao;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
@@ -47,15 +52,16 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
private static final String CREATE_NEW_TS_KV_TABLE = "create_new_ts_kv_table()";
private static final String CREATE_TS_KV_DICTIONARY_TABLE = "create_ts_kv_dictionary_table()";
private static final String INSERT_INTO_DICTIONARY = "insert_into_dictionary()";
- private static final String INSERT_INTO_TS_KV = "insert_into_ts_kv()";
+ private static final String INSERT_INTO_TS_KV = "insert_into_ts_kv(IN path_to_file varchar)";
+ private static final String INSERT_INTO_TS_KV_CURSOR = "insert_into_ts_kv_cursor()";
private static final String INSERT_INTO_TS_KV_LATEST = "insert_into_ts_kv_latest()";
private static final String CALL_CREATE_TS_KV_LATEST_TABLE = CALL_REGEX + CREATE_TS_KV_LATEST_TABLE;
private static final String CALL_CREATE_NEW_TENANT_TS_KV_TABLE = CALL_REGEX + CREATE_NEW_TS_KV_TABLE;
private static final String CALL_CREATE_TS_KV_DICTIONARY_TABLE = CALL_REGEX + CREATE_TS_KV_DICTIONARY_TABLE;
private static final String CALL_INSERT_INTO_DICTIONARY = CALL_REGEX + INSERT_INTO_DICTIONARY;
- private static final String CALL_INSERT_INTO_TS_KV = CALL_REGEX + INSERT_INTO_TS_KV;
private static final String CALL_INSERT_INTO_TS_KV_LATEST = CALL_REGEX + INSERT_INTO_TS_KV_LATEST;
+ private static final String CALL_INSERT_INTO_TS_KV_CURSOR = CALL_REGEX + INSERT_INTO_TS_KV_CURSOR;
private static final String DROP_OLD_TENANT_TS_KV_TABLE = DROP_TABLE + TENANT_TS_KV_OLD_TABLE;
@@ -63,7 +69,8 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
private static final String DROP_PROCEDURE_CREATE_TENANT_TS_KV_TABLE_COPY = DROP_PROCEDURE_IF_EXISTS + CREATE_NEW_TS_KV_TABLE;
private static final String DROP_PROCEDURE_CREATE_TS_KV_DICTIONARY_TABLE = DROP_PROCEDURE_IF_EXISTS + CREATE_TS_KV_DICTIONARY_TABLE;
private static final String DROP_PROCEDURE_INSERT_INTO_DICTIONARY = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_DICTIONARY;
- private static final String DROP_PROCEDURE_INSERT_INTO_TENANT_TS_KV = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV;
+ private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV;
+ private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV_CURSOR = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV_CURSOR;
private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV_LATEST;
@Autowired
@@ -91,7 +98,56 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
executeQuery(conn, CALL_CREATE_TS_KV_DICTIONARY_TABLE);
executeQuery(conn, CALL_INSERT_INTO_DICTIONARY);
- executeQuery(conn, CALL_INSERT_INTO_TS_KV);
+
+ Path pathToTempTsKvFile = null;
+ if (SystemUtils.IS_OS_WINDOWS) {
+ Path pathToDir;
+ log.info("Lookup for environment variable: {} ...", THINGSBOARD_WINDOWS_UPGRADE_DIR);
+ String thingsboardWindowsUpgradeDir = System.getenv(THINGSBOARD_WINDOWS_UPGRADE_DIR);
+ if (StringUtils.isNotEmpty(thingsboardWindowsUpgradeDir)) {
+ log.info("Environment variable: {} was found!", THINGSBOARD_WINDOWS_UPGRADE_DIR);
+ pathToDir = Paths.get(thingsboardWindowsUpgradeDir);
+ } else {
+ log.info("Failed to lookup environment variable: {}", THINGSBOARD_WINDOWS_UPGRADE_DIR);
+ pathToDir = Paths.get(PATH_TO_USERS_PUBLIC_FOLDER);
+ }
+ log.info("Directory: {} will be used for creation temporary upgrade file!", pathToDir);
+ try {
+ Path tsKvFile = Files.createTempFile(pathToDir, "ts_kv", ".sql");
+ pathToTempTsKvFile = tsKvFile.toAbsolutePath();
+ try {
+ executeQuery(conn, "call insert_into_ts_kv('" + pathToTempTsKvFile + "')");
+ } catch (Exception e) {
+ insertTimeseries(conn);
+ }
+ } catch (IOException | SecurityException e) {
+ log.warn("Failed to create time-series upgrade files due to: {}", e.getMessage());
+ insertTimeseries(conn);
+ }
+ } else {
+ try {
+ Path tempDirPath = Files.createTempDirectory("ts_kv");
+ File tempDirAsFile = tempDirPath.toFile();
+ boolean writable = tempDirAsFile.setWritable(true, false);
+ boolean readable = tempDirAsFile.setReadable(true, false);
+ boolean executable = tempDirAsFile.setExecutable(true, false);
+ pathToTempTsKvFile = tempDirPath.resolve(TS_KV_SQL).toAbsolutePath();
+ try {
+ if (writable && readable && executable) {
+ executeQuery(conn, "call insert_into_ts_kv('" + pathToTempTsKvFile + "')");
+ } else {
+ throw new RuntimeException("Failed to grant write permissions for the: " + tempDirPath + "folder!");
+ }
+ } catch (Exception e) {
+ insertTimeseries(conn);
+ }
+ } catch (IOException | SecurityException e) {
+ log.warn("Failed to create time-series upgrade files due to: {}", e.getMessage());
+ insertTimeseries(conn);
+ }
+ }
+ removeUpgradeFile(pathToTempTsKvFile);
+
executeQuery(conn, CALL_INSERT_INTO_TS_KV_LATEST);
executeQuery(conn, DROP_OLD_TENANT_TS_KV_TABLE);
@@ -100,7 +156,8 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
executeQuery(conn, DROP_PROCEDURE_CREATE_TENANT_TS_KV_TABLE_COPY);
executeQuery(conn, DROP_PROCEDURE_CREATE_TS_KV_DICTIONARY_TABLE);
executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_DICTIONARY);
- executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TENANT_TS_KV);
+ executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV);
+ executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV_CURSOR);
executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST);
executeQuery(conn, "ALTER TABLE ts_kv ADD COLUMN IF NOT EXISTS json_v json;");
@@ -115,11 +172,31 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
}
}
break;
+ case "2.5.0":
+ try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
+ executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001");
+ }
+ break;
default:
throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
}
}
+ private void insertTimeseries(Connection conn) {
+ log.warn("Upgrade script failed using the copy to/from files strategy!" +
+ " Trying to perfrom the upgrade using Inserts strategy ...");
+ executeQuery(conn, CALL_INSERT_INTO_TS_KV_CURSOR);
+ }
+
+ private void removeUpgradeFile(Path pathToTempTsKvFile) {
+ if (pathToTempTsKvFile != null && pathToTempTsKvFile.toFile().exists()) {
+ boolean deleteTsKvFile = pathToTempTsKvFile.toFile().delete();
+ if (deleteTsKvFile) {
+ log.info("Successfully deleted the temp file for ts_kv table upgrade!");
+ }
+ }
+ }
+
@Override
protected void loadSql(Connection conn, String fileName) {
Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.4.3", fileName);
diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
index 457d497e57..c951add112 100644
--- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
+++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
@@ -115,6 +115,21 @@ public class DefaultMailService implements MailService {
if (enableTls && jsonConfig.has("tlsVersion") && StringUtils.isNoneEmpty(jsonConfig.get("tlsVersion").asText())) {
javaMailProperties.put(MAIL_PROP + protocol + ".ssl.protocols", jsonConfig.get("tlsVersion").asText());
}
+
+ boolean enableProxy = jsonConfig.has("enableProxy") && jsonConfig.get("enableProxy").asBoolean();
+
+ if (enableProxy) {
+ javaMailProperties.put(MAIL_PROP + protocol + ".proxy.host", jsonConfig.get("proxyHost").asText());
+ javaMailProperties.put(MAIL_PROP + protocol + ".proxy.port", jsonConfig.get("proxyPort").asText());
+ String proxyUser = jsonConfig.get("proxyUser").asText();
+ if (StringUtils.isNoneEmpty(proxyUser)) {
+ javaMailProperties.put(MAIL_PROP + protocol + ".proxy.user", proxyUser);
+ }
+ String proxyPassword = jsonConfig.get("proxyPassword").asText();
+ if (StringUtils.isNoneEmpty(proxyPassword)) {
+ javaMailProperties.put(MAIL_PROP + protocol + ".proxy.password", proxyPassword);
+ }
+ }
return javaMailProperties;
}
diff --git a/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java
index 663cdc978d..cfb0c8a8ee 100644
--- a/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java
@@ -46,6 +46,9 @@ import java.util.concurrent.atomic.AtomicInteger;
@Service
public class RemoteJsInvokeService extends AbstractJsInvokeService {
+ @Value("${queue.js.max_eval_requests_timeout}")
+ private long maxEvalRequestsTimeout;
+
@Value("${queue.js.max_requests_timeout}")
private long maxRequestsTimeout;
@@ -59,22 +62,22 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
@Value("${js.remote.stats.enabled:false}")
private boolean statsEnabled;
- private final AtomicInteger kafkaPushedMsgs = new AtomicInteger(0);
- private final AtomicInteger kafkaInvokeMsgs = new AtomicInteger(0);
- private final AtomicInteger kafkaEvalMsgs = new AtomicInteger(0);
- private final AtomicInteger kafkaFailedMsgs = new AtomicInteger(0);
- private final AtomicInteger kafkaTimeoutMsgs = new AtomicInteger(0);
+ private final AtomicInteger queuePushedMsgs = new AtomicInteger(0);
+ private final AtomicInteger queueInvokeMsgs = new AtomicInteger(0);
+ private final AtomicInteger queueEvalMsgs = new AtomicInteger(0);
+ private final AtomicInteger queueFailedMsgs = new AtomicInteger(0);
+ private final AtomicInteger queueTimeoutMsgs = new AtomicInteger(0);
@Scheduled(fixedDelayString = "${js.remote.stats.print_interval_ms}")
public void printStats() {
if (statsEnabled) {
- int pushedMsgs = kafkaPushedMsgs.getAndSet(0);
- int invokeMsgs = kafkaInvokeMsgs.getAndSet(0);
- int evalMsgs = kafkaEvalMsgs.getAndSet(0);
- int failed = kafkaFailedMsgs.getAndSet(0);
- int timedOut = kafkaTimeoutMsgs.getAndSet(0);
+ int pushedMsgs = queuePushedMsgs.getAndSet(0);
+ int invokeMsgs = queueInvokeMsgs.getAndSet(0);
+ int evalMsgs = queueEvalMsgs.getAndSet(0);
+ int failed = queueFailedMsgs.getAndSet(0);
+ int timedOut = queueTimeoutMsgs.getAndSet(0);
if (pushedMsgs > 0 || invokeMsgs > 0 || evalMsgs > 0 || failed > 0 || timedOut > 0) {
- log.info("Kafka JS Invoke Stats: pushed [{}] received [{}] invoke [{}] eval [{}] failed [{}] timedOut [{}]",
+ log.info("Queue JS Invoke Stats: pushed [{}] received [{}] invoke [{}] eval [{}] failed [{}] timedOut [{}]",
pushedMsgs, invokeMsgs + evalMsgs, invokeMsgs, evalMsgs, failed, timedOut);
}
}
@@ -113,22 +116,22 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
log.trace("Post compile request for scriptId [{}]", scriptId);
ListenableFuture> future = requestTemplate.send(new TbProtoJsQueueMsg<>(UUID.randomUUID(), jsRequestWrapper));
- if (maxRequestsTimeout > 0) {
- future = Futures.withTimeout(future, maxRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService);
+ if (maxEvalRequestsTimeout > 0) {
+ future = Futures.withTimeout(future, maxEvalRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService);
}
- kafkaPushedMsgs.incrementAndGet();
+ queuePushedMsgs.incrementAndGet();
Futures.addCallback(future, new FutureCallback>() {
@Override
public void onSuccess(@Nullable TbProtoQueueMsg result) {
- kafkaEvalMsgs.incrementAndGet();
+ queueEvalMsgs.incrementAndGet();
}
@Override
public void onFailure(Throwable t) {
if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) {
- kafkaTimeoutMsgs.incrementAndGet();
+ queueTimeoutMsgs.incrementAndGet();
}
- kafkaFailedMsgs.incrementAndGet();
+ queueFailedMsgs.incrementAndGet();
}
}, MoreExecutors.directExecutor());
return Futures.transform(future, response -> {
@@ -170,20 +173,20 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
if (maxRequestsTimeout > 0) {
future = Futures.withTimeout(future, maxRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService);
}
- kafkaPushedMsgs.incrementAndGet();
+ queuePushedMsgs.incrementAndGet();
Futures.addCallback(future, new FutureCallback>() {
@Override
public void onSuccess(@Nullable TbProtoQueueMsg result) {
- kafkaInvokeMsgs.incrementAndGet();
+ queueInvokeMsgs.incrementAndGet();
}
@Override
public void onFailure(Throwable t) {
onScriptExecutionError(scriptId);
if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) {
- kafkaTimeoutMsgs.incrementAndGet();
+ queueTimeoutMsgs.incrementAndGet();
}
- kafkaFailedMsgs.incrementAndGet();
+ queueFailedMsgs.incrementAndGet();
}
}, MoreExecutors.directExecutor());
return Futures.transform(future, response -> {
diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java
index dd49418204..c51af005cc 100644
--- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java
+++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java
@@ -15,6 +15,9 @@
*/
package org.thingsboard.server.service.security.auth.oauth2;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -22,14 +25,21 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.DashboardId;
+import org.thingsboard.server.common.data.id.IdBased;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.page.TimePageData;
+import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.customer.CustomerService;
+import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.oauth2.OAuth2User;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService;
@@ -40,11 +50,15 @@ import org.thingsboard.server.service.security.model.UserPrincipal;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j
public abstract class AbstractOAuth2ClientMapper {
+ private static final int DASHBOARDS_REQUEST_LIMIT = 10;
+
+ private static final ObjectMapper objectMapper = new ObjectMapper();
@Autowired
private UserService userService;
@@ -58,6 +72,9 @@ public abstract class AbstractOAuth2ClientMapper {
@Autowired
private CustomerService customerService;
+ @Autowired
+ private DashboardService dashboardService;
+
@Autowired
private InstallScripts installScripts;
@@ -92,6 +109,20 @@ public abstract class AbstractOAuth2ClientMapper {
user.setEmail(oauth2User.getEmail());
user.setFirstName(oauth2User.getFirstName());
user.setLastName(oauth2User.getLastName());
+
+ if (!StringUtils.isEmpty(oauth2User.getDefaultDashboardName())) {
+ Optional dashboardIdOpt =
+ user.getAuthority() == Authority.TENANT_ADMIN ?
+ getDashboardId(tenantId, oauth2User.getDefaultDashboardName())
+ : getDashboardId(tenantId, customerId, oauth2User.getDefaultDashboardName());
+ if (dashboardIdOpt.isPresent()) {
+ ObjectNode additionalInfo = objectMapper.createObjectNode();
+ additionalInfo.put("defaultDashboardFullscreen", oauth2User.isAlwaysFullScreen());
+ additionalInfo.put("defaultDashboardId", dashboardIdOpt.get().getId().toString());
+ user.setAdditionalInfo(additionalInfo);
+ }
+ }
+
user = userService.saveUser(user);
if (activateUser) {
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());
@@ -143,4 +174,32 @@ public abstract class AbstractOAuth2ClientMapper {
return customerService.saveCustomer(customer).getId();
}
}
+
+ private Optional getDashboardId(TenantId tenantId, String dashboardName) {
+ TextPageLink searchTextLink = new TextPageLink(1, dashboardName);
+ TextPageData dashboardsPage = dashboardService.findDashboardsByTenantId(tenantId, searchTextLink);
+ return dashboardsPage.getData().stream()
+ .findAny()
+ .map(IdBased::getId);
+ }
+
+ private Optional getDashboardId(TenantId tenantId, CustomerId customerId, String dashboardName) {
+ TimePageData dashboardsPage = null;
+ do {
+ TimePageLink timePageLink = dashboardsPage != null ?
+ dashboardsPage.getNextPageLink() : new TimePageLink(DASHBOARDS_REQUEST_LIMIT);
+ try {
+ dashboardsPage = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, timePageLink).get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException("Failed to get customer's dashboards.", e);
+ }
+ Optional dashboardInfoOpt = dashboardsPage.getData().stream()
+ .filter(dashboardInfo -> dashboardName.equals(dashboardInfo.getName()))
+ .findAny();
+ if (dashboardInfoOpt.isPresent()) {
+ return dashboardInfoOpt.map(DashboardInfo::getId);
+ }
+ } while (dashboardsPage.hasNext());
+ return Optional.empty();
+ }
}
diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java
index c6b6aeaae3..2170479f92 100644
--- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java
+++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java
@@ -56,6 +56,10 @@ public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implemen
String customerName = sub.replace(config.getBasic().getCustomerNamePattern());
oauth2User.setCustomerName(customerName);
}
+ oauth2User.setAlwaysFullScreen(config.getBasic().isAlwaysFullScreen());
+ if (!StringUtils.isEmpty(config.getBasic().getDefaultDashboardName())) {
+ oauth2User.setDefaultDashboardName(config.getBasic().getDefaultDashboardName());
+ }
return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser());
}
diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java
index 0fb4563987..42ba95b4ee 100644
--- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java
+++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java
@@ -56,8 +56,8 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme
try {
return restTemplate.postForEntity(custom.getUrl(), request, OAuth2User.class).getBody();
} catch (Exception e) {
- log.error("Can't connect to custom mapper endpoint", e);
- throw new RuntimeException("Can't connect to custom mapper endpoint", e);
+ log.error("There was an error during connection to custom mapper endpoint", e);
+ throw new RuntimeException("Unable to login. Please contact your Administrator!");
}
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java
index 8702661196..3375d046e2 100644
--- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java
+++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java
@@ -32,6 +32,8 @@ import org.thingsboard.server.utils.MiscUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
@Component(value = "oauth2AuthenticationSuccessHandler")
@ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true")
@@ -57,16 +59,22 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException {
- OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
- OAuth2Client oauth2Client = oauth2Configuration.getClientByRegistrationId(token.getAuthorizedClientRegistrationId());
- OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(oauth2Client.getMapperConfig().getType());
- SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oauth2Client.getMapperConfig());
+ String baseUrl = MiscUtils.constructBaseUrl(request);
+ try {
+ OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
- JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
- JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
+ OAuth2Client oauth2Client = oauth2Configuration.getClientByRegistrationId(token.getAuthorizedClientRegistrationId());
+ OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(oauth2Client.getMapperConfig().getType());
+ SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oauth2Client.getMapperConfig());
- String baseUrl = MiscUtils.constructBaseUrl(request);
- getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken());
+ JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
+ JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
+
+ getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken());
+ } catch (Exception e) {
+ getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" +
+ URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.toString()));
+ }
}
}
\ No newline at end of file
diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java
index 4738ee44c5..0db9a7cf6f 100644
--- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java
+++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java
@@ -47,4 +47,13 @@ public enum Resource {
public Optional getEntityType() {
return Optional.ofNullable(entityType);
}
+
+ public static Resource of(EntityType entityType) {
+ for (Resource resource : Resource.values()) {
+ if (resource.getEntityType().get() == entityType) {
+ return resource;
+ }
+ }
+ throw new IllegalArgumentException("Unknown EntityType: " + entityType.name());
+ }
}
diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
index 77a932e10d..8acca0b4c7 100644
--- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
+++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
@@ -292,28 +292,38 @@ public class DefaultDeviceStateService implements DeviceStateService {
}
}
+ volatile Set pendingPartitions;
+
@Override
public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) {
synchronized (this) {
+ pendingPartitions = partitionChangeEvent.getPartitions();
if (!clusterUpdatePending) {
clusterUpdatePending = true;
queueExecutor.submit(() -> {
clusterUpdatePending = false;
- initStateFromDB(partitionChangeEvent.getPartitions());
+ initStateFromDB();
});
}
}
}
}
- private void initStateFromDB(Set partitions) {
+ private void initStateFromDB() {
try {
- Set addedPartitions = new HashSet<>(partitions);
+ log.info("CURRENT PARTITIONS: {}", partitionedDevices.keySet());
+ log.info("NEW PARTITIONS: {}", pendingPartitions);
+
+ Set addedPartitions = new HashSet<>(pendingPartitions);
addedPartitions.removeAll(partitionedDevices.keySet());
+ log.info("ADDED PARTITIONS: {}", addedPartitions);
+
Set removedPartitions = new HashSet<>(partitionedDevices.keySet());
- removedPartitions.removeAll(partitions);
+ removedPartitions.removeAll(pendingPartitions);
+
+ log.info("REMOVED PARTITIONS: {}", removedPartitions);
// We no longer manage current partition of devices;
removedPartitions.forEach(partition -> {
diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java
index 67c81ac06f..fc08ebfac1 100644
--- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java
@@ -224,7 +224,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer
return null;
}
},
- s -> (StringUtils.isEmpty(s.getScope()) || scope.equals(s.getScope().name())),
+ s -> (TbAttributeSubscriptionScope.ANY_SCOPE.equals(s.getScope()) || scope.equals(s.getScope().name())),
s -> {
List subscriptionUpdate = null;
for (AttributeKvEntry kv : attributes) {
diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAttributeSubscriptionScope.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAttributeSubscriptionScope.java
index d23cc3581d..70d6862b75 100644
--- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAttributeSubscriptionScope.java
+++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAttributeSubscriptionScope.java
@@ -17,6 +17,6 @@ package org.thingsboard.server.service.subscription;
public enum TbAttributeSubscriptionScope {
- CLIENT_SCOPE, SHARED_SCOPE, SERVER_SCOPE
+ ANY_SCOPE, CLIENT_SCOPE, SHARED_SCOPE, SERVER_SCOPE
}
diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java
index 6afcaf97bb..d6d9a6d070 100644
--- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java
+++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java
@@ -345,7 +345,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
keys.forEach(key -> subState.put(key, 0L));
attributesData.forEach(v -> subState.put(v.getKey(), v.getTs()));
- TbAttributeSubscriptionScope scope = StringUtils.isEmpty(cmd.getScope()) ? TbAttributeSubscriptionScope.SERVER_SCOPE : TbAttributeSubscriptionScope.valueOf(cmd.getScope());
+ TbAttributeSubscriptionScope scope = StringUtils.isEmpty(cmd.getScope()) ? TbAttributeSubscriptionScope.ANY_SCOPE : TbAttributeSubscriptionScope.valueOf(cmd.getScope());
TbAttributeSubscription sub = TbAttributeSubscription.builder()
.serviceId(serviceId)
@@ -442,7 +442,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
Map subState = new HashMap<>(attributesData.size());
attributesData.forEach(v -> subState.put(v.getKey(), v.getTs()));
- TbAttributeSubscriptionScope scope = StringUtils.isEmpty(cmd.getScope()) ? TbAttributeSubscriptionScope.SERVER_SCOPE : TbAttributeSubscriptionScope.valueOf(cmd.getScope());
+ TbAttributeSubscriptionScope scope = StringUtils.isEmpty(cmd.getScope()) ? TbAttributeSubscriptionScope.ANY_SCOPE : TbAttributeSubscriptionScope.valueOf(cmd.getScope());
TbAttributeSubscription sub = TbAttributeSubscription.builder()
.serviceId(serviceId)
diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
index 7456ca7662..b08ff7c20f 100644
--- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
+++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.transport;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
@@ -24,13 +25,18 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
+import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.Tenant;
+import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.TbMsgDataType;
+import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.relation.RelationService;
@@ -48,6 +54,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509Ce
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
+import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.state.DeviceStateService;
import java.util.UUID;
@@ -82,6 +89,9 @@ public class DefaultTransportApiService implements TransportApiService {
@Autowired
private DbCallbackExecutorService dbCallbackExecutorService;
+ @Autowired
+ protected TbClusterService tbClusterService;
+
private ReentrantLock deviceCreationLock = new ReentrantLock();
@Override
@@ -119,14 +129,27 @@ public class DefaultTransportApiService implements TransportApiService {
try {
Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), requestMsg.getDeviceName());
if (device == null) {
+ TenantId tenantId = gateway.getTenantId();
device = new Device();
- device.setTenantId(gateway.getTenantId());
+ device.setTenantId(tenantId);
device.setName(requestMsg.getDeviceName());
device.setType(requestMsg.getDeviceType());
device.setCustomerId(gateway.getCustomerId());
device = deviceService.saveDevice(device);
relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created"));
deviceStateService.onDeviceAdded(device);
+
+ TbMsgMetaData metaData = new TbMsgMetaData();
+ CustomerId customerId = gateway.getCustomerId();
+ if (customerId != null && !customerId.isNullUid()) {
+ metaData.putValue("customerId", customerId.toString());
+ }
+ metaData.putValue("gatewayId", gatewayId.toString());
+
+ DeviceId deviceId = device.getId();
+ ObjectNode entityNode = mapper.valueToTree(device);
+ TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, metaData, TbMsgDataType.JSON, mapper.writeValueAsString(entityNode));
+ tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, null);
}
return TransportApiResponseMsg.newBuilder()
.setGetOrCreateDeviceResponseMsg(GetOrCreateDeviceFromGatewayResponseMsg.newBuilder().setDeviceInfo(getDeviceInfoProto(device)).build()).build();
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index 428ef7aa65..a974de3cd6 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -54,6 +54,13 @@ server:
customer:
enabled: "${TB_SERVER_REST_LIMITS_CUSTOMER_ENABLED:false}"
configuration: "${TB_SERVER_REST_LIMITS_CUSTOMER_CONFIGURATION:50:1,1000:60}"
+ server_side_rpc:
+ # Minimum value of the server side RPC timeout. May override value provided in the REST API call.
+ # Since 2.5 migration to queues, the RPC delay depends on the size of the pending messages in the queue,
+ # so default UI parameter of 500ms may not be sufficient for loaded environments.
+ min_timeout: "${MIN_SERVER_SIDE_RPC_TIMEOUT:5000}"
+ # Default value of the server side RPC timeout.
+ default_timeout: "${DEFAULT_SERVER_SIDE_RPC_TIMEOUT:10000}"
# Zookeeper connection parameters. Used for service discovery.
zk:
@@ -148,6 +155,10 @@ security:
# If this field is not empty, user will be created as a user under defined Customer
# %{attribute_key} as placeholder for attribute value of attributes of external user object
customerNamePattern: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_CUSTOMER_NAME_PATTERN:}"
+ # If this field is not empty, user will be created with default defined Dashboard
+ defaultDashboardName: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_DEFAULT_DASHBOARD_NAME:}"
+ # If this field is set 'true' along with non-empty 'defaultDashboardName', user will start from the defined Dashboard in fullscreen mode
+ alwaysFullScreen: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_ALWAYS_FULL_SCREEN:false}"
custom:
url: "${SECURITY_OAUTH2_DEFAULT_MAPPER_CUSTOM_URL:}"
username: "${SECURITY_OAUTH2_DEFAULT_MAPPER_CUSTOM_USERNAME:}"
@@ -676,7 +687,7 @@ queue:
topic: "${TB_QUEUE_CORE_TOPIC:tb_core}"
poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
- pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}"
+ pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:2000}"
stats:
enabled: "${TB_QUEUE_CORE_STATS_ENABLED:true}"
print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:60000}"
@@ -688,6 +699,8 @@ queue:
# JS Eval max pending requests
max_pending_requests: "${REMOTE_JS_MAX_PENDING_REQUESTS:10000}"
# JS Eval max request timeout
+ max_eval_requests_timeout: "${REMOTE_JS_MAX_EVAL_REQUEST_TIMEOUT:60000}"
+ # JS max request timeout
max_requests_timeout: "${REMOTE_JS_MAX_REQUEST_TIMEOUT:10000}"
# JS response poll interval
response_poll_interval: "${REMOTE_JS_RESPONSE_POLL_INTERVAL_MS:25}"
@@ -696,7 +709,7 @@ queue:
rule-engine:
topic: "${TB_QUEUE_RULE_ENGINE_TOPIC:tb_rule_engine}"
poll-interval: "${TB_QUEUE_RULE_ENGINE_POLL_INTERVAL_MS:25}"
- pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:60000}"
+ pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:2000}"
stats:
enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}"
print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:60000}"
@@ -705,7 +718,7 @@ queue:
topic: "${TB_QUEUE_RE_MAIN_TOPIC:tb_rule_engine.main}"
poll-interval: "${TB_QUEUE_RE_MAIN_POLL_INTERVAL_MS:25}"
partitions: "${TB_QUEUE_RE_MAIN_PARTITIONS:10}"
- pack-processing-timeout: "${TB_QUEUE_RE_MAIN_PACK_PROCESSING_TIMEOUT_MS:60000}"
+ pack-processing-timeout: "${TB_QUEUE_RE_MAIN_PACK_PROCESSING_TIMEOUT_MS:2000}"
submit-strategy:
type: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
# For BATCH only
@@ -720,7 +733,7 @@ queue:
topic: "${TB_QUEUE_RE_HP_TOPIC:tb_rule_engine.hp}"
poll-interval: "${TB_QUEUE_RE_HP_POLL_INTERVAL_MS:25}"
partitions: "${TB_QUEUE_RE_HP_PARTITIONS:10}"
- pack-processing-timeout: "${TB_QUEUE_RE_HP_PACK_PROCESSING_TIMEOUT_MS:60000}"
+ pack-processing-timeout: "${TB_QUEUE_RE_HP_PACK_PROCESSING_TIMEOUT_MS:2000}"
submit-strategy:
type: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
# For BATCH only
@@ -735,7 +748,7 @@ queue:
topic: "${TB_QUEUE_RE_SQ_TOPIC:tb_rule_engine.sq}"
poll-interval: "${TB_QUEUE_RE_SQ_POLL_INTERVAL_MS:25}"
partitions: "${TB_QUEUE_RE_SQ_PARTITIONS:10}"
- pack-processing-timeout: "${TB_QUEUE_RE_SQ_PACK_PROCESSING_TIMEOUT_MS:60000}"
+ pack-processing-timeout: "${TB_QUEUE_RE_SQ_PACK_PROCESSING_TIMEOUT_MS:2000}"
submit-strategy:
type: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_TYPE:SEQUENTIAL_BY_ORIGINATOR}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
# For BATCH only
diff --git a/application/src/main/scripts/control/rpm/postinst b/application/src/main/scripts/control/rpm/postinst
deleted file mode 100644
index d8021e2dd9..0000000000
--- a/application/src/main/scripts/control/rpm/postinst
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-chown -R ${pkg.user}: ${pkg.logFolder}
-chown -R ${pkg.user}: ${pkg.installFolder}
-
-if [ $1 -eq 1 ] ; then
- # Initial installation
- systemctl --no-reload enable ${pkg.name}.service >/dev/null 2>&1 || :
-fi
diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
index 26f375c464..5462bb12bd 100644
--- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
@@ -33,7 +33,6 @@ import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
-import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootContextLoader;
import org.springframework.boot.test.context.SpringBootTest;
@@ -49,7 +48,6 @@ import org.springframework.mock.http.MockHttpOutputMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
@@ -75,7 +73,6 @@ import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRequest;
import org.thingsboard.server.service.security.auth.rest.LoginRequest;
import java.io.IOException;
-import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
@@ -223,6 +220,27 @@ public abstract class AbstractControllerTest {
login(CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD);
}
+ private Tenant savedDifferentTenant;
+ protected void loginDifferentTenant() throws Exception {
+ loginSysAdmin();
+ Tenant tenant = new Tenant();
+ tenant.setTitle("Different tenant");
+ savedDifferentTenant = doPost("/api/tenant", tenant, Tenant.class);
+ Assert.assertNotNull(savedDifferentTenant);
+ User differentTenantAdmin = new User();
+ differentTenantAdmin.setAuthority(Authority.TENANT_ADMIN);
+ differentTenantAdmin.setTenantId(savedDifferentTenant.getId());
+ differentTenantAdmin.setEmail("different_tenant@thingsboard.org");
+
+ createUserAndLogin(differentTenantAdmin, "testPassword");
+ }
+
+ protected void deleteDifferentTenant() throws Exception {
+ loginSysAdmin();
+ doDelete("/api/tenant/" + savedDifferentTenant.getId().getId().toString())
+ .andExpect(status().isOk());
+ }
+
protected User createUserAndLogin(User user, String password) throws Exception {
User savedUser = doPost("/api/user", user, User.class);
logout();
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
index 0422a85416..24dbcca2db 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
@@ -98,6 +98,18 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
Assert.assertEquals(foundAsset.getName(), savedAsset.getName());
}
+ @Test
+ public void testUpdateAssetFromDifferentTenant() throws Exception {
+ Asset asset = new Asset();
+ asset.setName("My asset");
+ asset.setType("default");
+ Asset savedAsset = doPost("/api/asset", asset, Asset.class);
+
+ loginDifferentTenant();
+ doPost("/api/asset", savedAsset, Asset.class, status().isForbidden());
+ deleteDifferentTenant();
+ }
+
@Test
public void testFindAssetById() throws Exception {
Asset asset = new Asset();
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java
index f5ea544560..558600c73e 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java
@@ -23,6 +23,8 @@ import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.RandomStringUtils;
+import org.junit.After;
+import org.junit.Before;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
@@ -38,25 +40,39 @@ import com.fasterxml.jackson.core.type.TypeReference;
public abstract class BaseCustomerControllerTest extends AbstractControllerTest {
private IdComparator idComparator = new IdComparator<>();
-
- @Test
- public void testSaveCustomer() throws Exception {
+
+ private Tenant savedTenant;
+ private User tenantAdmin;
+
+ @Before
+ public void beforeTest() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
- Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
+ savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
-
- User tenantAdmin = new User();
+
+ tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin.setTenantId(savedTenant.getId());
tenantAdmin.setEmail("tenant2@thingsboard.org");
tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs");
-
+
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
-
+ }
+
+ @After
+ public void afterTest() throws Exception {
+ loginSysAdmin();
+
+ doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
+ .andExpect(status().isOk());
+ }
+
+ @Test
+ public void testSaveCustomer() throws Exception {
Customer customer = new Customer();
customer.setTitle("My customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
@@ -66,266 +82,159 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
Assert.assertEquals(customer.getTitle(), savedCustomer.getTitle());
savedCustomer.setTitle("My new customer");
doPost("/api/customer", savedCustomer, Customer.class);
-
- Customer foundCustomer = doGet("/api/customer/"+savedCustomer.getId().getId().toString(), Customer.class);
+
+ Customer foundCustomer = doGet("/api/customer/"+savedCustomer.getId().getId().toString(), Customer.class);
Assert.assertEquals(foundCustomer.getTitle(), savedCustomer.getTitle());
-
+
doDelete("/api/customer/"+savedCustomer.getId().getId().toString())
.andExpect(status().isOk());
-
- loginSysAdmin();
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
}
-
+
@Test
- public void testFindCustomerById() throws Exception {
-
- loginSysAdmin();
+ public void testUpdateCustomerFromDifferentTenant() throws Exception {
+ Customer customer = new Customer();
+ customer.setTitle("My customer");
+ Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
+ doPost("/api/customer", savedCustomer, Customer.class);
- Tenant tenant = new Tenant();
- tenant.setTitle("My tenant");
- Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
- Assert.assertNotNull(savedTenant);
-
- User tenantAdmin = new User();
- tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
- tenantAdmin.setTenantId(savedTenant.getId());
- tenantAdmin.setEmail("tenant2@thingsboard.org");
- tenantAdmin.setFirstName("Joe");
- tenantAdmin.setLastName("Downs");
-
- tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
-
+ loginDifferentTenant();
+ doPost("/api/customer", savedCustomer, Customer.class, status().isForbidden());
+ deleteDifferentTenant();
+
+ login(tenantAdmin.getName(), "testPassword1");
+ doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
+ .andExpect(status().isOk());
+ }
+
+ @Test
+ public void testFindCustomerById() throws Exception {
Customer customer = new Customer();
customer.setTitle("My customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
-
- Customer foundCustomer = doGet("/api/customer/"+savedCustomer.getId().getId().toString(), Customer.class);
+
+ Customer foundCustomer = doGet("/api/customer/" + savedCustomer.getId().getId().toString(), Customer.class);
Assert.assertNotNull(foundCustomer);
Assert.assertEquals(savedCustomer, foundCustomer);
-
- doDelete("/api/customer/"+savedCustomer.getId().getId().toString())
- .andExpect(status().isOk());
-
- loginSysAdmin();
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
+
+ doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
@Test
public void testDeleteCustomer() throws Exception {
-
- loginSysAdmin();
-
- Tenant tenant = new Tenant();
- tenant.setTitle("My tenant");
- Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
- Assert.assertNotNull(savedTenant);
-
- User tenantAdmin = new User();
- tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
- tenantAdmin.setTenantId(savedTenant.getId());
- tenantAdmin.setEmail("tenant2@thingsboard.org");
- tenantAdmin.setFirstName("Joe");
- tenantAdmin.setLastName("Downs");
-
- tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
-
Customer customer = new Customer();
customer.setTitle("My customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
-
- doDelete("/api/customer/"+savedCustomer.getId().getId().toString())
- .andExpect(status().isOk());
- doGet("/api/customer/"+savedCustomer.getId().getId().toString())
- .andExpect(status().isNotFound());
-
- loginSysAdmin();
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
+ doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
+ .andExpect(status().isOk());
+
+ doGet("/api/customer/" + savedCustomer.getId().getId().toString())
+ .andExpect(status().isNotFound());
}
-
+
@Test
public void testSaveCustomerWithEmptyTitle() throws Exception {
-
- loginSysAdmin();
-
- Tenant tenant = new Tenant();
- tenant.setTitle("My tenant");
- Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
- Assert.assertNotNull(savedTenant);
-
- User tenantAdmin = new User();
- tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
- tenantAdmin.setTenantId(savedTenant.getId());
- tenantAdmin.setEmail("tenant2@thingsboard.org");
- tenantAdmin.setFirstName("Joe");
- tenantAdmin.setLastName("Downs");
-
- tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
-
Customer customer = new Customer();
doPost("/api/customer", customer)
- .andExpect(status().isBadRequest())
- .andExpect(statusReason(containsString("Customer title should be specified")));
-
- loginSysAdmin();
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
+ .andExpect(status().isBadRequest())
+ .andExpect(statusReason(containsString("Customer title should be specified")));
}
-
+
@Test
public void testSaveCustomerWithInvalidEmail() throws Exception {
-
- loginSysAdmin();
-
- Tenant tenant = new Tenant();
- tenant.setTitle("My tenant");
- Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
- Assert.assertNotNull(savedTenant);
-
- User tenantAdmin = new User();
- tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
- tenantAdmin.setTenantId(savedTenant.getId());
- tenantAdmin.setEmail("tenant2@thingsboard.org");
- tenantAdmin.setFirstName("Joe");
- tenantAdmin.setLastName("Downs");
-
- tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
-
Customer customer = new Customer();
customer.setTitle("My customer");
customer.setEmail("invalid@mail");
doPost("/api/customer", customer)
- .andExpect(status().isBadRequest())
- .andExpect(statusReason(containsString("Invalid email address format 'invalid@mail'")));
-
- loginSysAdmin();
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
+ .andExpect(status().isBadRequest())
+ .andExpect(statusReason(containsString("Invalid email address format 'invalid@mail'")));
+
+// loginSysAdmin();
+//
+// doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
+// .andExpect(status().isOk());
}
-
+
@Test
public void testFindCustomers() throws Exception {
- loginSysAdmin();
-
- Tenant tenant = new Tenant();
- tenant.setTitle("My tenant");
- Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
- Assert.assertNotNull(savedTenant);
-
TenantId tenantId = savedTenant.getId();
-
- User tenantAdmin = new User();
- tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
- tenantAdmin.setTenantId(tenantId);
- tenantAdmin.setEmail("tenant2@thingsboard.org");
- tenantAdmin.setFirstName("Joe");
- tenantAdmin.setLastName("Downs");
-
- tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
-
+
List customers = new ArrayList<>();
- for (int i=0;i<135;i++) {
+ for (int i = 0; i < 135; i++) {
Customer customer = new Customer();
customer.setTenantId(tenantId);
- customer.setTitle("Customer"+i);
+ customer.setTitle("Customer" + i);
customers.add(doPost("/api/customer", customer, Customer.class));
}
-
+
List loadedCustomers = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(23);
TextPageData pageData = null;
do {
- pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>() {
+ }, pageLink);
loadedCustomers.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
-
+
Collections.sort(customers, idComparator);
Collections.sort(loadedCustomers, idComparator);
-
+
Assert.assertEquals(customers, loadedCustomers);
-
- loginSysAdmin();
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
}
-
+
@Test
public void testFindCustomersByTitle() throws Exception {
-
- loginSysAdmin();
-
- Tenant tenant = new Tenant();
- tenant.setTitle("My tenant");
- Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
- Assert.assertNotNull(savedTenant);
-
TenantId tenantId = savedTenant.getId();
-
- User tenantAdmin = new User();
- tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
- tenantAdmin.setTenantId(tenantId);
- tenantAdmin.setEmail("tenant2@thingsboard.org");
- tenantAdmin.setFirstName("Joe");
- tenantAdmin.setLastName("Downs");
-
- tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
-
+
String title1 = "Customer title 1";
List customersTitle1 = new ArrayList<>();
- for (int i=0;i<143;i++) {
+ for (int i = 0; i < 143; i++) {
Customer customer = new Customer();
customer.setTenantId(tenantId);
- String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
- String title = title1+suffix;
+ String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
+ String title = title1 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
customer.setTitle(title);
customersTitle1.add(doPost("/api/customer", customer, Customer.class));
}
String title2 = "Customer title 2";
List customersTitle2 = new ArrayList<>();
- for (int i=0;i<175;i++) {
+ for (int i = 0; i < 175; i++) {
Customer customer = new Customer();
customer.setTenantId(tenantId);
- String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
- String title = title2+suffix;
+ String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
+ String title = title2 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
customer.setTitle(title);
customersTitle2.add(doPost("/api/customer", customer, Customer.class));
}
-
+
List loadedCustomersTitle1 = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(15, title1);
TextPageData pageData = null;
do {
- pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>() {
+ }, pageLink);
loadedCustomersTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
-
+
Collections.sort(customersTitle1, idComparator);
Collections.sort(loadedCustomersTitle1, idComparator);
-
+
Assert.assertEquals(customersTitle1, loadedCustomersTitle1);
-
+
List loadedCustomersTitle2 = new ArrayList<>();
pageLink = new TextPageLink(4, title2);
do {
- pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>() {
+ }, pageLink);
loadedCustomersTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
@@ -334,33 +243,30 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
Collections.sort(customersTitle2, idComparator);
Collections.sort(loadedCustomersTitle2, idComparator);
-
+
Assert.assertEquals(customersTitle2, loadedCustomersTitle2);
-
+
for (Customer customer : loadedCustomersTitle1) {
- doDelete("/api/customer/"+customer.getId().getId().toString())
- .andExpect(status().isOk());
+ doDelete("/api/customer/" + customer.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
pageLink = new TextPageLink(4, title1);
- pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>() {
+ }, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
-
+
for (Customer customer : loadedCustomersTitle2) {
- doDelete("/api/customer/"+customer.getId().getId().toString())
- .andExpect(status().isOk());
+ doDelete("/api/customer/" + customer.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
pageLink = new TextPageLink(4, title2);
- pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>() {
+ }, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
-
- loginSysAdmin();
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
}
-
+
}
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java
index 0ae939550a..e8fbfe4aa1 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java
@@ -16,10 +16,8 @@
package org.thingsboard.server.controller;
import static org.hamcrest.Matchers.containsString;
-import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-import java.sql.Time;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -33,7 +31,6 @@ import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.security.Authority;
-import org.thingsboard.server.dao.model.ModelConstants;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -93,6 +90,17 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class);
Assert.assertEquals(foundDashboard.getTitle(), savedDashboard.getTitle());
}
+
+ @Test
+ public void testUpdateDashboardFromDifferentTenant() throws Exception {
+ Dashboard dashboard = new Dashboard();
+ dashboard.setTitle("My dashboard");
+ Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class);
+
+ loginDifferentTenant();
+ doPost("/api/dashboard", savedDashboard, Dashboard.class, status().isForbidden());
+ deleteDifferentTenant();
+ }
@Test
public void testFindDashboardById() throws Exception {
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
index 2f347f1f89..aa11c4ab40 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
@@ -107,6 +107,17 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
Assert.assertEquals(foundDevice.getName(), savedDevice.getName());
}
+
+ @Test
+ public void testUpdateDeviceFromDifferentTenant() throws Exception {
+ Device device = new Device();
+ device.setName("My device");
+ device.setType("default");
+ Device savedDevice = doPost("/api/device", device, Device.class);
+ loginDifferentTenant();
+ doPost("/api/device", savedDevice, Device.class, status().isForbidden());
+ deleteDifferentTenant();
+ }
@Test
public void testFindDeviceById() throws Exception {
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
index 420446e04d..bec1b55a79 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
@@ -25,7 +25,6 @@ import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
@@ -132,6 +131,15 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
assertEquals(foundEntityView.getKeys(), telemetry);
}
+
+ @Test
+ public void testUpdateEntityViewFromDifferentTenant() throws Exception {
+ EntityView savedView = getNewSavedEntityView("Test entity view");
+ loginDifferentTenant();
+ doPost("/api/entityView", savedView, EntityView.class, status().isForbidden());
+ deleteDifferentTenant();
+ }
+
@Test
public void testDeleteEntityView() throws Exception {
EntityView view = getNewSavedEntityView("Test entity view");
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java
index 2a2032e4c7..e290ac9f24 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java
@@ -38,21 +38,23 @@ import java.util.List;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public abstract class BaseUserControllerTest extends AbstractControllerTest {
-
+
private IdComparator idComparator = new IdComparator<>();
@Test
public void testSaveUser() throws Exception {
loginSysAdmin();
-
+
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
-
+
String email = "tenant2@thingsboard.org";
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
@@ -66,13 +68,13 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
Assert.assertTrue(savedUser.getCreatedTime() > 0);
Assert.assertEquals(user.getEmail(), savedUser.getEmail());
- User foundUser = doGet("/api/user/"+savedUser.getId().getId().toString(), User.class);
+ User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
Assert.assertEquals(foundUser, savedUser);
-
+
logout();
doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken)
- .andExpect(status().isSeeOther())
- .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken));
+ .andExpect(status().isSeeOther())
+ .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken));
JsonNode activateRequest = new ObjectMapper().createObjectNode()
.put("activateToken", TestMailService.currentActivateToken)
@@ -82,36 +84,61 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
validateAndSetJwtToken(tokenInfo, email);
doGet("/api/auth/user")
- .andExpect(status().isOk())
- .andExpect(jsonPath("$.authority",is(Authority.TENANT_ADMIN.name())))
- .andExpect(jsonPath("$.email",is(email)));
-
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.authority", is(Authority.TENANT_ADMIN.name())))
+ .andExpect(jsonPath("$.email", is(email)));
+
logout();
-
+
login(email, "testPassword");
-
+
doGet("/api/auth/user")
- .andExpect(status().isOk())
- .andExpect(jsonPath("$.authority",is(Authority.TENANT_ADMIN.name())))
- .andExpect(jsonPath("$.email",is(email)));
-
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.authority", is(Authority.TENANT_ADMIN.name())))
+ .andExpect(jsonPath("$.email", is(email)));
+
loginSysAdmin();
- doDelete("/api/user/"+savedUser.getId().getId().toString())
- .andExpect(status().isOk());
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
+ doDelete("/api/user/" + savedUser.getId().getId().toString())
+ .andExpect(status().isOk());
+
+ doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
+ @Test
+ public void testUpdateUserFromDifferentTenant() throws Exception {
+ loginSysAdmin();
+ Tenant tenant = new Tenant();
+ tenant.setTitle("My tenant");
+ Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
+ Assert.assertNotNull(savedTenant);
+
+ User tenantAdmin = new User();
+ tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
+ tenantAdmin.setTenantId(savedTenant.getId());
+ tenantAdmin.setEmail("tenant2@thingsboard.org");
+ tenantAdmin.setFirstName("Joe");
+ tenantAdmin.setLastName("Downs");
+ tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
+
+ loginDifferentTenant();
+ doPost("/api/user", tenantAdmin, User.class, status().isForbidden());
+ deleteDifferentTenant();
+
+ loginSysAdmin();
+ doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
+ .andExpect(status().isOk());
+ }
+
@Test
public void testResetPassword() throws Exception {
loginSysAdmin();
-
+
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
-
+
String email = "tenant2@thingsboard.org";
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
@@ -119,7 +146,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
user.setEmail(email);
user.setFirstName("Joe");
user.setLastName("Downs");
-
+
User savedUser = createUserAndLogin(user, "testPassword1");
logout();
@@ -127,10 +154,10 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
.put("email", email);
doPost("/api/noauth/resetPasswordByEmail", resetPasswordByEmailRequest)
- .andExpect(status().isOk());
+ .andExpect(status().isOk());
doGet("/api/noauth/resetPassword?resetToken={resetToken}", TestMailService.currentResetPasswordToken)
- .andExpect(status().isSeeOther())
- .andExpect(header().string(HttpHeaders.LOCATION, "/login/resetPassword?resetToken=" + TestMailService.currentResetPasswordToken));
+ .andExpect(status().isSeeOther())
+ .andExpect(header().string(HttpHeaders.LOCATION, "/login/resetPassword?resetToken=" + TestMailService.currentResetPasswordToken));
JsonNode resetPasswordRequest = new ObjectMapper().createObjectNode()
.put("resetToken", TestMailService.currentResetPasswordToken)
@@ -140,35 +167,35 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
validateAndSetJwtToken(tokenInfo, email);
doGet("/api/auth/user")
- .andExpect(status().isOk())
- .andExpect(jsonPath("$.authority",is(Authority.TENANT_ADMIN.name())))
- .andExpect(jsonPath("$.email",is(email)));
-
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.authority", is(Authority.TENANT_ADMIN.name())))
+ .andExpect(jsonPath("$.email", is(email)));
+
logout();
-
+
login(email, "testPassword2");
doGet("/api/auth/user")
- .andExpect(status().isOk())
- .andExpect(jsonPath("$.authority",is(Authority.TENANT_ADMIN.name())))
- .andExpect(jsonPath("$.email",is(email)));
-
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.authority", is(Authority.TENANT_ADMIN.name())))
+ .andExpect(jsonPath("$.email", is(email)));
+
loginSysAdmin();
- doDelete("/api/user/"+savedUser.getId().getId().toString())
- .andExpect(status().isOk());
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
+ doDelete("/api/user/" + savedUser.getId().getId().toString())
+ .andExpect(status().isOk());
+
+ doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
@Test
public void testFindUserById() throws Exception {
loginSysAdmin();
-
+
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
-
+
String email = "tenant2@thingsboard.org";
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
@@ -176,25 +203,25 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
user.setEmail(email);
user.setFirstName("Joe");
user.setLastName("Downs");
-
+
User savedUser = doPost("/api/user", user, User.class);
- User foundUser = doGet("/api/user/"+savedUser.getId().getId().toString(), User.class);
+ User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
Assert.assertNotNull(foundUser);
Assert.assertEquals(savedUser, foundUser);
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
+
+ doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
@Test
public void testSaveUserWithSameEmail() throws Exception {
loginSysAdmin();
-
+
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
-
+
String email = TENANT_ADMIN_EMAIL;
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
@@ -202,24 +229,24 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
user.setEmail(email);
user.setFirstName("Joe");
user.setLastName("Downs");
-
+
doPost("/api/user", user)
- .andExpect(status().isBadRequest())
- .andExpect(statusReason(containsString("User with email '" + email + "' already present in database")));
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
+ .andExpect(status().isBadRequest())
+ .andExpect(statusReason(containsString("User with email '" + email + "' already present in database")));
+
+ doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
@Test
public void testSaveUserWithInvalidEmail() throws Exception {
loginSysAdmin();
-
+
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
-
+
String email = "tenant_thingsboard.org";
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
@@ -227,62 +254,62 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
user.setEmail(email);
user.setFirstName("Joe");
user.setLastName("Downs");
-
+
doPost("/api/user", user)
- .andExpect(status().isBadRequest())
- .andExpect(statusReason(containsString("Invalid email address format '" + email + "'")));
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
+ .andExpect(status().isBadRequest())
+ .andExpect(statusReason(containsString("Invalid email address format '" + email + "'")));
+
+ doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
@Test
public void testSaveUserWithEmptyEmail() throws Exception {
loginSysAdmin();
-
+
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
-
+
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(savedTenant.getId());
user.setFirstName("Joe");
user.setLastName("Downs");
-
+
doPost("/api/user", user)
- .andExpect(status().isBadRequest())
- .andExpect(statusReason(containsString("User email should be specified")));
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
+ .andExpect(status().isBadRequest())
+ .andExpect(statusReason(containsString("User email should be specified")));
+
+ doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
@Test
public void testSaveUserWithoutTenant() throws Exception {
loginSysAdmin();
-
+
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setEmail("tenant2@thingsboard.org");
user.setFirstName("Joe");
user.setLastName("Downs");
-
+
doPost("/api/user", user)
- .andExpect(status().isBadRequest())
- .andExpect(statusReason(containsString("Tenant administrator should be assigned to tenant")));
+ .andExpect(status().isBadRequest())
+ .andExpect(statusReason(containsString("Tenant administrator should be assigned to tenant")));
}
-
+
@Test
public void testDeleteUser() throws Exception {
loginSysAdmin();
-
+
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
-
+
String email = "tenant2@thingsboard.org";
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
@@ -290,176 +317,182 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
user.setEmail(email);
user.setFirstName("Joe");
user.setLastName("Downs");
-
+
User savedUser = doPost("/api/user", user, User.class);
- User foundUser = doGet("/api/user/"+savedUser.getId().getId().toString(), User.class);
+ User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
Assert.assertNotNull(foundUser);
-
- doDelete("/api/user/"+savedUser.getId().getId().toString())
- .andExpect(status().isOk());
-
- doGet("/api/user/"+savedUser.getId().getId().toString())
- .andExpect(status().isNotFound());
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
+
+ doDelete("/api/user/" + savedUser.getId().getId().toString())
+ .andExpect(status().isOk());
+
+ doGet("/api/user/" + savedUser.getId().getId().toString())
+ .andExpect(status().isNotFound());
+
+ doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
@Test
public void testFindTenantAdmins() throws Exception {
loginSysAdmin();
-
+
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
-
+
TenantId tenantId = savedTenant.getId();
-
+
List tenantAdmins = new ArrayList<>();
- for (int i=0;i<64;i++) {
+ for (int i = 0; i < 64; i++) {
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(tenantId);
user.setEmail("testTenant" + i + "@thingsboard.org");
tenantAdmins.add(doPost("/api/user", user, User.class));
}
-
+
List loadedTenantAdmins = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(33);
TextPageData pageData = null;
do {
- pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
+ new TypeReference>() {
+ }, pageLink);
loadedTenantAdmins.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
-
+
Collections.sort(tenantAdmins, idComparator);
Collections.sort(loadedTenantAdmins, idComparator);
-
+
Assert.assertEquals(tenantAdmins, loadedTenantAdmins);
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
-
+
+ doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
+ .andExpect(status().isOk());
+
pageLink = new TextPageLink(33);
- pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
+ new TypeReference>() {
+ }, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertTrue(pageData.getData().isEmpty());
}
-
+
@Test
public void testFindTenantAdminsByEmail() throws Exception {
-
+
loginSysAdmin();
-
+
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
-
+
TenantId tenantId = savedTenant.getId();
-
- String email1 = "testEmail1";
+
+ String email1 = "testEmail1";
List tenantAdminsEmail1 = new ArrayList<>();
-
- for (int i=0;i<124;i++) {
+
+ for (int i = 0; i < 124; i++) {
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(tenantId);
- String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
- String email = email1+suffix+ "@thingsboard.org";
+ String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
+ String email = email1 + suffix + "@thingsboard.org";
email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase();
user.setEmail(email);
tenantAdminsEmail1.add(doPost("/api/user", user, User.class));
}
-
- String email2 = "testEmail2";
+
+ String email2 = "testEmail2";
List tenantAdminsEmail2 = new ArrayList<>();
-
- for (int i=0;i<112;i++) {
+
+ for (int i = 0; i < 112; i++) {
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(tenantId);
- String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
- String email = email2+suffix+ "@thingsboard.org";
+ String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
+ String email = email2 + suffix + "@thingsboard.org";
email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase();
user.setEmail(email);
tenantAdminsEmail2.add(doPost("/api/user", user, User.class));
}
-
+
List loadedTenantAdminsEmail1 = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(33, email1);
TextPageData pageData = null;
do {
- pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
+ new TypeReference>() {
+ }, pageLink);
loadedTenantAdminsEmail1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
-
+
Collections.sort(tenantAdminsEmail1, idComparator);
Collections.sort(loadedTenantAdminsEmail1, idComparator);
-
+
Assert.assertEquals(tenantAdminsEmail1, loadedTenantAdminsEmail1);
-
+
List loadedTenantAdminsEmail2 = new ArrayList<>();
pageLink = new TextPageLink(16, email2);
do {
- pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
+ new TypeReference>() {
+ }, pageLink);
loadedTenantAdminsEmail2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
-
+
Collections.sort(tenantAdminsEmail2, idComparator);
Collections.sort(loadedTenantAdminsEmail2, idComparator);
-
+
Assert.assertEquals(tenantAdminsEmail2, loadedTenantAdminsEmail2);
-
+
for (User user : loadedTenantAdminsEmail1) {
- doDelete("/api/user/"+user.getId().getId().toString())
- .andExpect(status().isOk());
+ doDelete("/api/user/" + user.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
pageLink = new TextPageLink(4, email1);
- pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
+ new TypeReference>() {
+ }, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
-
+
for (User user : loadedTenantAdminsEmail2) {
- doDelete("/api/user/"+user.getId().getId().toString())
- .andExpect(status().isOk());
+ doDelete("/api/user/" + user.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
pageLink = new TextPageLink(4, email2);
- pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
+ new TypeReference>() {
+ }, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
+
+ doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
@Test
public void testFindCustomerUsers() throws Exception {
-
+
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
-
+
TenantId tenantId = savedTenant.getId();
User tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
@@ -467,59 +500,60 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
tenantAdmin.setEmail("tenant2@thingsboard.org");
tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs");
-
+
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
-
+
Customer customer = new Customer();
customer.setTitle("My customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
CustomerId customerId = savedCustomer.getId();
-
+
List customerUsers = new ArrayList<>();
- for (int i=0;i<56;i++) {
+ for (int i = 0; i < 56; i++) {
User user = new User();
user.setAuthority(Authority.CUSTOMER_USER);
user.setCustomerId(customerId);
user.setEmail("testCustomer" + i + "@thingsboard.org");
customerUsers.add(doPost("/api/user", user, User.class));
}
-
+
List loadedCustomerUsers = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(33);
TextPageData pageData = null;
do {
- pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
+ new TypeReference>() {
+ }, pageLink);
loadedCustomerUsers.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
-
+
Collections.sort(customerUsers, idComparator);
Collections.sort(loadedCustomerUsers, idComparator);
-
+
Assert.assertEquals(customerUsers, loadedCustomerUsers);
-
- doDelete("/api/customer/"+customerId.getId().toString())
- .andExpect(status().isOk());
-
+
+ doDelete("/api/customer/" + customerId.getId().toString())
+ .andExpect(status().isOk());
+
loginSysAdmin();
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
+
+ doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
@Test
public void testFindCustomerUsersByEmail() throws Exception {
-
+
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
-
+
TenantId tenantId = savedTenant.getId();
User tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
@@ -527,105 +561,109 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
tenantAdmin.setEmail("tenant2@thingsboard.org");
tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs");
-
+
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
-
+
Customer customer = new Customer();
customer.setTitle("My customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
CustomerId customerId = savedCustomer.getId();
-
- String email1 = "testEmail1";
+
+ String email1 = "testEmail1";
List customerUsersEmail1 = new ArrayList<>();
-
- for (int i=0;i<74;i++) {
+
+ for (int i = 0; i < 74; i++) {
User user = new User();
user.setAuthority(Authority.CUSTOMER_USER);
user.setCustomerId(customerId);
- String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
- String email = email1+suffix+ "@thingsboard.org";
+ String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
+ String email = email1 + suffix + "@thingsboard.org";
email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase();
user.setEmail(email);
customerUsersEmail1.add(doPost("/api/user", user, User.class));
}
-
- String email2 = "testEmail2";
+
+ String email2 = "testEmail2";
List customerUsersEmail2 = new ArrayList<>();
-
- for (int i=0;i<92;i++) {
+
+ for (int i = 0; i < 92; i++) {
User user = new User();
user.setAuthority(Authority.CUSTOMER_USER);
user.setCustomerId(customerId);
- String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
- String email = email2+suffix+ "@thingsboard.org";
+ String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
+ String email = email2 + suffix + "@thingsboard.org";
email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase();
user.setEmail(email);
customerUsersEmail2.add(doPost("/api/user", user, User.class));
}
-
+
List loadedCustomerUsersEmail1 = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(33, email1);
TextPageData pageData = null;
do {
- pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
+ new TypeReference>() {
+ }, pageLink);
loadedCustomerUsersEmail1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
-
+
Collections.sort(customerUsersEmail1, idComparator);
Collections.sort(loadedCustomerUsersEmail1, idComparator);
-
+
Assert.assertEquals(customerUsersEmail1, loadedCustomerUsersEmail1);
-
+
List loadedCustomerUsersEmail2 = new ArrayList<>();
pageLink = new TextPageLink(16, email2);
do {
- pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
+ new TypeReference>() {
+ }, pageLink);
loadedCustomerUsersEmail2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
-
+
Collections.sort(customerUsersEmail2, idComparator);
Collections.sort(loadedCustomerUsersEmail2, idComparator);
-
+
Assert.assertEquals(customerUsersEmail2, loadedCustomerUsersEmail2);
-
+
for (User user : loadedCustomerUsersEmail1) {
- doDelete("/api/user/"+user.getId().getId().toString())
- .andExpect(status().isOk());
+ doDelete("/api/user/" + user.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
pageLink = new TextPageLink(4, email1);
- pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
+ new TypeReference>() {
+ }, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
-
+
for (User user : loadedCustomerUsersEmail2) {
- doDelete("/api/user/"+user.getId().getId().toString())
- .andExpect(status().isOk());
+ doDelete("/api/user/" + user.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
pageLink = new TextPageLink(4, email2);
- pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
+ new TypeReference>() {
+ }, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
-
- doDelete("/api/customer/"+customerId.getId().toString())
- .andExpect(status().isOk());
-
+
+ doDelete("/api/customer/" + customerId.getId().toString())
+ .andExpect(status().isOk());
+
loginSysAdmin();
-
- doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
+
+ doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
+ .andExpect(status().isOk());
}
-
+
}
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseWidgetTypeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseWidgetTypeControllerTest.java
index 85dc0572cc..ce2404041c 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseWidgetTypeControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseWidgetTypeControllerTest.java
@@ -64,7 +64,6 @@ public abstract class BaseWidgetTypeControllerTest extends AbstractControllerTes
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTitle("My widgets bundle");
savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class);
-
}
@After
@@ -100,6 +99,19 @@ public abstract class BaseWidgetTypeControllerTest extends AbstractControllerTes
Assert.assertEquals(foundWidgetType.getName(), savedWidgetType.getName());
}
+ @Test
+ public void testUpdateWidgetTypeFromDifferentTenant() throws Exception {
+ WidgetType widgetType = new WidgetType();
+ widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
+ widgetType.setName("Widget Type");
+ widgetType.setDescriptor(new ObjectMapper().readValue("{ \"someKey\": \"someValue\" }", JsonNode.class));
+ WidgetType savedWidgetType = doPost("/api/widgetType", widgetType, WidgetType.class);
+
+ loginDifferentTenant();
+ doPost("/api/widgetType", savedWidgetType, WidgetType.class, status().isForbidden());
+ deleteDifferentTenant();
+ }
+
@Test
public void testFindWidgetTypeById() throws Exception {
WidgetType widgetType = new WidgetType();
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java
index c274ea92eb..ae23f6633b 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java
@@ -88,6 +88,17 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
Assert.assertEquals(foundWidgetsBundle.getTitle(), savedWidgetsBundle.getTitle());
}
+ @Test
+ public void testUpdateWidgetsBundleFromDifferentTenant() throws Exception {
+ WidgetsBundle widgetsBundle = new WidgetsBundle();
+ widgetsBundle.setTitle("My widgets bundle");
+ WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class);
+
+ loginDifferentTenant();
+ doPost("/api/widgetsBundle", savedWidgetsBundle, WidgetsBundle.class, status().isForbidden());
+ deleteDifferentTenant();
+ }
+
@Test
public void testFindWidgetsBundleById() throws Exception {
WidgetsBundle widgetsBundle = new WidgetsBundle();
diff --git a/application/src/test/java/org/thingsboard/server/rules/RuleEngineNoSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/rules/RuleEngineNoSqlTestSuite.java
deleted file mode 100644
index fbab13147c..0000000000
--- a/application/src/test/java/org/thingsboard/server/rules/RuleEngineNoSqlTestSuite.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * Copyright © 2016-2020 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.rules;
-
-import org.cassandraunit.dataset.cql.ClassPathCQLDataSet;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.extensions.cpsuite.ClasspathSuite;
-import org.junit.runner.RunWith;
-import org.thingsboard.server.dao.CustomCassandraCQLUnit;
-import org.thingsboard.server.dao.CustomSqlUnit;
-import org.thingsboard.server.queue.memory.InMemoryStorage;
-
-import java.util.Arrays;
-
-@RunWith(ClasspathSuite.class)
-@ClasspathSuite.ClassnameFilters({
- "org.thingsboard.server.rules.flow.nosql.*Test",
- "org.thingsboard.server.rules.lifecycle.nosql.*Test"
-})
-public class RuleEngineNoSqlTestSuite {
-
- @ClassRule
- public static CustomCassandraCQLUnit cassandraUnit =
- new CustomCassandraCQLUnit(
- Arrays.asList(
- new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false),
- new ClassPathCQLDataSet("cassandra/schema-entities.cql", false, false),
- new ClassPathCQLDataSet("cassandra/system-data.cql", false, false)),
- "cassandra-test.yaml", 30000l);
-
- @BeforeClass
- public static void cleanupInMemStorage(){
- InMemoryStorage.getInstance().cleanup();
- }
-
-}
diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml
index 6ef1fae0a4..1c9417b763 100644
--- a/common/dao-api/pom.xml
+++ b/common/dao-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 2.5.0-SNAPSHOT
+ 2.5.1-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
index 13a3799cf2..e0db26b718 100644
--- a/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
@@ -25,6 +25,8 @@ import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.page.TimePageData;
+import org.thingsboard.server.common.data.page.TimePageLink;
import java.util.List;
import java.util.Optional;
@@ -67,9 +69,7 @@ public interface AssetService {
Asset assignAssetToEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId);
- Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId);
+ Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId);
- TextPageData findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink);
-
- TextPageData findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink);
+ ListenableFuture> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
}
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
index 3bd47fbd68..4e3099e25b 100644
--- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
@@ -25,11 +25,13 @@ import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.page.TimePageData;
+import org.thingsboard.server.common.data.page.TimePageLink;
import java.util.List;
public interface DeviceService {
-
+
Device findDeviceById(TenantId tenantId, DeviceId deviceId);
ListenableFuture findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId);
@@ -68,10 +70,7 @@ public interface DeviceService {
Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
- Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId);
-
- TextPageData findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink);
-
- TextPageData findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink);
+ Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
+ ListenableFuture> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
}
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java
index d3913cf691..48f02c28fb 100644
--- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java
@@ -82,6 +82,8 @@ public interface EdgeService {
Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException;
+ void assignDefaultRuleChainsToEdge(TenantId tenantId, EdgeId edgeId);
+
ListenableFuture> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId, TimePageLink pageLink);
ListenableFuture> findEdgesByTenantIdAndDashboardId(TenantId tenantId, DashboardId dashboardId, TimePageLink pageLink);
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
index 04b096f4b2..6061a448e6 100644
--- a/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
@@ -26,6 +26,8 @@ import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.page.TimePageData;
+import org.thingsboard.server.common.data.page.TimePageLink;
import java.util.List;
@@ -68,11 +70,7 @@ public interface EntityViewService {
EntityView assignEntityViewToEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId);
- EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId);
-
- TextPageData findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink);
-
- TextPageData findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink);
-
+ EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId);
+ ListenableFuture> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
}
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2User.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2User.java
index c0075633e5..7ebc79cff2 100644
--- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2User.java
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2User.java
@@ -28,4 +28,6 @@ public class OAuth2User {
private String email;
private String firstName;
private String lastName;
+ private boolean alwaysFullScreen;
+ private String defaultDashboardName;
}
diff --git a/common/data/pom.xml b/common/data/pom.xml
index 69f4281661..2f89107d95 100644
--- a/common/data/pom.xml
+++ b/common/data/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 2.5.0-SNAPSHOT
+ 2.5.1-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java
index 201bbc8b07..cd617ec345 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java
@@ -18,7 +18,6 @@ package org.thingsboard.server.common.data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
@EqualsAndHashCode(callSuper = true)
@@ -28,7 +27,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen
private TenantId tenantId;
private CustomerId customerId;
- private EdgeId edgeId;
private String name;
private String type;
private String label;
@@ -48,7 +46,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen
this.name = device.getName();
this.type = device.getType();
this.label = device.getLabel();
- this.edgeId = device.getEdgeId();
}
public TenantId getTenantId() {
@@ -67,14 +64,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen
this.customerId = customerId;
}
- public EdgeId getEdgeId() {
- return edgeId;
- }
-
- public void setEdgeId(EdgeId edgeId) {
- this.edgeId = edgeId;
- }
-
@Override
public String getName() {
return name;
@@ -112,8 +101,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen
builder.append(tenantId);
builder.append(", customerId=");
builder.append(customerId);
- builder.append(", edgeId=");
- builder.append(edgeId);
builder.append(", name=");
builder.append(name);
builder.append(", type=");
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java
index 157a5c884e..4c7e69d79e 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java
@@ -19,7 +19,6 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.CustomerId;
-import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
@@ -40,7 +39,6 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo
private EntityId entityId;
private TenantId tenantId;
private CustomerId customerId;
- private EdgeId edgeId;
private String name;
private String type;
private TelemetryEntityView keys;
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java
index e0dc5dc90c..e0f587dda4 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java
@@ -15,13 +15,10 @@
*/
package org.thingsboard.server.common.data.asset;
-import com.fasterxml.jackson.databind.JsonNode;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
-import org.thingsboard.server.common.data.id.EdgeId;
-import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
@EqualsAndHashCode(callSuper = true)
@@ -34,7 +31,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements
private String name;
private String type;
private String label;
- private EdgeId edgeId;
public Asset() {
super();
@@ -51,7 +47,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements
this.name = asset.getName();
this.type = asset.getType();
this.label = asset.getLabel();
- this.edgeId = asset.getEdgeId();
}
public TenantId getTenantId() {
@@ -70,14 +65,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements
this.customerId = customerId;
}
- public EdgeId getEdgeId() {
- return edgeId;
- }
-
- public void setEdgeId(EdgeId edgeId) {
- this.edgeId = edgeId;
- }
-
@Override
public String getName() {
return name;
@@ -115,8 +102,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements
builder.append(tenantId);
builder.append(", customerId=");
builder.append(customerId);
- builder.append(", edgeId=");
- builder.append(edgeId);
builder.append(", name=");
builder.append(name);
builder.append(", type=");
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java
index 611c4777a3..07315428e3 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java
@@ -22,6 +22,7 @@ public enum RelationTypeGroup {
DASHBOARD,
RULE_CHAIN,
RULE_NODE,
- EDGE
+ EDGE,
+ EDGE_DEFAULT_RULE_CHAIN
}
diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml
index bc42a75370..dfd42240c4 100644
--- a/common/edge-api/pom.xml
+++ b/common/edge-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 2.5.0-SNAPSHOT
+ 2.5.1-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/message/pom.xml b/common/message/pom.xml
index 5c7c624db8..3241e98efc 100644
--- a/common/message/pom.xml
+++ b/common/message/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 2.5.0-SNAPSHOT
+ 2.5.1-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java
index 9da7407552..845138d92c 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java
+++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java
@@ -40,6 +40,7 @@ import java.util.UUID;
public final class TbMsg implements Serializable {
private final UUID id;
+ private final long ts;
private final String type;
private final EntityId originator;
private final TbMsgMetaData metaData;
@@ -51,38 +52,43 @@ public final class TbMsg implements Serializable {
transient private final TbMsgCallback callback;
public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) {
- return new TbMsg(UUID.randomUUID(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, TbMsgCallback.EMPTY);
+ return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, TbMsgCallback.EMPTY);
}
public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
- return new TbMsg(UUID.randomUUID(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
+ return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
}
public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) {
- return new TbMsg(UUID.randomUUID(), type, originator, metaData.copy(), dataType, data, null, null, TbMsgCallback.EMPTY);
+ return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, null, null, TbMsgCallback.EMPTY);
}
public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
- return new TbMsg(UUID.randomUUID(), type, originator, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
+ return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
}
public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) {
- return new TbMsg(UUID.randomUUID(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, callback);
+ return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, callback);
}
public static TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) {
- return new TbMsg(origMsg.getId(), type, originator, metaData.copy(), origMsg.getDataType(),
+ return new TbMsg(origMsg.getId(), origMsg.getTs(), type, originator, metaData.copy(), origMsg.getDataType(),
data, origMsg.getRuleChainId(), origMsg.getRuleNodeId(), origMsg.getCallback());
}
public static TbMsg newMsg(TbMsg tbMsg, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
- return new TbMsg(UUID.randomUUID(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(),
+ return new TbMsg(UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(),
tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
}
- private TbMsg(UUID id, String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data,
+ private TbMsg(UUID id, long ts, String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data,
RuleChainId ruleChainId, RuleNodeId ruleNodeId, TbMsgCallback callback) {
this.id = id;
+ if (ts > 0) {
+ this.ts = ts;
+ } else {
+ this.ts = System.currentTimeMillis();
+ }
this.type = type;
this.originator = originator;
this.metaData = metaData;
@@ -105,6 +111,7 @@ public final class TbMsg implements Serializable {
public static byte[] toByteArray(TbMsg msg) {
MsgProtos.TbMsgProto.Builder builder = MsgProtos.TbMsgProto.newBuilder();
builder.setId(msg.getId().toString());
+ builder.setTs(msg.getTs());
builder.setType(msg.getType());
builder.setEntityType(msg.getOriginator().getEntityType().name());
builder.setEntityIdMSB(msg.getOriginator().getId().getMostSignificantBits());
@@ -124,7 +131,6 @@ public final class TbMsg implements Serializable {
builder.setMetaData(MsgProtos.TbMsgMetaDataProto.newBuilder().putAllData(msg.getMetaData().getData()).build());
}
-
builder.setDataType(msg.getDataType().ordinal());
builder.setData(msg.getData());
return builder.build().toByteArray();
@@ -144,18 +150,18 @@ public final class TbMsg implements Serializable {
ruleNodeId = new RuleNodeId(new UUID(proto.getRuleNodeIdMSB(), proto.getRuleNodeIdLSB()));
}
TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()];
- return new TbMsg(UUID.fromString(proto.getId()), proto.getType(), entityId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, callback);
+ return new TbMsg(UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, callback);
} catch (InvalidProtocolBufferException e) {
throw new IllegalStateException("Could not parse protobuf for TbMsg", e);
}
}
public TbMsg copyWithRuleChainId(RuleChainId ruleChainId) {
- return new TbMsg(this.id, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, null, callback);
+ return new TbMsg(this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, null, callback);
}
public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
- return new TbMsg(this.id, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, callback);
+ return new TbMsg(this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, callback);
}
public TbMsgCallback getCallback() {
diff --git a/common/message/src/main/proto/tbmsg.proto b/common/message/src/main/proto/tbmsg.proto
index 737d5f6f57..25e006121f 100644
--- a/common/message/src/main/proto/tbmsg.proto
+++ b/common/message/src/main/proto/tbmsg.proto
@@ -44,4 +44,5 @@ message TbMsgProto {
int32 dataType = 13;
string data = 14;
+ int64 ts = 15;
}
\ No newline at end of file
diff --git a/common/pom.xml b/common/pom.xml
index ddc3243b0b..47498b2a48 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 2.5.0-SNAPSHOT
+ 2.5.1-SNAPSHOT
thingsboard
common
diff --git a/common/queue/pom.xml b/common/queue/pom.xml
index 668da07e29..838d032482 100644
--- a/common/queue/pom.xml
+++ b/common/queue/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 2.5.0-SNAPSHOT
+ 2.5.1-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusConsumerTemplate.java
index 734c79d927..9dc5a19ecd 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusConsumerTemplate.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusConsumerTemplate.java
@@ -101,7 +101,7 @@ public class TbServiceBusConsumerTemplate extends Abstract
@Override
protected void doSubscribe(List topicNames) {
createReceivers();
- messagesPerQueue = receivers.size() / partitions.size();
+ messagesPerQueue = receivers.size() / Math.max(partitions.size(), 1);
}
@Override
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueConsumerTemplate.java
index c8cc545601..8512eba1e7 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueConsumerTemplate.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueConsumerTemplate.java
@@ -85,7 +85,12 @@ public abstract class AbstractTbQueueConsumerTemplate i
subscribed = true;
}
- List records = doPoll(durationInMillis);
+ List records;
+ if (partitions.isEmpty()) {
+ records = Collections.emptyList();
+ } else {
+ records = doPoll(durationInMillis);
+ }
if (!records.isEmpty()) {
List result = new ArrayList<>(records.size());
records.forEach(record -> {
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java
index f3f11fa7a1..c683ed846d 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java
@@ -36,6 +36,7 @@ import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
@@ -148,6 +149,14 @@ public class HashPartitionService implements PartitionService {
}
}
});
+
+ oldPartitions.forEach((serviceQueueKey, partitions) -> {
+ if (!myPartitions.containsKey(serviceQueueKey)) {
+ log.info("[{}] NO MORE PARTITIONS FOR CURRENT KEY", serviceQueueKey);
+ applicationEventPublisher.publishEvent(new PartitionChangeEvent(this, serviceQueueKey, Collections.emptySet()));
+ }
+ });
+
myPartitions.forEach((serviceQueueKey, partitions) -> {
if (!partitions.equals(oldPartitions.get(serviceQueueKey))) {
log.info("[{}] NEW PARTITIONS: {}", serviceQueueKey, partitions);
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java
index de94db804d..1fbfb68e3e 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java
@@ -71,8 +71,12 @@ public class TbKafkaConsumerTemplate extends AbstractTbQue
@Override
protected void doSubscribe(List topicNames) {
- topicNames.forEach(admin::createTopicIfNotExists);
- consumer.subscribe(topicNames);
+ if (!topicNames.isEmpty()) {
+ topicNames.forEach(admin::createTopicIfNotExists);
+ consumer.subscribe(topicNames);
+ } else {
+ consumer.unsubscribe();
+ }
}
@Override
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java
index 2aca5e6da1..31ef4d3e45 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java
@@ -200,7 +200,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi
consumerBuilder.settings(kafkaSettings);
consumerBuilder.topic(transportApiSettings.getRequestsTopic());
consumerBuilder.clientId("monolith-transport-api-consumer-" + serviceInfoProvider.getServiceId());
- consumerBuilder.groupId("monolith-transport-api-consumer-" + serviceInfoProvider.getServiceId());
+ consumerBuilder.groupId("monolith-transport-api-consumer");
consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders()));
consumerBuilder.admin(transportApiAdmin);
return consumerBuilder.build();
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java
index 632a6e480e..bf2ec6751e 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java
@@ -170,7 +170,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory {
consumerBuilder.settings(kafkaSettings);
consumerBuilder.topic(transportApiSettings.getRequestsTopic());
consumerBuilder.clientId("tb-core-transport-api-consumer-" + serviceInfoProvider.getServiceId());
- consumerBuilder.groupId("tb-core-transport-api-consumer-" + serviceInfoProvider.getServiceId());
+ consumerBuilder.groupId("tb-core-transport-api-consumer");
consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders()));
consumerBuilder.admin(transportApiAdmin);
return consumerBuilder.build();
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java
index b5b6126cd5..f02cc27ee4 100644
--- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java
+++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java
@@ -106,7 +106,7 @@ public class TbPubSubConsumerTemplate extends AbstractPara
subscriptionNames = new LinkedHashSet<>(topicNames);
subscriptionNames.forEach(admin::createTopicIfNotExists);
initNewExecutor(subscriptionNames.size() + 1);
- messagesPerTopic = pubSubSettings.getMaxMessages() / subscriptionNames.size();
+ messagesPerTopic = pubSubSettings.getMaxMessages() / Math.max(subscriptionNames.size(), 1);
}
@Override
diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml
index 228ea8ec2e..fcbacf2bb5 100644
--- a/common/transport/coap/pom.xml
+++ b/common/transport/coap/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 2.5.0-SNAPSHOT
+ 2.5.1-SNAPSHOT
transport
org.thingsboard.common.transport
diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml
index dc922a732e..0a8479688d 100644
--- a/common/transport/http/pom.xml
+++ b/common/transport/http/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 2.5.0-SNAPSHOT
+ 2.5.1-SNAPSHOT
transport
org.thingsboard.common.transport
diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml
index 9e44e73f37..68a1e55c0d 100644
--- a/common/transport/mqtt/pom.xml
+++ b/common/transport/mqtt/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 2.5.0-SNAPSHOT
+ 2.5.1-SNAPSHOT
transport
org.thingsboard.common.transport
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
index 351be0865f..ac19e72037 100644
--- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
@@ -294,7 +294,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
break;
}
} catch (Exception e) {
- log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS);
+ log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS, e);
grantedQoSList.add(FAILURE.value());
}
}
diff --git a/common/transport/pom.xml b/common/transport/pom.xml
index ead5786108..121301f5bf 100644
--- a/common/transport/pom.xml
+++ b/common/transport/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 2.5.0-SNAPSHOT
+ 2.5.1-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml
index af471e537c..a0b73855d3 100644
--- a/common/transport/transport-api/pom.xml
+++ b/common/transport/transport-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 2.5.0-SNAPSHOT
+ 2.5.1-SNAPSHOT
transport
org.thingsboard.common.transport
@@ -52,10 +52,6 @@
org.thingsboard.common
util
-
- org.thingsboard.common
- queue
-
com.google.code.gson
gson
diff --git a/common/util/pom.xml b/common/util/pom.xml
index 961998f13c..4f0fa51066 100644
--- a/common/util/pom.xml
+++ b/common/util/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 2.5.0-SNAPSHOT
+ 2.5.1-SNAPSHOT
common
org.thingsboard.common
diff --git a/dao/pom.xml b/dao/pom.xml
index ebee947f7a..0b7643ddb2 100644
--- a/dao/pom.xml
+++ b/dao/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 2.5.0-SNAPSHOT
+ 2.5.1-SNAPSHOT
thingsboard
dao
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
index 41f8a2b630..118b8d47fd 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
@@ -341,16 +341,6 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
}
- private void deleteRelation(TenantId tenantId, EntityRelation alarmRelation) {
- log.debug("Deleting Alarm relation: {}", alarmRelation);
- relationService.deleteRelation(tenantId, alarmRelation);
- }
-
- private void createRelation(TenantId tenantId, EntityRelation alarmRelation) {
- log.debug("Creating Alarm relation: {}", alarmRelation);
- relationService.saveRelation(tenantId, alarmRelation);
- }
-
private Alarm merge(Alarm existing, Alarm alarm) {
if (alarm.getStartTs() > existing.getEndTs()) {
existing.setEndTs(alarm.getStartTs());
@@ -395,7 +385,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
}
- private void createAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status, boolean createAnyRelation) {
+ private void createAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status, boolean createAnyRelation) throws ExecutionException, InterruptedException {
if (createAnyRelation) {
createRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + AlarmSearchStatus.ANY.name(), RelationTypeGroup.ALARM));
}
@@ -404,13 +394,13 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
createRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
}
- private void deleteAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status) {
+ private void deleteAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status) throws ExecutionException, InterruptedException {
deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM));
deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM));
deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
}
- private void updateAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) {
+ private void updateAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) throws ExecutionException, InterruptedException {
deleteAlarmRelation(tenantId, entityId, alarmId, oldStatus);
createAlarmRelation(tenantId, entityId, alarmId, newStatus, false);
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
index 958dcf05b3..0566cb895a 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
@@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.Dao;
import java.util.List;
@@ -116,23 +117,12 @@ public interface AssetDao extends Dao {
ListenableFuture> findTenantAssetTypesAsync(UUID tenantId);
/**
- * Find assets by tenantId, customerId and page link.
+ * Find assets by tenantId, edgeId and page link.
*
* @param tenantId the tenantId
* @param edgeId the edgeId
* @param pageLink the page link
* @return the list of asset objects
*/
- List findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink);
-
- /**
- * Find assets by tenantId, customerId, type and page link.
- *
- * @param tenantId the tenantId
- * @param edgeId the edgeId
- * @param type the type
- * @param pageLink the page link
- * @return the list of asset objects
- */
- List findAssetsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink);
+ ListenableFuture> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink);
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
index 907b0399fa..8c0f4cf644 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
@@ -16,6 +16,7 @@
package org.thingsboard.server.dao.asset;
+import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
@@ -29,12 +30,14 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetSearchQuery;
+import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
@@ -42,9 +45,13 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.page.TimePageData;
+import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
+import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.dao.customer.CustomerDao;
+import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.DataValidationException;
@@ -52,6 +59,7 @@ import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.tenant.TenantDao;
+import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -89,6 +97,9 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
@Autowired
private EntityViewService entityViewService;
+ @Autowired
+ private EdgeService edgeService;
+
@Autowired
private CacheManager cacheManager;
@@ -285,36 +296,52 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
@Override
public Asset assignAssetToEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId) {
Asset asset = findAssetById(tenantId, assetId);
- asset.setEdgeId(edgeId);
- return saveAsset(asset);
+ Edge edge = edgeService.findEdgeById(tenantId, edgeId);
+ if (edge == null) {
+ throw new DataValidationException("Can't assign asset to non-existent edge!");
+ }
+ if (!edge.getTenantId().getId().equals(asset.getTenantId().getId())) {
+ throw new DataValidationException("Can't assign asset to edge from different tenant!");
+ }
+ try {
+ createRelation(tenantId, new EntityRelation(edgeId, assetId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
+ } catch (ExecutionException | InterruptedException e) {
+ log.warn("[{}] Failed to create asset relation. Edge Id: [{}]", assetId, edgeId);
+ throw new RuntimeException(e);
+ }
+ return asset;
}
@Override
- public Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId) {
+ public Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId) {
Asset asset = findAssetById(tenantId, assetId);
- asset.setEdgeId(null);
- return saveAsset(asset);
+ Edge edge = edgeService.findEdgeById(tenantId, edgeId);
+ if (edge == null) {
+ throw new DataValidationException("Can't unassign asset from non-existent edge!");
+ }
+ try {
+ deleteRelation(tenantId, new EntityRelation(edgeId, assetId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
+ } catch (ExecutionException | InterruptedException e) {
+ log.warn("[{}] Failed to delete asset relation. Edge Id: [{}]", assetId, edgeId);
+ throw new RuntimeException(e);
+ }
+ return asset;
}
@Override
- public TextPageData findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink) {
+ public ListenableFuture> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) {
log.trace("Executing findAssetsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink);
- List assets = assetDao.findAssetsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
- return new TextPageData<>(assets, pageLink);
- }
-
- @Override
- public TextPageData findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink) {
- log.trace("Executing findAssetsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}], pageLink [{}]", tenantId, edgeId, type, pageLink);
- validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
- validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
- validateString(type, "Incorrect type " + type);
- validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink);
- List assets = assetDao.findAssetsByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink);
- return new TextPageData<>(assets, pageLink);
+ ListenableFuture> assets = assetDao.findAssetsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
+ return Futures.transform(assets, new Function, TimePageData>() {
+ @Nullable
+ @Override
+ public TimePageData apply(@Nullable List assets) {
+ return new TimePageData<>(assets, pageLink);
+ }
+ }, MoreExecutors.directExecutor());
}
private DataValidator assetValidator =
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java
index 5f8632516f..87ccef86f1 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java
@@ -25,16 +25,23 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.asset.Asset;
+import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.page.TimePageLink;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.EntitySubtypeEntity;
import org.thingsboard.server.dao.model.nosql.AssetEntity;
import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao;
+import org.thingsboard.server.dao.relation.RelationDao;
import org.thingsboard.server.dao.util.NoSqlDao;
import javax.annotation.Nullable;
@@ -68,6 +75,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY;
@NoSqlDao
public class CassandraAssetDao extends CassandraAbstractSearchTextDao implements AssetDao {
+ @Autowired
+ private RelationDao relationDao;
+
@Override
protected Class getColumnFamilyClass() {
return AssetEntity.class;
@@ -190,30 +200,16 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink) {
-// log.debug("Try to find assets by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink);
-// List assetEntities = findPageWithTextSearch(new TenantId(tenantId), ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
-// Arrays.asList(eq(ASSET_CUSTOMER_ID_PROPERTY, customerId),
-// eq(ASSET_TENANT_ID_PROPERTY, tenantId)),
-// pageLink);
-//
-// log.trace("Found assets [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", assetEntities, tenantId, customerId, pageLink);
-// return DaoUtil.convertDataList(assetEntities);
- throw new UnsupportedOperationException("Cassandra is not supported yet");
- }
-
- @Override
- public List findAssetsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink) {
-// log.debug("Try to find assets by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink);
-// List assetEntities = findPageWithTextSearch(new TenantId(tenantId), ASSET_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
-// Arrays.asList(eq(ASSET_TYPE_PROPERTY, type),
-// eq(ASSET_CUSTOMER_ID_PROPERTY, customerId),
-// eq(ASSET_TENANT_ID_PROPERTY, tenantId)),
-// pageLink);
-//
-// log.trace("Found assets [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", assetEntities, tenantId, customerId, type, pageLink);
-// return DaoUtil.convertDataList(assetEntities);
- throw new UnsupportedOperationException("Cassandra is not supported yet");
+ public ListenableFuture> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) {
+ log.debug("Try to find assets by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink);
+ ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.ASSET, pageLink);
+ return Futures.transformAsync(relations, input -> {
+ List> assetFutures = new ArrayList<>(input.size());
+ for (EntityRelation relation : input) {
+ assetFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId()));
+ }
+ return Futures.successfulAsList(assetFutures);
+ }, MoreExecutors.directExecutor());
}
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
index 3658dc22e8..ae3f787e5a 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
@@ -162,16 +162,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
}
}
- private void deleteRelation(TenantId tenantId, EntityRelation dashboardRelation) throws ExecutionException, InterruptedException {
- log.debug("Deleting Dashboard relation: {}", dashboardRelation);
- relationService.deleteRelationAsync(tenantId, dashboardRelation).get();
- }
-
- private void createRelation(TenantId tenantId, EntityRelation dashboardRelation) throws ExecutionException, InterruptedException {
- log.debug("Creating Dashboard relation: {}", dashboardRelation);
- relationService.saveRelationAsync(tenantId, dashboardRelation).get();
- }
-
@Override
public void deleteDashboard(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing deleteDashboard [{}]", dashboardId);
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java
index 513b313aa0..41d0ff8d2c 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java
@@ -25,16 +25,22 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.page.TimePageLink;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.EntitySubtypeEntity;
import org.thingsboard.server.dao.model.nosql.DeviceEntity;
import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao;
+import org.thingsboard.server.dao.relation.RelationDao;
import org.thingsboard.server.dao.util.NoSqlDao;
import javax.annotation.Nullable;
@@ -68,6 +74,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY;
@NoSqlDao
public class CassandraDeviceDao extends CassandraAbstractSearchTextDao implements DeviceDao {
+ @Autowired
+ private RelationDao relationDao;
+
@Override
protected Class getColumnFamilyClass() {
return DeviceEntity.class;
@@ -190,30 +199,17 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink) {
-// log.debug("Try to find devices by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink);
-// List deviceEntities = findPageWithTextSearch(new TenantId(tenantId), DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
-// Arrays.asList(eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId),
-// eq(DEVICE_TENANT_ID_PROPERTY, tenantId)),
-// pageLink);
-//
-// log.trace("Found devices [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", deviceEntities, tenantId, customerId, pageLink);
-// return DaoUtil.convertDataList(deviceEntities);
- throw new UnsupportedOperationException("Cassandra is not supported yet");
+ public ListenableFuture> findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) {
+ log.debug("Try to find devices by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink);
+ ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.DEVICE, pageLink);
+ return Futures.transformAsync(relations, input -> {
+ List> deviceFutures = new ArrayList<>(input.size());
+ for (EntityRelation relation : input) {
+ deviceFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId()));
+ }
+ return Futures.successfulAsList(deviceFutures);
+ }, MoreExecutors.directExecutor());
}
- @Override
- public List findDevicesByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink) {
-// log.debug("Try to find devices by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink);
-// List deviceEntities = findPageWithTextSearch(new TenantId(tenantId), DEVICE_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
-// Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type),
-// eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId),
-// eq(DEVICE_TENANT_ID_PROPERTY, tenantId)),
-// pageLink);
-//
-// log.trace("Found devices [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", deviceEntities, tenantId, customerId, type, pageLink);
-// return DaoUtil.convertDataList(deviceEntities);
- throw new UnsupportedOperationException("Cassandra is not supported yet");
- }
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
index 856243a4a5..d6687b7278 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
@@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.Dao;
import java.util.List;
@@ -124,16 +125,5 @@ public interface DeviceDao extends Dao {
* @param pageLink the page link
* @return the list of device objects
*/
- List findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink);
-
- /**
- * Find devices by tenantId, edgeId, type and page link.
- *
- * @param tenantId the tenantId
- * @param edgeId the edgeId
- * @param type the type
- * @param pageLink the page link
- * @return the list of device objects
- */
- List findDevicesByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink);
+ ListenableFuture> findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink);
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
index cb151bcd97..c8f24262a1 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
@@ -41,19 +41,26 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.page.TimePageData;
+import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
+import org.thingsboard.server.common.data.relation.RelationTypeGroup;
+import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.customer.CustomerDao;
+import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
+import org.thingsboard.server.dao.service.Validator;
import org.thingsboard.server.dao.tenant.TenantDao;
import javax.annotation.Nullable;
@@ -98,6 +105,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
@Autowired
private EntityViewService entityViewService;
+ @Autowired
+ private EdgeService edgeService;
+
@Autowired
private CacheManager cacheManager;
@@ -324,36 +334,52 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
@Override
public Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId) {
Device device = findDeviceById(tenantId, deviceId);
- device.setEdgeId(edgeId);
- return saveDevice(device);
+ Edge edge = edgeService.findEdgeById(tenantId, edgeId);
+ if (edge == null) {
+ throw new DataValidationException("Can't assign device to non-existent edge!");
+ }
+ if (!edge.getTenantId().getId().equals(device.getTenantId().getId())) {
+ throw new DataValidationException("Can't assign device to edge from different tenant!");
+ }
+ try {
+ createRelation(tenantId, new EntityRelation(edgeId, deviceId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
+ } catch (ExecutionException | InterruptedException e) {
+ log.warn("[{}] Failed to create device relation. Edge Id: [{}]", deviceId, edgeId);
+ throw new RuntimeException(e);
+ }
+ return device;
}
@Override
- public Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId) {
+ public Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId) {
Device device = findDeviceById(tenantId, deviceId);
- device.setEdgeId(null);
- return saveDevice(device);
+ Edge edge = edgeService.findEdgeById(tenantId, edgeId);
+ if (edge == null) {
+ throw new DataValidationException("Can't unassign device from non-existent edge!");
+ }
+ try {
+ deleteRelation(tenantId, new EntityRelation(edgeId, deviceId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
+ } catch (ExecutionException | InterruptedException e) {
+ log.warn("[{}] Failed to delete device relation. Edge Id: [{}]", deviceId, edgeId);
+ throw new RuntimeException(e);
+ }
+ return device;
}
@Override
- public TextPageData findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink) {
+ public ListenableFuture> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink);
- List devices = deviceDao.findDevicesByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
- return new TextPageData<>(devices, pageLink);
- }
-
- @Override
- public TextPageData findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink) {
- log.trace("Executing findDevicesByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}], pageLink [{}]", tenantId, edgeId, type, pageLink);
- validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
- validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
- validateString(type, "Incorrect type " + type);
- validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink);
- List devices = deviceDao.findDevicesByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink);
- return new TextPageData<>(devices, pageLink);
+ ListenableFuture> devices = deviceDao.findDevicesByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
+ return Futures.transform(devices, new Function, TimePageData>() {
+ @Nullable
+ @Override
+ public TimePageData apply(@Nullable List devices) {
+ return new TimePageData<>(devices, pageLink);
+ }
+ }, MoreExecutors.directExecutor());
}
private DataValidator deviceValidator =
diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java
index 835917b153..fbe44c2267 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java
@@ -110,26 +110,26 @@ public class CassandraEdgeDao extends CassandraAbstractSearchTextDao> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId, TimePageLink pageLink) {
- log.debug("Try to find edges by tenantId [{}], ruleChainId [{}] and pageLink [{}]", tenantId, ruleChainId, pageLink);
+ public ListenableFuture> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId) {
+ log.debug("Try to find edges by tenantId [{}], ruleChainId [{}]", tenantId, ruleChainId);
ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new RuleChainId(ruleChainId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
return Futures.transformAsync(relations, input -> {
List> edgeFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) {
- edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId()));
+ edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getFrom().getId()));
}
return Futures.successfulAsList(edgeFutures);
}, MoreExecutors.directExecutor());
}
@Override
- public ListenableFuture> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId, TimePageLink pageLink) {
- log.debug("Try to find edges by tenantId [{}], dashboardId [{}] and pageLink [{}]", tenantId, dashboardId, pageLink);
+ public ListenableFuture> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId) {
+ log.debug("Try to find edges by tenantId [{}], dashboardId [{}]", tenantId, dashboardId);
ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new DashboardId(dashboardId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
return Futures.transformAsync(relations, input -> {
List> edgeFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) {
- edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId()));
+ edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getFrom().getId()));
}
return Futures.successfulAsList(edgeFutures);
}, MoreExecutors.directExecutor());
diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java
index adc844f14e..f9e561b0f4 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java
@@ -127,22 +127,20 @@ public interface EdgeDao extends Dao {
Optional findByRoutingKey(UUID tenantId, String routingKey);
/**
- * Find edges by tenantId, ruleChainId and page link.
+ * Find edges by tenantId and ruleChainId.
*
* @param tenantId the tenantId
* @param ruleChainId the ruleChainId
- * @param pageLink the page link
* @return the list of rule chain objects
*/
- ListenableFuture> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId, TimePageLink pageLink);
+ ListenableFuture> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId);
/**
- * Find edges by tenantId, dashboardId and page link.
+ * Find edges by tenantId and dashboardId.
*
* @param tenantId the tenantId
* @param dashboardId the dashboardId
- * @param pageLink the page link
* @return the list of rule chain objects
*/
- ListenableFuture> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId, TimePageLink pageLink);
+ ListenableFuture> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId);
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java
index 31cbe1c765..c7b1d87ab9 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java
@@ -62,6 +62,7 @@ import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
+import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainType;
@@ -75,6 +76,7 @@ import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.exception.DataValidationException;
+import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
@@ -144,6 +146,9 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
@Autowired
private EntityViewService entityViewService;
+ @Autowired
+ private RelationService relationService;
+
private ExecutorService tsCallBackExecutor;
@PostConstruct
@@ -219,7 +224,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
dashboardService.unassignEdgeDashboards(tenantId, edgeId);
// TODO: validate that rule chains are removed by deleteEntityRelations(tenantId, edgeId); call
- ruleChainService.unassignEdgeRuleChains(tenantId, edgeId);
+ ruleChainService.unassignEdgeRuleChains(tenantId, edgeId);
List