Commit 3b1e1416fba029c9f768cc6f539b8ad58dfdbd20

Authored by deaflynx
2 parents 36997c5c 3d3dd887

Merge with 2.5.1

Showing 88 changed files with 1353 additions and 1225 deletions

Too many changes to show.

To preserve performance only 88 of 337 files are displayed.

... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.0-SNAPSHOT</version>
  23 + <version>2.5.1-SNAPSHOT</version>
24 24 <artifactId>thingsboard</artifactId>
25 25 </parent>
26 26 <artifactId>application</artifactId>
... ... @@ -34,10 +34,15 @@
34 34 <properties>
35 35 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
36 36 <main.dir>${basedir}/..</main.dir>
  37 + <pkg.type>java</pkg.type>
  38 + <pkg.disabled>false</pkg.disabled>
  39 + <pkg.process-resources.phase>process-resources</pkg.process-resources.phase>
  40 + <pkg.package.phase>package</pkg.package.phase>
37 41 <pkg.name>thingsboard</pkg.name>
38   - <pkg.unixLogFolder>/var/log/${pkg.name}</pkg.unixLogFolder>
39   - <pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
40 42 <pkg.win.dist>${project.build.directory}/windows</pkg.win.dist>
  43 + <pkg.copyInstallScripts>true</pkg.copyInstallScripts>
  44 + <pkg.implementationTitle>ThingsBoard</pkg.implementationTitle>
  45 + <pkg.mainClass>org.thingsboard.server.ThingsboardServerApplication</pkg.mainClass>
41 46 </properties>
42 47
43 48 <dependencies>
... ... @@ -313,6 +318,10 @@
313 318 <plugins>
314 319 <plugin>
315 320 <groupId>org.apache.maven.plugins</groupId>
  321 + <artifactId>maven-compiler-plugin</artifactId>
  322 + </plugin>
  323 + <plugin>
  324 + <groupId>org.apache.maven.plugins</groupId>
316 325 <artifactId>maven-surefire-plugin</artifactId>
317 326 <version>${surfire.version}</version>
318 327 <configuration>
... ... @@ -327,313 +336,30 @@
327 336 <plugin>
328 337 <groupId>org.apache.maven.plugins</groupId>
329 338 <artifactId>maven-resources-plugin</artifactId>
330   - <executions>
331   - <execution>
332   - <id>copy-conf</id>
333   - <phase>process-resources</phase>
334   - <goals>
335   - <goal>copy-resources</goal>
336   - </goals>
337   - <configuration>
338   - <outputDirectory>${project.build.directory}/conf</outputDirectory>
339   - <resources>
340   - <resource>
341   - <directory>src/main/resources</directory>
342   - <excludes>
343   - <exclude>logback.xml</exclude>
344   - </excludes>
345   - <filtering>false</filtering>
346   - </resource>
347   - </resources>
348   - </configuration>
349   - </execution>
350   - <execution>
351   - <id>copy-service-conf</id>
352   - <phase>process-resources</phase>
353   - <goals>
354   - <goal>copy-resources</goal>
355   - </goals>
356   - <configuration>
357   - <outputDirectory>${project.build.directory}/conf</outputDirectory>
358   - <resources>
359   - <resource>
360   - <directory>src/main/conf</directory>
361   - <filtering>true</filtering>
362   - </resource>
363   - </resources>
364   - <filters>
365   - <filter>src/main/filters/unix.properties</filter>
366   - </filters>
367   - </configuration>
368   - </execution>
369   - <execution>
370   - <id>copy-win-conf</id>
371   - <phase>process-resources</phase>
372   - <goals>
373   - <goal>copy-resources</goal>
374   - </goals>
375   - <configuration>
376   - <outputDirectory>${pkg.win.dist}/conf</outputDirectory>
377   - <resources>
378   - <resource>
379   - <directory>src/main/resources</directory>
380   - <excludes>
381   - <exclude>logback.xml</exclude>
382   - </excludes>
383   - <filtering>false</filtering>
384   - </resource>
385   - <resource>
386   - <directory>src/main/conf</directory>
387   - <excludes>
388   - <exclude>thingsboard.conf</exclude>
389   - </excludes>
390   - <filtering>true</filtering>
391   - </resource>
392   - </resources>
393   - <filters>
394   - <filter>src/main/filters/windows.properties</filter>
395   - </filters>
396   - </configuration>
397   - </execution>
398   - <execution>
399   - <id>copy-control</id>
400   - <phase>process-resources</phase>
401   - <goals>
402   - <goal>copy-resources</goal>
403   - </goals>
404   - <configuration>
405   - <outputDirectory>${project.build.directory}/control</outputDirectory>
406   - <resources>
407   - <resource>
408   - <directory>src/main/scripts/control</directory>
409   - <filtering>true</filtering>
410   - </resource>
411   - </resources>
412   - <filters>
413   - <filter>src/main/filters/unix.properties</filter>
414   - </filters>
415   - </configuration>
416   - </execution>
417   - <execution>
418   - <id>copy-install</id>
419   - <phase>process-resources</phase>
420   - <goals>
421   - <goal>copy-resources</goal>
422   - </goals>
423   - <configuration>
424   - <outputDirectory>${project.build.directory}/bin/install</outputDirectory>
425   - <resources>
426   - <resource>
427   - <directory>src/main/scripts/install</directory>
428   - <includes>
429   - <include>**/*.sh</include>
430   - <include>**/*.xml</include>
431   - </includes>
432   - <filtering>true</filtering>
433   - </resource>
434   - </resources>
435   - <filters>
436   - <filter>src/main/filters/unix.properties</filter>
437   - </filters>
438   - </configuration>
439   - </execution>
440   - <execution>
441   - <id>copy-windows-control</id>
442   - <phase>process-resources</phase>
443   - <goals>
444   - <goal>copy-resources</goal>
445   - </goals>
446   - <configuration>
447   - <outputDirectory>${pkg.win.dist}</outputDirectory>
448   - <resources>
449   - <resource>
450   - <directory>src/main/scripts/windows</directory>
451   - <filtering>true</filtering>
452   - </resource>
453   - </resources>
454   - <filters>
455   - <filter>src/main/filters/windows.properties</filter>
456   - </filters>
457   - </configuration>
458   - </execution>
459   - <execution>
460   - <id>copy-windows-install</id>
461   - <phase>process-resources</phase>
462   - <goals>
463   - <goal>copy-resources</goal>
464   - </goals>
465   - <configuration>
466   - <outputDirectory>${pkg.win.dist}/install</outputDirectory>
467   - <resources>
468   - <resource>
469   - <directory>src/main/scripts/install</directory>
470   - <includes>
471   - <include>logback.xml</include>
472   - </includes>
473   - <filtering>true</filtering>
474   - </resource>
475   - </resources>
476   - <filters>
477   - <filter>src/main/filters/windows.properties</filter>
478   - </filters>
479   - </configuration>
480   - </execution>
481   - <execution>
482   - <id>copy-data</id>
483   - <phase>process-resources</phase>
484   - <goals>
485   - <goal>copy-resources</goal>
486   - </goals>
487   - <configuration>
488   - <outputDirectory>${project.build.directory}/data</outputDirectory>
489   - <resources>
490   - <resource>
491   - <directory>src/main/data</directory>
492   - </resource>
493   - <resource>
494   - <directory>../dao/src/main/resources</directory>
495   - <includes>
496   - <include>**/*.cql</include>
497   - <include>**/*.sql</include>
498   - </includes>
499   - <filtering>false</filtering>
500   - </resource>
501   - </resources>
502   - </configuration>
503   - </execution>
504   - </executions>
505 339 </plugin>
506 340 <plugin>
507 341 <groupId>org.apache.maven.plugins</groupId>
508 342 <artifactId>maven-dependency-plugin</artifactId>
509   - <executions>
510   - <execution>
511   - <id>copy-winsw-service</id>
512   - <phase>package</phase>
513   - <goals>
514   - <goal>copy</goal>
515   - </goals>
516   - <configuration>
517   - <artifactItems>
518   - <artifactItem>
519   - <groupId>com.sun.winsw</groupId>
520   - <artifactId>winsw</artifactId>
521   - <classifier>bin</classifier>
522   - <type>exe</type>
523   - <destFileName>service.exe</destFileName>
524   - </artifactItem>
525   - </artifactItems>
526   - <outputDirectory>${pkg.win.dist}</outputDirectory>
527   - </configuration>
528   - </execution>
529   - </executions>
530 343 </plugin>
531 344 <plugin>
532 345 <groupId>org.apache.maven.plugins</groupId>
533 346 <artifactId>maven-jar-plugin</artifactId>
534   - <configuration>
535   - <excludes>
536   - <exclude>**/logback.xml</exclude>
537   - </excludes>
538   - <archive>
539   - <manifestEntries>
540   - <Implementation-Title>ThingsBoard</Implementation-Title>
541   - <Implementation-Version>${project.version}</Implementation-Version>
542   - </manifestEntries>
543   - </archive>
544   - </configuration>
545 347 </plugin>
546 348 <plugin>
547 349 <groupId>org.springframework.boot</groupId>
548 350 <artifactId>spring-boot-maven-plugin</artifactId>
549   - <configuration>
550   - <mainClass>org.thingsboard.server.ThingsboardServerApplication</mainClass>
551   - <classifier>boot</classifier>
552   - <layout>ZIP</layout>
553   - <executable>true</executable>
554   - <excludeDevtools>true</excludeDevtools>
555   - <embeddedLaunchScriptProperties>
556   - <confFolder>${pkg.installFolder}/conf</confFolder>
557   - <logFolder>${pkg.unixLogFolder}</logFolder>
558   - <logFilename>${pkg.name}.out</logFilename>
559   - <initInfoProvides>${pkg.name}</initInfoProvides>
560   - </embeddedLaunchScriptProperties>
561   - </configuration>
562   - <executions>
563   - <execution>
564   - <goals>
565   - <goal>repackage</goal>
566   - </goals>
567   - </execution>
568   - </executions>
569 351 </plugin>
570 352 <plugin>
571 353 <groupId>org.thingsboard</groupId>
572 354 <artifactId>gradle-maven-plugin</artifactId>
573   - <configuration>
574   - <tasks>
575   - <task>build</task>
576   - <task>buildDeb</task>
577   - <task>buildRpm</task>
578   - </tasks>
579   - <args>
580   - <arg>-PprojectBuildDir=${project.build.directory}</arg>
581   - <arg>-PprojectVersion=${project.version}</arg>
582   - <arg>-PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}
583   - </arg>
584   - <arg>-PpkgName=${pkg.name}</arg>
585   - <arg>-PpkgInstallFolder=${pkg.installFolder}</arg>
586   - <arg>-PpkgLogFolder=${pkg.unixLogFolder}</arg>
587   - </args>
588   - </configuration>
589   - <executions>
590   - <execution>
591   - <phase>package</phase>
592   - <goals>
593   - <goal>invoke</goal>
594   - </goals>
595   - </execution>
596   - </executions>
597 355 </plugin>
598 356 <plugin>
599 357 <groupId>org.apache.maven.plugins</groupId>
600 358 <artifactId>maven-assembly-plugin</artifactId>
601   - <configuration>
602   - <finalName>${pkg.name}</finalName>
603   - <descriptors>
604   - <descriptor>src/main/assembly/windows.xml</descriptor>
605   - </descriptors>
606   - </configuration>
607   - <executions>
608   - <execution>
609   - <id>assembly</id>
610   - <phase>package</phase>
611   - <goals>
612   - <goal>single</goal>
613   - </goals>
614   - </execution>
615   - </executions>
616 359 </plugin>
617 360 <plugin>
618 361 <groupId>org.apache.maven.plugins</groupId>
619 362 <artifactId>maven-install-plugin</artifactId>
620   - <configuration>
621   - <file>${project.build.directory}/${pkg.name}.deb</file>
622   - <artifactId>${project.artifactId}</artifactId>
623   - <groupId>${project.groupId}</groupId>
624   - <version>${project.version}</version>
625   - <classifier>deb</classifier>
626   - <packaging>deb</packaging>
627   - </configuration>
628   - <executions>
629   - <execution>
630   - <id>install-deb</id>
631   - <phase>package</phase>
632   - <goals>
633   - <goal>install-file</goal>
634   - </goals>
635   - </execution>
636   - </executions>
637 363 </plugin>
638 364 <plugin>
639 365 <groupId>org.xolstice.maven.plugins</groupId>
... ...
... ... @@ -16,50 +16,85 @@
16 16
17 17 -- call create_partition_ts_kv_table();
18 18
19   -CREATE OR REPLACE PROCEDURE create_partition_ts_kv_table() LANGUAGE plpgsql AS $$
  19 +CREATE OR REPLACE PROCEDURE create_partition_ts_kv_table()
  20 + LANGUAGE plpgsql AS
  21 +$$
20 22
21 23 BEGIN
22   - ALTER TABLE ts_kv
23   - RENAME TO ts_kv_old;
24   - ALTER TABLE ts_kv_old
25   - RENAME CONSTRAINT ts_kv_pkey TO ts_kv_pkey_old;
26   - CREATE TABLE IF NOT EXISTS ts_kv
27   - (
28   - LIKE ts_kv_old
29   - )
30   - PARTITION BY RANGE (ts);
31   - ALTER TABLE ts_kv
32   - DROP COLUMN entity_type;
33   - ALTER TABLE ts_kv
34   - ALTER COLUMN entity_id TYPE uuid USING entity_id::uuid;
35   - ALTER TABLE ts_kv
36   - ALTER COLUMN key TYPE integer USING key::integer;
37   - ALTER TABLE ts_kv
38   - ADD CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts);
  24 + ALTER TABLE ts_kv
  25 + DROP CONSTRAINT IF EXISTS ts_kv_unq_key;
  26 + ALTER TABLE ts_kv
  27 + DROP CONSTRAINT IF EXISTS ts_kv_pkey;
  28 + ALTER TABLE ts_kv
  29 + ADD CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_type, entity_id, key, ts);
  30 + ALTER TABLE ts_kv
  31 + RENAME TO ts_kv_old;
  32 + ALTER TABLE ts_kv_old
  33 + RENAME CONSTRAINT ts_kv_pkey TO ts_kv_pkey_old;
  34 + CREATE TABLE IF NOT EXISTS ts_kv
  35 + (
  36 + LIKE ts_kv_old
  37 + )
  38 + PARTITION BY RANGE (ts);
  39 + ALTER TABLE ts_kv
  40 + DROP COLUMN entity_type;
  41 + ALTER TABLE ts_kv
  42 + ALTER COLUMN entity_id TYPE uuid USING entity_id::uuid;
  43 + ALTER TABLE ts_kv
  44 + ALTER COLUMN key TYPE integer USING key::integer;
  45 + ALTER TABLE ts_kv
  46 + ADD CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts);
  47 + CREATE TABLE IF NOT EXISTS ts_kv_indefinite PARTITION OF ts_kv DEFAULT;
39 48 END;
40 49 $$;
41 50
42 51 -- call create_new_ts_kv_latest_table();
43 52
44   -CREATE OR REPLACE PROCEDURE create_new_ts_kv_latest_table() LANGUAGE plpgsql AS $$
  53 +CREATE OR REPLACE PROCEDURE create_new_ts_kv_latest_table()
  54 + LANGUAGE plpgsql AS
  55 +$$
45 56
46 57 BEGIN
47   - ALTER TABLE ts_kv_latest
48   - RENAME TO ts_kv_latest_old;
49   - ALTER TABLE ts_kv_latest_old
50   - RENAME CONSTRAINT ts_kv_latest_pkey TO ts_kv_latest_pkey_old;
51   - CREATE TABLE IF NOT EXISTS ts_kv_latest
52   - (
53   - LIKE ts_kv_latest_old
54   - );
55   - ALTER TABLE ts_kv_latest
56   - DROP COLUMN entity_type;
57   - ALTER TABLE ts_kv_latest
58   - ALTER COLUMN entity_id TYPE uuid USING entity_id::uuid;
59   - ALTER TABLE ts_kv_latest
60   - ALTER COLUMN key TYPE integer USING key::integer;
61   - ALTER TABLE ts_kv_latest
62   - ADD CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key);
  58 + IF NOT EXISTS(SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = 'ts_kv_latest_old') THEN
  59 + ALTER TABLE ts_kv_latest
  60 + DROP CONSTRAINT IF EXISTS ts_kv_latest_unq_key;
  61 + ALTER TABLE ts_kv_latest
  62 + DROP CONSTRAINT IF EXISTS ts_kv_latest_pkey;
  63 + ALTER TABLE ts_kv_latest
  64 + ADD CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_type, entity_id, key);
  65 + ALTER TABLE ts_kv_latest
  66 + RENAME TO ts_kv_latest_old;
  67 + ALTER TABLE ts_kv_latest_old
  68 + RENAME CONSTRAINT ts_kv_latest_pkey TO ts_kv_latest_pkey_old;
  69 + CREATE TABLE IF NOT EXISTS ts_kv_latest
  70 + (
  71 + LIKE ts_kv_latest_old
  72 + );
  73 + ALTER TABLE ts_kv_latest
  74 + DROP COLUMN entity_type;
  75 + ALTER TABLE ts_kv_latest
  76 + ALTER COLUMN entity_id TYPE uuid USING entity_id::uuid;
  77 + ALTER TABLE ts_kv_latest
  78 + ALTER COLUMN key TYPE integer USING key::integer;
  79 + ALTER TABLE ts_kv_latest
  80 + ADD CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key);
  81 + ELSE
  82 + RAISE NOTICE 'ts_kv_latest_old table already exists!';
  83 + IF NOT EXISTS(SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = 'ts_kv_latest') THEN
  84 + CREATE TABLE IF NOT EXISTS ts_kv_latest
  85 + (
  86 + entity_id uuid NOT NULL,
  87 + key int NOT NULL,
  88 + ts bigint NOT NULL,
  89 + bool_v boolean,
  90 + str_v varchar(10000000),
  91 + long_v bigint,
  92 + dbl_v double precision,
  93 + json_v json,
  94 + CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)
  95 + );
  96 + END IF;
  97 + END IF;
63 98 END;
64 99 $$;
65 100
... ... @@ -93,8 +128,9 @@ BEGIN
93 128 RETURN QUERY SELECT SUBSTRING(year_date.year, 1, 4) AS partition_date,
94 129 (extract(epoch from (year_date.year)::timestamp) * 1000)::bigint AS from_ts,
95 130 (extract(epoch from (year_date.year::date + INTERVAL '1 YEAR')::timestamp) *
96   - 1000)::bigint AS to_ts
97   - FROM (SELECT DISTINCT TO_CHAR(TO_TIMESTAMP(ts / 1000), 'YYYY_01_01') AS year FROM ts_kv_old) AS year_date;
  131 + 1000)::bigint AS to_ts
  132 + FROM (SELECT DISTINCT TO_CHAR(TO_TIMESTAMP(ts / 1000), 'YYYY_01_01') AS year
  133 + FROM ts_kv_old) AS year_date;
98 134 ELSE
99 135 RAISE EXCEPTION 'Failed to parse partitioning property: % !', partition_type;
100 136 END CASE;
... ... @@ -103,13 +139,16 @@ $$ LANGUAGE plpgsql;
103 139
104 140 -- call create_partitions();
105 141
106   -CREATE OR REPLACE PROCEDURE create_partitions(IN partition_type varchar) LANGUAGE plpgsql AS $$
  142 +CREATE OR REPLACE PROCEDURE create_partitions(IN partition_type varchar)
  143 + LANGUAGE plpgsql AS
  144 +$$
107 145
108 146 DECLARE
109 147 partition_date varchar;
110 148 from_ts bigint;
111 149 to_ts bigint;
112   - partitions_cursor CURSOR FOR SELECT * FROM get_partitions_data(partition_type);
  150 + partitions_cursor CURSOR FOR SELECT *
  151 + FROM get_partitions_data(partition_type);
113 152 BEGIN
114 153 OPEN partitions_cursor;
115 154 LOOP
... ... @@ -127,21 +166,25 @@ $$;
127 166
128 167 -- call create_ts_kv_dictionary_table();
129 168
130   -CREATE OR REPLACE PROCEDURE create_ts_kv_dictionary_table() LANGUAGE plpgsql AS $$
  169 +CREATE OR REPLACE PROCEDURE create_ts_kv_dictionary_table()
  170 + LANGUAGE plpgsql AS
  171 +$$
131 172
132 173 BEGIN
133   - CREATE TABLE IF NOT EXISTS ts_kv_dictionary
134   - (
135   - key varchar(255) NOT NULL,
136   - key_id serial UNIQUE,
137   - CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
138   - );
  174 + CREATE TABLE IF NOT EXISTS ts_kv_dictionary
  175 + (
  176 + key varchar(255) NOT NULL,
  177 + key_id serial UNIQUE,
  178 + CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
  179 + );
139 180 END;
140 181 $$;
141 182
142 183 -- call insert_into_dictionary();
143 184
144   -CREATE OR REPLACE PROCEDURE insert_into_dictionary() LANGUAGE plpgsql AS $$
  185 +CREATE OR REPLACE PROCEDURE insert_into_dictionary()
  186 + LANGUAGE plpgsql AS
  187 +$$
145 188
146 189 DECLARE
147 190 insert_record RECORD;
... ... @@ -164,31 +207,89 @@ BEGIN
164 207 END;
165 208 $$;
166 209
167   --- call insert_into_ts_kv();
  210 +CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS
  211 +$$
  212 +BEGIN
  213 + uuid_id := substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) ||
  214 + '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12);
  215 +END;
  216 +$$ LANGUAGE plpgsql;
168 217
169   -CREATE OR REPLACE PROCEDURE insert_into_ts_kv() LANGUAGE plpgsql AS $$
170   -DECLARE
171   - insert_size CONSTANT integer := 10000;
172   - insert_counter integer DEFAULT 0;
173   - insert_record RECORD;
174   - 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,
  218 +CREATE OR REPLACE PROCEDURE insert_into_ts_kv(IN path_to_file varchar)
  219 + LANGUAGE plpgsql AS
  220 +$$
  221 +BEGIN
  222 + EXECUTE format('COPY (SELECT to_uuid(entity_id) AS entity_id,
175 223 ts_kv_records.key AS key,
176 224 ts_kv_records.ts AS ts,
177 225 ts_kv_records.bool_v AS bool_v,
178 226 ts_kv_records.str_v AS str_v,
179 227 ts_kv_records.long_v AS long_v,
180 228 ts_kv_records.dbl_v AS dbl_v
181   - FROM (SELECT SUBSTRING(entity_id, 8, 8) AS entity_id_uuid_first_part,
182   - SUBSTRING(entity_id, 4, 4) AS entity_id_uuid_second_part,
183   - SUBSTRING(entity_id, 1, 3) AS entity_id_uuid_third_part,
184   - SUBSTRING(entity_id, 16, 4) AS entity_id_uuid_fourth_part,
185   - SUBSTRING(entity_id, 20) AS entity_id_uuid_fifth_part,
  229 + FROM (SELECT entity_id AS entity_id,
  230 + key_id AS key,
  231 + ts,
  232 + bool_v,
  233 + str_v,
  234 + long_v,
  235 + dbl_v
  236 + FROM ts_kv_old
  237 + INNER JOIN ts_kv_dictionary ON (ts_kv_old.key = ts_kv_dictionary.key)) AS ts_kv_records) TO %L;',
  238 + path_to_file);
  239 + EXECUTE format('COPY ts_kv FROM %L', path_to_file);
  240 +END
  241 +$$;
  242 +
  243 +-- call insert_into_ts_kv_latest();
  244 +
  245 +CREATE OR REPLACE PROCEDURE insert_into_ts_kv_latest(IN path_to_file varchar)
  246 + LANGUAGE plpgsql AS
  247 +$$
  248 +BEGIN
  249 + EXECUTE format('COPY (SELECT to_uuid(entity_id) AS entity_id,
  250 + ts_kv_latest_records.key AS key,
  251 + ts_kv_latest_records.ts AS ts,
  252 + ts_kv_latest_records.bool_v AS bool_v,
  253 + ts_kv_latest_records.str_v AS str_v,
  254 + ts_kv_latest_records.long_v AS long_v,
  255 + ts_kv_latest_records.dbl_v AS dbl_v
  256 + FROM (SELECT entity_id AS entity_id,
186 257 key_id AS key,
187 258 ts,
188 259 bool_v,
189 260 str_v,
190 261 long_v,
191 262 dbl_v
  263 + FROM ts_kv_latest_old
  264 + INNER JOIN ts_kv_dictionary ON (ts_kv_latest_old.key = ts_kv_dictionary.key)) AS ts_kv_latest_records) TO %L;',
  265 + path_to_file);
  266 + EXECUTE format('COPY ts_kv_latest FROM %L', path_to_file);
  267 +END;
  268 +$$;
  269 +
  270 +-- call insert_into_ts_kv_cursor();
  271 +
  272 +CREATE OR REPLACE PROCEDURE insert_into_ts_kv_cursor()
  273 + LANGUAGE plpgsql AS
  274 +$$
  275 +DECLARE
  276 + insert_size CONSTANT integer := 10000;
  277 + insert_counter integer DEFAULT 0;
  278 + insert_record RECORD;
  279 + insert_cursor CURSOR FOR SELECT to_uuid(entity_id) AS entity_id,
  280 + ts_kv_records.key AS key,
  281 + ts_kv_records.ts AS ts,
  282 + ts_kv_records.bool_v AS bool_v,
  283 + ts_kv_records.str_v AS str_v,
  284 + ts_kv_records.long_v AS long_v,
  285 + ts_kv_records.dbl_v AS dbl_v
  286 + FROM (SELECT entity_id AS entity_id,
  287 + key_id AS key,
  288 + ts,
  289 + bool_v,
  290 + str_v,
  291 + long_v,
  292 + dbl_v
192 293 FROM ts_kv_old
193 294 INNER JOIN ts_kv_dictionary ON (ts_kv_old.key = ts_kv_dictionary.key)) AS ts_kv_records;
194 295 BEGIN
... ... @@ -211,26 +312,24 @@ BEGIN
211 312 END;
212 313 $$;
213 314
214   --- call insert_into_ts_kv_latest();
  315 +-- call insert_into_ts_kv_latest_cursor();
215 316
216   -CREATE OR REPLACE PROCEDURE insert_into_ts_kv_latest() LANGUAGE plpgsql AS $$
  317 +CREATE OR REPLACE PROCEDURE insert_into_ts_kv_latest_cursor()
  318 + LANGUAGE plpgsql AS
  319 +$$
217 320 DECLARE
218 321 insert_size CONSTANT integer := 10000;
219 322 insert_counter integer DEFAULT 0;
220 323 insert_record RECORD;
221   - 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,
222   - ts_kv_latest_records.key AS key,
223   - ts_kv_latest_records.ts AS ts,
224   - ts_kv_latest_records.bool_v AS bool_v,
225   - ts_kv_latest_records.str_v AS str_v,
226   - ts_kv_latest_records.long_v AS long_v,
227   - ts_kv_latest_records.dbl_v AS dbl_v
228   - FROM (SELECT SUBSTRING(entity_id, 8, 8) AS entity_id_uuid_first_part,
229   - SUBSTRING(entity_id, 4, 4) AS entity_id_uuid_second_part,
230   - SUBSTRING(entity_id, 1, 3) AS entity_id_uuid_third_part,
231   - SUBSTRING(entity_id, 16, 4) AS entity_id_uuid_fourth_part,
232   - SUBSTRING(entity_id, 20) AS entity_id_uuid_fifth_part,
233   - key_id AS key,
  324 + insert_cursor CURSOR FOR SELECT to_uuid(entity_id) AS entity_id,
  325 + ts_kv_latest_records.key AS key,
  326 + ts_kv_latest_records.ts AS ts,
  327 + ts_kv_latest_records.bool_v AS bool_v,
  328 + ts_kv_latest_records.str_v AS str_v,
  329 + ts_kv_latest_records.long_v AS long_v,
  330 + ts_kv_latest_records.dbl_v AS dbl_v
  331 + FROM (SELECT entity_id AS entity_id,
  332 + key_id AS key,
234 333 ts,
235 334 bool_v,
236 335 str_v,
... ... @@ -258,4 +357,3 @@ BEGIN
258 357 END;
259 358 $$;
260 359
261   -
... ...
... ... @@ -96,51 +96,36 @@ BEGIN
96 96 END;
97 97 $$;
98 98
99   --- call insert_into_ts_kv();
  99 +CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS
  100 +$$
  101 +BEGIN
  102 + uuid_id := substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) ||
  103 + '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12);
  104 +END;
  105 +$$ LANGUAGE plpgsql;
100 106
101   -CREATE OR REPLACE PROCEDURE insert_into_ts_kv() LANGUAGE plpgsql AS $$
  107 +-- call insert_into_ts_kv();
102 108
103   -DECLARE
104   - insert_size CONSTANT integer := 10000;
105   - insert_counter integer DEFAULT 0;
106   - insert_record RECORD;
107   - 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,
108   - new_ts_kv_records.key AS key,
109   - new_ts_kv_records.ts AS ts,
110   - new_ts_kv_records.bool_v AS bool_v,
111   - new_ts_kv_records.str_v AS str_v,
112   - new_ts_kv_records.long_v AS long_v,
113   - new_ts_kv_records.dbl_v AS dbl_v
114   - FROM (SELECT SUBSTRING(entity_id, 8, 8) AS entity_id_uuid_first_part,
115   - SUBSTRING(entity_id, 4, 4) AS entity_id_uuid_second_part,
116   - SUBSTRING(entity_id, 1, 3) AS entity_id_uuid_third_part,
117   - SUBSTRING(entity_id, 16, 4) AS entity_id_uuid_fourth_part,
118   - SUBSTRING(entity_id, 20) AS entity_id_uuid_fifth_part,
119   - key_id AS key,
120   - ts,
121   - bool_v,
122   - str_v,
123   - long_v,
124   - dbl_v
125   - FROM tenant_ts_kv_old
126   - INNER JOIN ts_kv_dictionary ON (tenant_ts_kv_old.key = ts_kv_dictionary.key)) AS new_ts_kv_records;
  109 +CREATE OR REPLACE PROCEDURE insert_into_ts_kv(IN path_to_file varchar) LANGUAGE plpgsql AS $$
127 110 BEGIN
128   - OPEN insert_cursor;
129   - LOOP
130   - insert_counter := insert_counter + 1;
131   - FETCH insert_cursor INTO insert_record;
132   - IF NOT FOUND THEN
133   - RAISE NOTICE '% records have been inserted into the new ts_kv table!',insert_counter - 1;
134   - EXIT;
135   - END IF;
136   - INSERT INTO ts_kv(entity_id, key, ts, bool_v, str_v, long_v, dbl_v)
137   - VALUES (insert_record.entity_id, insert_record.key, insert_record.ts, insert_record.bool_v, insert_record.str_v,
138   - insert_record.long_v, insert_record.dbl_v);
139   - IF MOD(insert_counter, insert_size) = 0 THEN
140   - RAISE NOTICE '% records have been inserted into the new ts_kv table!',insert_counter;
141   - END IF;
142   - END LOOP;
143   - CLOSE insert_cursor;
  111 +
  112 + EXECUTE format ('COPY (SELECT to_uuid(entity_id) AS entity_id,
  113 + new_ts_kv_records.key AS key,
  114 + new_ts_kv_records.ts AS ts,
  115 + new_ts_kv_records.bool_v AS bool_v,
  116 + new_ts_kv_records.str_v AS str_v,
  117 + new_ts_kv_records.long_v AS long_v,
  118 + new_ts_kv_records.dbl_v AS dbl_v
  119 + FROM (SELECT entity_id AS entity_id,
  120 + key_id AS key,
  121 + ts,
  122 + bool_v,
  123 + str_v,
  124 + long_v,
  125 + dbl_v
  126 + FROM tenant_ts_kv_old
  127 + 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);
  128 + EXECUTE format ('COPY ts_kv FROM %L', path_to_file);
144 129 END;
145 130 $$;
146 131
... ... @@ -177,3 +162,47 @@ BEGIN
177 162 CLOSE insert_cursor;
178 163 END;
179 164 $$;
  165 +
  166 +-- call insert_into_ts_kv_cursor();
  167 +
  168 +CREATE OR REPLACE PROCEDURE insert_into_ts_kv_cursor() LANGUAGE plpgsql AS $$
  169 +
  170 +DECLARE
  171 + insert_size CONSTANT integer := 10000;
  172 + insert_counter integer DEFAULT 0;
  173 + insert_record RECORD;
  174 + insert_cursor CURSOR FOR SELECT to_uuid(entity_id) AS entity_id,
  175 + new_ts_kv_records.key AS key,
  176 + new_ts_kv_records.ts AS ts,
  177 + new_ts_kv_records.bool_v AS bool_v,
  178 + new_ts_kv_records.str_v AS str_v,
  179 + new_ts_kv_records.long_v AS long_v,
  180 + new_ts_kv_records.dbl_v AS dbl_v
  181 + FROM (SELECT entity_id AS entity_id,
  182 + key_id AS key,
  183 + ts,
  184 + bool_v,
  185 + str_v,
  186 + long_v,
  187 + dbl_v
  188 + FROM tenant_ts_kv_old
  189 + INNER JOIN ts_kv_dictionary ON (tenant_ts_kv_old.key = ts_kv_dictionary.key)) AS new_ts_kv_records;
  190 +BEGIN
  191 + OPEN insert_cursor;
  192 + LOOP
  193 + insert_counter := insert_counter + 1;
  194 + FETCH insert_cursor INTO insert_record;
  195 + IF NOT FOUND THEN
  196 + RAISE NOTICE '% records have been inserted into the new ts_kv table!',insert_counter - 1;
  197 + EXIT;
  198 + END IF;
  199 + INSERT INTO ts_kv(entity_id, key, ts, bool_v, str_v, long_v, dbl_v)
  200 + VALUES (insert_record.entity_id, insert_record.key, insert_record.ts, insert_record.bool_v, insert_record.str_v,
  201 + insert_record.long_v, insert_record.dbl_v);
  202 + IF MOD(insert_counter, insert_size) = 0 THEN
  203 + RAISE NOTICE '% records have been inserted into the new ts_kv table!',insert_counter;
  204 + END IF;
  205 + END LOOP;
  206 + CLOSE insert_cursor;
  207 +END;
  208 +$$;
\ No newline at end of file
... ...
... ... @@ -56,6 +56,7 @@ import org.thingsboard.server.dao.audit.AuditLogService;
56 56 import org.thingsboard.server.dao.cassandra.CassandraCluster;
57 57 import org.thingsboard.server.dao.customer.CustomerService;
58 58 import org.thingsboard.server.dao.dashboard.DashboardService;
  59 +import org.thingsboard.server.dao.device.ClaimDevicesService;
59 60 import org.thingsboard.server.dao.device.DeviceService;
60 61 import org.thingsboard.server.dao.edge.EdgeService;
61 62 import org.thingsboard.server.dao.entityview.EntityViewService;
... ... @@ -219,6 +220,10 @@ public class ActorSystemContext {
219 220 @Getter
220 221 private MailService mailService;
221 222
  223 + @Autowired
  224 + @Getter
  225 + private ClaimDevicesService claimDevicesService;
  226 +
222 227 //TODO: separate context for TbCore and TbRuleEngine
223 228 @Autowired(required = false)
224 229 @Getter
... ...
... ... @@ -39,6 +39,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
39 39 import org.thingsboard.server.common.msg.queue.TbCallback;
40 40 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
41 41 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
  42 +import org.thingsboard.server.gen.transport.TransportProtos;
42 43 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
43 44 import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry;
44 45 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
... ... @@ -232,9 +233,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
232 233 if (msg.hasSubscriptionInfo()) {
233 234 handleSessionActivity(context, msg.getSessionInfo(), msg.getSubscriptionInfo());
234 235 }
  236 + if (msg.hasClaimDevice()) {
  237 + handleClaimDeviceMsg(context, msg.getSessionInfo(), msg.getClaimDevice());
  238 + }
235 239 callback.onSuccess();
236 240 }
237 241
  242 + private void handleClaimDeviceMsg(ActorContext context, SessionInfoProto sessionInfo, TransportProtos.ClaimDeviceMsg msg) {
  243 + DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
  244 + systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs());
  245 + }
  246 +
238 247 private void reportSessionOpen() {
239 248 systemContext.getDeviceStateService().onDeviceConnect(deviceId);
240 249 }
... ...
... ... @@ -206,7 +206,7 @@ public class TenantActor extends RuleChainManagerActor {
206 206 if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) {
207 207 RuleChain ruleChain = systemContext.getRuleChainService().
208 208 findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId()));
209   - if (ruleChain.getType().equals(RuleChainType.SYSTEM)) {
  209 + if (ruleChain != null && ruleChain.getType().equals(RuleChainType.SYSTEM)) {
210 210 visit(ruleChain, target);
211 211 }
212 212 }
... ...
... ... @@ -28,7 +28,6 @@ import org.springframework.web.bind.annotation.ResponseStatus;
28 28 import org.springframework.web.bind.annotation.RestController;
29 29 import org.thingsboard.server.common.data.EntityType;
30 30 import org.thingsboard.server.common.data.alarm.Alarm;
31   -import org.thingsboard.server.common.data.id.AlarmId;
32 31 import org.thingsboard.server.common.data.alarm.AlarmInfo;
33 32 import org.thingsboard.server.common.data.alarm.AlarmQuery;
34 33 import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
... ... @@ -37,6 +36,7 @@ import org.thingsboard.server.common.data.alarm.AlarmStatus;
37 36 import org.thingsboard.server.common.data.audit.ActionType;
38 37 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
39 38 import org.thingsboard.server.common.data.exception.ThingsboardException;
  39 +import org.thingsboard.server.common.data.id.AlarmId;
40 40 import org.thingsboard.server.common.data.id.EntityId;
41 41 import org.thingsboard.server.common.data.id.EntityIdFactory;
42 42 import org.thingsboard.server.common.data.page.TimePageData;
... ... @@ -84,8 +84,9 @@ public class AlarmController extends BaseController {
84 84 public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException {
85 85 try {
86 86 alarm.setTenantId(getCurrentUser().getTenantId());
87   - Operation operation = alarm.getId() == null ? Operation.CREATE : Operation.WRITE;
88   - accessControlService.checkPermission(getCurrentUser(), Resource.ALARM, operation, alarm.getId(), alarm);
  87 +
  88 + checkEntity(alarm.getId(), alarm, Resource.ALARM);
  89 +
89 90 Alarm savedAlarm = checkNotNull(alarmService.createOrUpdateAlarm(alarm));
90 91 logEntityAction(savedAlarm.getId(), savedAlarm,
91 92 getCurrentUser().getCustomerId(),
... ...
... ... @@ -40,6 +40,8 @@ import org.thingsboard.server.common.data.id.EdgeId;
40 40 import org.thingsboard.server.common.data.id.TenantId;
41 41 import org.thingsboard.server.common.data.page.TextPageData;
42 42 import org.thingsboard.server.common.data.page.TextPageLink;
  43 +import org.thingsboard.server.common.data.page.TimePageData;
  44 +import org.thingsboard.server.common.data.page.TimePageLink;
43 45 import org.thingsboard.server.dao.exception.IncorrectParameterException;
44 46 import org.thingsboard.server.dao.model.ModelConstants;
45 47 import org.thingsboard.server.queue.util.TbCoreComponent;
... ... @@ -51,6 +53,8 @@ import java.util.ArrayList;
51 53 import java.util.List;
52 54 import java.util.stream.Collectors;
53 55
  56 +import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
  57 +
54 58 @RestController
55 59 @TbCoreComponent
56 60 @RequestMapping("/api")
... ... @@ -78,18 +82,15 @@ public class AssetController extends BaseController {
78 82 try {
79 83 asset.setTenantId(getCurrentUser().getTenantId());
80 84
81   - Operation operation = asset.getId() == null ? Operation.CREATE : Operation.WRITE;
82   -
83   - accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, operation,
84   - asset.getId(), asset);
  85 + checkEntity(asset.getId(), asset, Resource.ASSET);
85 86
86   - Asset savedAsset = checkNotNull(assetService.saveAsset(asset));
  87 + Asset savedAsset = checkNotNull(assetService.saveAsset(asset));
87 88
88 89 logEntityAction(savedAsset.getId(), savedAsset,
89 90 savedAsset.getCustomerId(),
90 91 asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
91 92
92   - return savedAsset;
  93 + return savedAsset;
93 94 } catch (Exception e) {
94 95 logEntityAction(emptyId(EntityType.ASSET), asset,
95 96 null, asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
... ... @@ -140,7 +141,7 @@ public class AssetController extends BaseController {
140 141 savedAsset.getCustomerId(),
141 142 ActionType.ASSIGNED_TO_CUSTOMER, null, strAssetId, strCustomerId, customer.getName());
142 143
143   - return savedAsset;
  144 + return savedAsset;
144 145 } catch (Exception e) {
145 146
146 147 logEntityAction(emptyId(EntityType.ASSET), null,
... ... @@ -220,7 +221,7 @@ public class AssetController extends BaseController {
220 221 try {
221 222 TenantId tenantId = getCurrentUser().getTenantId();
222 223 TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
223   - if (type != null && type.trim().length()>0) {
  224 + if (type != null && type.trim().length() > 0) {
224 225 return checkNotNull(assetService.findAssetsByTenantIdAndType(tenantId, type, pageLink));
225 226 } else {
226 227 return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink));
... ... @@ -259,7 +260,7 @@ public class AssetController extends BaseController {
259 260 CustomerId customerId = new CustomerId(toUUID(strCustomerId));
260 261 checkCustomerId(customerId, Operation.READ);
261 262 TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
262   - if (type != null && type.trim().length()>0) {
  263 + if (type != null && type.trim().length() > 0) {
263 264 return checkNotNull(assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
264 265 } else {
265 266 return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink));
... ... @@ -336,9 +337,9 @@ public class AssetController extends BaseController {
336 337 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
337 338 @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.POST)
338 339 @ResponseBody
339   - public Asset assignAssetToEdge(@PathVariable("edgeId") String strEdgeId,
  340 + public Asset assignAssetToEdge(@PathVariable(EDGE_ID) String strEdgeId,
340 341 @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
341   - checkParameter("edgeId", strEdgeId);
  342 + checkParameter(EDGE_ID, strEdgeId);
342 343 checkParameter(ASSET_ID, strAssetId);
343 344 try {
344 345 EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
... ... @@ -365,20 +366,20 @@ public class AssetController extends BaseController {
365 366 }
366 367
367 368 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
368   - @RequestMapping(value = "/edge/asset/{assetId}", method = RequestMethod.DELETE)
  369 + @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.DELETE)
369 370 @ResponseBody
370   - public Asset unassignAssetFromEdge(@PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
  371 + public Asset unassignAssetFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
  372 + @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
  373 + checkParameter(EDGE_ID, strEdgeId);
371 374 checkParameter(ASSET_ID, strAssetId);
372 375 try {
  376 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  377 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  378 +
373 379 AssetId assetId = new AssetId(toUUID(strAssetId));
374 380 Asset asset = checkAssetId(assetId, Operation.UNASSIGN_FROM_EDGE);
375   - if (asset.getEdgeId() == null || asset.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) {
376   - throw new IncorrectParameterException("Asset isn't assigned to any edge!");
377   - }
378   -
379   - Edge edge = checkEdgeId(asset.getEdgeId(), Operation.READ);
380 381
381   - Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(getTenantId(), assetId));
  382 + Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(getTenantId(), assetId, edgeId));
382 383
383 384 logEntityAction(assetId, asset,
384 385 asset.getCustomerId(),
... ... @@ -398,24 +399,20 @@ public class AssetController extends BaseController {
398 399 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
399 400 @RequestMapping(value = "/edge/{edgeId}/assets", params = {"limit"}, method = RequestMethod.GET)
400 401 @ResponseBody
401   - public TextPageData<Asset> getEdgeAssets(
402   - @PathVariable("edgeId") String strEdgeId,
  402 + public TimePageData<Asset> getEdgeAssets(
  403 + @PathVariable(EDGE_ID) String strEdgeId,
403 404 @RequestParam int limit,
404   - @RequestParam(required = false) String type,
405   - @RequestParam(required = false) String textSearch,
406   - @RequestParam(required = false) String idOffset,
407   - @RequestParam(required = false) String textOffset) throws ThingsboardException {
408   - checkParameter("edgeId", strEdgeId);
  405 + @RequestParam(required = false) Long startTime,
  406 + @RequestParam(required = false) Long endTime,
  407 + @RequestParam(required = false, defaultValue = "false") boolean ascOrder,
  408 + @RequestParam(required = false) String offset) throws ThingsboardException {
  409 + checkParameter(EDGE_ID, strEdgeId);
409 410 try {
410 411 TenantId tenantId = getCurrentUser().getTenantId();
411 412 EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
412 413 checkEdgeId(edgeId, Operation.READ);
413   - TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
414   - if (type != null && type.trim().length()>0) {
415   - return checkNotNull(assetService.findAssetsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink));
416   - } else {
417   - return checkNotNull(assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
418   - }
  414 + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
  415 + return checkNotNull(assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get());
419 416 } catch (Exception e) {
420 417 throw handleException(e);
421 418 }
... ...
... ... @@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Value;
26 26 import org.springframework.security.core.Authentication;
27 27 import org.springframework.security.core.context.SecurityContextHolder;
28 28 import org.springframework.web.bind.annotation.ExceptionHandler;
  29 +import org.thingsboard.server.common.data.BaseData;
29 30 import org.thingsboard.server.common.data.Customer;
30 31 import org.thingsboard.server.common.data.Dashboard;
31 32 import org.thingsboard.server.common.data.DashboardInfo;
... ... @@ -107,7 +108,6 @@ import org.thingsboard.server.service.state.DeviceStateService;
107 108 import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
108 109
109 110 import javax.mail.MessagingException;
110   -import javax.servlet.http.HttpServletRequest;
111 111 import javax.servlet.http.HttpServletResponse;
112 112 import java.util.List;
113 113 import java.util.Optional;
... ... @@ -337,11 +337,23 @@ public abstract class BaseController {
337 337 }
338 338 }
339 339
  340 + protected <I extends EntityId, T extends HasTenantId> void checkEntity(I entityId, T entity, Resource resource) throws ThingsboardException {
  341 + if (entityId == null) {
  342 + accessControlService
  343 + .checkPermission(getCurrentUser(), resource, Operation.CREATE, null, entity);
  344 + } else {
  345 + checkEntityId(entityId, Operation.WRITE);
  346 + }
  347 + }
  348 +
340 349 protected void checkEntityId(EntityId entityId, Operation operation) throws ThingsboardException {
341 350 try {
342 351 checkNotNull(entityId);
343 352 validateId(entityId.getId(), "Incorrect entityId " + entityId);
344 353 switch (entityId.getEntityType()) {
  354 + case ALARM:
  355 + checkAlarmId(new AlarmId(entityId.getId()), operation);
  356 + return;
345 357 case DEVICE:
346 358 checkDeviceId(new DeviceId(entityId.getId()), operation);
347 359 return;
... ... @@ -372,6 +384,12 @@ public abstract class BaseController {
372 384 case EDGE:
373 385 checkEdgeId(new EdgeId(entityId.getId()), operation);
374 386 return;
  387 + case WIDGETS_BUNDLE:
  388 + checkWidgetsBundleId(new WidgetsBundleId(entityId.getId()), operation);
  389 + return;
  390 + case WIDGET_TYPE:
  391 + checkWidgetTypeId(new WidgetTypeId(entityId.getId()), operation);
  392 + return;
375 393 default:
376 394 throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType());
377 395 }
... ...
... ... @@ -100,8 +100,7 @@ public class CustomerController extends BaseController {
100 100 try {
101 101 customer.setTenantId(getCurrentUser().getTenantId());
102 102
103   - Operation operation = customer.getId() == null ? Operation.CREATE : Operation.WRITE;
104   - accessControlService.checkPermission(getCurrentUser(), Resource.CUSTOMER, operation, customer.getId(), customer);
  103 + checkEntity(customer.getId(), customer, Resource.CUSTOMER);
105 104
106 105 Customer savedCustomer = checkNotNull(customerService.saveCustomer(customer));
107 106
... ...
... ... @@ -103,15 +103,12 @@ public class DashboardController extends BaseController {
103 103
104 104 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
105 105 @RequestMapping(value = "/dashboard", method = RequestMethod.POST)
106   - @ResponseBody
  106 + @ResponseBody
107 107 public Dashboard saveDashboard(@RequestBody Dashboard dashboard) throws ThingsboardException {
108 108 try {
109 109 dashboard.setTenantId(getCurrentUser().getTenantId());
110 110
111   - Operation operation = dashboard.getId() == null ? Operation.CREATE : Operation.WRITE;
112   -
113   - accessControlService.checkPermission(getCurrentUser(), Resource.DASHBOARD, operation,
114   - dashboard.getId(), dashboard);
  111 + checkEntity(dashboard.getId(), dashboard, Resource.DASHBOARD);
115 112
116 113 Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard));
117 114
... ... @@ -155,9 +152,9 @@ public class DashboardController extends BaseController {
155 152
156 153 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
157 154 @RequestMapping(value = "/customer/{customerId}/dashboard/{dashboardId}", method = RequestMethod.POST)
158   - @ResponseBody
  155 + @ResponseBody
159 156 public Dashboard assignDashboardToCustomer(@PathVariable("customerId") String strCustomerId,
160   - @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
  157 + @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
161 158 checkParameter("customerId", strCustomerId);
162 159 checkParameter(DASHBOARD_ID, strDashboardId);
163 160 try {
... ... @@ -166,7 +163,7 @@ public class DashboardController extends BaseController {
166 163
167 164 DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
168 165 checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
169   -
  166 +
170 167 Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
171 168
172 169 logEntityAction(dashboardId, savedDashboard,
... ... @@ -187,7 +184,7 @@ public class DashboardController extends BaseController {
187 184
188 185 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
189 186 @RequestMapping(value = "/customer/{customerId}/dashboard/{dashboardId}", method = RequestMethod.DELETE)
190   - @ResponseBody
  187 + @ResponseBody
191 188 public Dashboard unassignDashboardFromCustomer(@PathVariable("customerId") String strCustomerId,
192 189 @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
193 190 checkParameter("customerId", strCustomerId);
... ... @@ -421,7 +418,7 @@ public class DashboardController extends BaseController {
421 418 }
422 419
423 420 @PreAuthorize("hasAuthority('SYS_ADMIN')")
424   - @RequestMapping(value = "/tenant/{tenantId}/dashboards", params = { "limit" }, method = RequestMethod.GET)
  421 + @RequestMapping(value = "/tenant/{tenantId}/dashboards", params = {"limit"}, method = RequestMethod.GET)
425 422 @ResponseBody
426 423 public TextPageData<DashboardInfo> getTenantDashboards(
427 424 @PathVariable("tenantId") String strTenantId,
... ... @@ -440,7 +437,7 @@ public class DashboardController extends BaseController {
440 437 }
441 438
442 439 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
443   - @RequestMapping(value = "/tenant/dashboards", params = { "limit" }, method = RequestMethod.GET)
  440 + @RequestMapping(value = "/tenant/dashboards", params = {"limit"}, method = RequestMethod.GET)
444 441 @ResponseBody
445 442 public TextPageData<DashboardInfo> getTenantDashboards(
446 443 @RequestParam int limit,
... ... @@ -457,7 +454,7 @@ public class DashboardController extends BaseController {
457 454 }
458 455
459 456 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
460   - @RequestMapping(value = "/customer/{customerId}/dashboards", params = { "limit" }, method = RequestMethod.GET)
  457 + @RequestMapping(value = "/customer/{customerId}/dashboards", params = {"limit"}, method = RequestMethod.GET)
461 458 @ResponseBody
462 459 public TimePageData<DashboardInfo> getCustomerDashboards(
463 460 @PathVariable("customerId") String strCustomerId,
... ...
... ... @@ -49,6 +49,9 @@ import org.thingsboard.server.common.data.id.EdgeId;
49 49 import org.thingsboard.server.common.data.id.TenantId;
50 50 import org.thingsboard.server.common.data.page.TextPageData;
51 51 import org.thingsboard.server.common.data.page.TextPageLink;
  52 +import org.thingsboard.server.common.data.page.TimePageData;
  53 +import org.thingsboard.server.common.data.page.TimePageLink;
  54 +import org.thingsboard.server.common.data.rule.RuleChain;
52 55 import org.thingsboard.server.common.data.security.DeviceCredentials;
53 56 import org.thingsboard.server.dao.device.claim.ClaimResponse;
54 57 import org.thingsboard.server.dao.device.claim.ClaimResult;
... ... @@ -96,10 +99,7 @@ public class DeviceController extends BaseController {
96 99 try {
97 100 device.setTenantId(getCurrentUser().getTenantId());
98 101
99   - Operation operation = device.getId() == null ? Operation.CREATE : Operation.WRITE;
100   -
101   - accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, operation,
102   - device.getId(), device);
  102 + checkEntity(device.getId(), device, Resource.DEVICE);
103 103
104 104 Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken));
105 105
... ... @@ -519,19 +519,20 @@ public class DeviceController extends BaseController {
519 519 }
520 520
521 521 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
522   - @RequestMapping(value = "/edge/device/{deviceId}", method = RequestMethod.DELETE)
  522 + @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.DELETE)
523 523 @ResponseBody
524   - public Device unassignDeviceFromEdge(@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
  524 + public Device unassignDeviceFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
  525 + @PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
  526 + checkParameter(EDGE_ID, strEdgeId);
525 527 checkParameter(DEVICE_ID, strDeviceId);
526 528 try {
  529 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  530 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  531 +
527 532 DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
528 533 Device device = checkDeviceId(deviceId, Operation.UNASSIGN_FROM_EDGE);
529   - if (device.getEdgeId() == null || device.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) {
530   - throw new IncorrectParameterException("Device isn't assigned to any edge!");
531   - }
532   - Edge edge = checkEdgeId(device.getEdgeId(), Operation.READ);
533 534
534   - Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(getCurrentUser().getTenantId(), deviceId));
  535 + Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(getCurrentUser().getTenantId(), deviceId, edgeId));
535 536
536 537 logEntityAction(deviceId, device,
537 538 device.getCustomerId(),
... ... @@ -549,24 +550,20 @@ public class DeviceController extends BaseController {
549 550 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
550 551 @RequestMapping(value = "/edge/{edgeId}/devices", params = {"limit"}, method = RequestMethod.GET)
551 552 @ResponseBody
552   - public TextPageData<Device> getEdgeDevices(
553   - @PathVariable("edgeId") String strEdgeId,
  553 + public TimePageData<Device> getEdgeDevices(
  554 + @PathVariable(EDGE_ID) String strEdgeId,
554 555 @RequestParam int limit,
555   - @RequestParam(required = false) String type,
556   - @RequestParam(required = false) String textSearch,
557   - @RequestParam(required = false) String idOffset,
558   - @RequestParam(required = false) String textOffset) throws ThingsboardException {
559   - checkParameter("edgeId", strEdgeId);
  556 + @RequestParam(required = false) Long startTime,
  557 + @RequestParam(required = false) Long endTime,
  558 + @RequestParam(required = false, defaultValue = "false") boolean ascOrder,
  559 + @RequestParam(required = false) String offset) throws ThingsboardException {
  560 + checkParameter(EDGE_ID, strEdgeId);
560 561 try {
561 562 TenantId tenantId = getCurrentUser().getTenantId();
562 563 EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
563 564 checkEdgeId(edgeId, Operation.READ);
564   - TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
565   - if (type != null && type.trim().length()>0) {
566   - return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink));
567   - } else {
568   - return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
569   - }
  565 + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
  566 + return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get());
570 567 } catch (Exception e) {
571 568 throw handleException(e);
572 569 }
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.controller;
17 17
  18 +import com.google.common.util.concurrent.Futures;
18 19 import com.google.common.util.concurrent.ListenableFuture;
19 20 import org.springframework.http.HttpStatus;
20 21 import org.springframework.security.access.prepost.PreAuthorize;
... ... @@ -92,15 +93,16 @@ public class EdgeController extends BaseController {
92 93 accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation,
93 94 edge.getId(), edge);
94 95
95   - Edge result = checkNotNull(edgeService.saveEdge(edge));
  96 + Edge savedEdge = checkNotNull(edgeService.saveEdge(edge));
96 97
97 98 if (created) {
98   - ruleChainService.assignRuleChainToEdge(tenantId, defaultRootEdgeRuleChain.getId(), result.getId());
99   - edgeService.setEdgeRootRuleChain(tenantId, result, defaultRootEdgeRuleChain.getId());
  99 + ruleChainService.assignRuleChainToEdge(tenantId, defaultRootEdgeRuleChain.getId(), savedEdge.getId());
  100 + edgeService.setEdgeRootRuleChain(tenantId, savedEdge, defaultRootEdgeRuleChain.getId());
  101 + edgeService.assignDefaultRuleChainsToEdge(tenantId, savedEdge.getId());
100 102 }
101 103
102   - logEntityAction(result.getId(), result, null, created ? ActionType.ADDED : ActionType.UPDATED, null);
103   - return result;
  104 + logEntityAction(savedEdge.getId(), savedEdge, null, created ? ActionType.ADDED : ActionType.UPDATED, null);
  105 + return savedEdge;
104 106 } catch (Exception e) {
105 107 logEntityAction(emptyId(EntityType.EDGE), edge,
106 108 null, edge.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
... ...
... ... @@ -48,6 +48,8 @@ import org.thingsboard.server.common.data.id.UUIDBased;
48 48 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
49 49 import org.thingsboard.server.common.data.page.TextPageData;
50 50 import org.thingsboard.server.common.data.page.TextPageLink;
  51 +import org.thingsboard.server.common.data.page.TimePageData;
  52 +import org.thingsboard.server.common.data.page.TimePageLink;
51 53 import org.thingsboard.server.dao.exception.IncorrectParameterException;
52 54 import org.thingsboard.server.dao.model.ModelConstants;
53 55 import org.thingsboard.server.queue.util.TbCoreComponent;
... ... @@ -95,10 +97,7 @@ public class EntityViewController extends BaseController {
95 97 try {
96 98 entityView.setTenantId(getCurrentUser().getTenantId());
97 99
98   - Operation operation = entityView.getId() == null ? Operation.CREATE : Operation.WRITE;
99   -
100   - accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, operation,
101   - entityView.getId(), entityView);
  100 + checkEntity(entityView.getId(), entityView, Resource.ENTITY_VIEW);
102 101
103 102 EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView));
104 103 List<ListenableFuture<List<Void>>> futures = new ArrayList<>();
... ... @@ -400,18 +399,20 @@ public class EntityViewController extends BaseController {
400 399 }
401 400
402 401 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
403   - @RequestMapping(value = "/edge/entityView/{entityViewId}", method = RequestMethod.DELETE)
  402 + @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.DELETE)
404 403 @ResponseBody
405   - public EntityView unassignEntityViewFromEdge(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
  404 + public EntityView unassignEntityViewFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
  405 + @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
  406 + checkParameter(EDGE_ID, strEdgeId);
406 407 checkParameter(ENTITY_VIEW_ID, strEntityViewId);
407 408 try {
  409 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  410 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  411 +
408 412 EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
409 413 EntityView entityView = checkEntityViewId(entityViewId, Operation.UNASSIGN_FROM_EDGE);
410   - if (entityView.getEdgeId() == null || entityView.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) {
411   - throw new IncorrectParameterException("Entity View isn't assigned to any edge!");
412   - }
413   - Edge edge = checkEdgeId(entityView.getEdgeId(), Operation.READ);
414   - EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(getTenantId(), entityViewId));
  414 +
  415 + EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(getTenantId(), entityViewId, edgeId));
415 416 logEntityAction(entityViewId, entityView,
416 417 entityView.getCustomerId(),
417 418 ActionType.UNASSIGNED_FROM_EDGE, null, strEntityViewId, edge.getId().toString(), edge.getName());
... ... @@ -428,24 +429,20 @@ public class EntityViewController extends BaseController {
428 429 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
429 430 @RequestMapping(value = "/edge/{edgeId}/entityViews", params = {"limit"}, method = RequestMethod.GET)
430 431 @ResponseBody
431   - public TextPageData<EntityView> getEdgeEntityViews(
432   - @PathVariable("edgeId") String strEdgeId,
  432 + public TimePageData<EntityView> getEdgeEntityViews(
  433 + @PathVariable(EDGE_ID) String strEdgeId,
433 434 @RequestParam int limit,
434   - @RequestParam(required = false) String type,
435   - @RequestParam(required = false) String textSearch,
436   - @RequestParam(required = false) String idOffset,
437   - @RequestParam(required = false) String textOffset) throws ThingsboardException {
438   - checkParameter("edgeId", strEdgeId);
  435 + @RequestParam(required = false) Long startTime,
  436 + @RequestParam(required = false) Long endTime,
  437 + @RequestParam(required = false, defaultValue = "false") boolean ascOrder,
  438 + @RequestParam(required = false) String offset) throws ThingsboardException {
  439 + checkParameter(EDGE_ID, strEdgeId);
439 440 try {
440 441 TenantId tenantId = getCurrentUser().getTenantId();
441 442 EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
442 443 checkEdgeId(edgeId, Operation.READ);
443   - TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
444   - if (type != null && type.trim().length()>0) {
445   - return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink));
446   - } else {
447   - return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
448   - }
  444 + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
  445 + return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get());
449 446 } catch (Exception e) {
450 447 throw handleException(e);
451 448 }
... ...
... ... @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
20 20 import com.google.common.util.concurrent.FutureCallback;
21 21 import lombok.extern.slf4j.Slf4j;
22 22 import org.springframework.beans.factory.annotation.Autowired;
  23 +import org.springframework.beans.factory.annotation.Value;
23 24 import org.springframework.http.HttpStatus;
24 25 import org.springframework.http.ResponseEntity;
25 26 import org.springframework.security.access.prepost.PreAuthorize;
... ... @@ -65,7 +66,6 @@ import java.util.UUID;
65 66 @Slf4j
66 67 public class RpcController extends BaseController {
67 68
68   - public static final int DEFAULT_TIMEOUT = 10000;
69 69 protected final ObjectMapper jsonMapper = new ObjectMapper();
70 70
71 71 @Autowired
... ... @@ -74,6 +74,12 @@ public class RpcController extends BaseController {
74 74 @Autowired
75 75 private AccessValidator accessValidator;
76 76
  77 + @Value("${server.rest.server_side_rpc.min_timeout:5000}")
  78 + private long minTimeout;
  79 +
  80 + @Value("${server.rest.server_side_rpc.default_timeout:10000}")
  81 + private long defaultTimeout;
  82 +
77 83 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
78 84 @RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
79 85 @ResponseBody
... ... @@ -100,7 +106,8 @@ public class RpcController extends BaseController {
100 106 SecurityUser currentUser = getCurrentUser();
101 107 TenantId tenantId = currentUser.getTenantId();
102 108 final DeferredResult<ResponseEntity> response = new DeferredResult<>();
103   - long timeout = System.currentTimeMillis() + (cmd.getTimeout() != null ? cmd.getTimeout() : DEFAULT_TIMEOUT);
  109 + long timeout = cmd.getTimeout() != null ? cmd.getTimeout() : defaultTimeout;
  110 + long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout);
104 111 ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(cmd.getMethodName(), cmd.getRequestData());
105 112 accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<DeferredResult<ResponseEntity>>() {
106 113 @Override
... ... @@ -109,7 +116,7 @@ public class RpcController extends BaseController {
109 116 tenantId,
110 117 deviceId,
111 118 oneWay,
112   - timeout,
  119 + expTime,
113 120 body
114 121 );
115 122 deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse));
... ...
... ... @@ -130,15 +130,14 @@ public class RuleChainController extends BaseController {
130 130 boolean created = ruleChain.getId() == null;
131 131 ruleChain.setTenantId(getCurrentUser().getTenantId());
132 132
133   - Operation operation = created ? Operation.CREATE : Operation.WRITE;
134   -
135   - accessControlService.checkPermission(getCurrentUser(), Resource.RULE_CHAIN, operation,
136   - ruleChain.getId(), ruleChain);
  133 + checkEntity(ruleChain.getId(), ruleChain, Resource.RULE_CHAIN);
137 134
138 135 RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain));
139 136
140   - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(),
141   - created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
  137 + if (RuleChainType.SYSTEM.equals(savedRuleChain.getType())) {
  138 + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(),
  139 + created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
  140 + }
142 141
143 142 logEntityAction(savedRuleChain.getId(), savedRuleChain,
144 143 null,
... ... @@ -210,7 +209,9 @@ public class RuleChainController extends BaseController {
210 209 RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE);
211 210 RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData));
212 211
213   - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
  212 + if (RuleChainType.SYSTEM.equals(ruleChain.getType())) {
  213 + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
  214 + }
214 215
215 216 logEntityAction(ruleChain.getId(), ruleChain,
216 217 null,
... ... @@ -266,10 +267,12 @@ public class RuleChainController extends BaseController {
266 267
267 268 referencingRuleChainIds.remove(ruleChain.getId());
268 269
269   - referencingRuleChainIds.forEach(referencingRuleChainId ->
270   - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
  270 + if (RuleChainType.SYSTEM.equals(ruleChain.getType())) {
  271 + referencingRuleChainIds.forEach(referencingRuleChainId ->
  272 + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
271 273
272   - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
  274 + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
  275 + }
273 276
274 277 logEntityAction(ruleChainId, ruleChain,
275 278 null,
... ...
... ... @@ -72,10 +72,8 @@ public class TenantController extends BaseController {
72 72 try {
73 73 boolean newTenant = tenant.getId() == null;
74 74
75   - Operation operation = newTenant ? Operation.CREATE : Operation.WRITE;
  75 + checkEntity(tenant.getId(), tenant, Resource.TENANT);
76 76
77   - accessControlService.checkPermission(getCurrentUser(), Resource.TENANT, operation,
78   - tenant.getId(), tenant);
79 77 tenant = checkNotNull(tenantService.saveTenant(tenant));
80 78 if (newTenant) {
81 79 installScripts.createDefaultRuleChains(tenant.getId());
... ...
... ... @@ -132,17 +132,13 @@ public class UserController extends BaseController {
132 132 @ResponseBody
133 133 public User saveUser(@RequestBody User user,
134 134 @RequestParam(required = false, defaultValue = "true") boolean sendActivationMail,
135   - HttpServletRequest request) throws ThingsboardException {
  135 + HttpServletRequest request) throws ThingsboardException {
136 136 try {
137   -
138 137 if (getCurrentUser().getAuthority() == Authority.TENANT_ADMIN) {
139 138 user.setTenantId(getCurrentUser().getTenantId());
140 139 }
141 140
142   - Operation operation = user.getId() == null ? Operation.CREATE : Operation.WRITE;
143   -
144   - accessControlService.checkPermission(getCurrentUser(), Resource.USER, operation,
145   - user.getId(), user);
  141 + checkEntity(user.getId(), user, Resource.USER);
146 142
147 143 boolean sendEmail = user.getId() == null && sendActivationMail;
148 144 User savedUser = checkNotNull(userService.saveUser(user));
... ... @@ -250,7 +246,7 @@ public class UserController extends BaseController {
250 246 }
251 247
252 248 @PreAuthorize("hasAuthority('SYS_ADMIN')")
253   - @RequestMapping(value = "/tenant/{tenantId}/users", params = { "limit" }, method = RequestMethod.GET)
  249 + @RequestMapping(value = "/tenant/{tenantId}/users", params = {"limit"}, method = RequestMethod.GET)
254 250 @ResponseBody
255 251 public TextPageData<User> getTenantAdmins(
256 252 @PathVariable("tenantId") String strTenantId,
... ... @@ -269,7 +265,7 @@ public class UserController extends BaseController {
269 265 }
270 266
271 267 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
272   - @RequestMapping(value = "/customer/{customerId}/users", params = { "limit" }, method = RequestMethod.GET)
  268 + @RequestMapping(value = "/customer/{customerId}/users", params = {"limit"}, method = RequestMethod.GET)
273 269 @ResponseBody
274 270 public TextPageData<User> getCustomerUsers(
275 271 @PathVariable("customerId") String strCustomerId,
... ...
... ... @@ -66,10 +66,7 @@ public class WidgetTypeController extends BaseController {
66 66 widgetType.setTenantId(getCurrentUser().getTenantId());
67 67 }
68 68
69   - Operation operation = widgetType.getId() == null ? Operation.CREATE : Operation.WRITE;
70   -
71   - accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, operation,
72   - widgetType.getId(), widgetType);
  69 + checkEntity(widgetType.getId(), widgetType, Resource.WIDGET_TYPE);
73 70
74 71 return checkNotNull(widgetTypeService.saveWidgetType(widgetType));
75 72 } catch (Exception e) {
... ... @@ -92,7 +89,7 @@ public class WidgetTypeController extends BaseController {
92 89 }
93 90
94 91 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
95   - @RequestMapping(value = "/widgetTypes", params = { "isSystem", "bundleAlias"}, method = RequestMethod.GET)
  92 + @RequestMapping(value = "/widgetTypes", params = {"isSystem", "bundleAlias"}, method = RequestMethod.GET)
96 93 @ResponseBody
97 94 public List<WidgetType> getBundleWidgetTypes(
98 95 @RequestParam boolean isSystem,
... ... @@ -111,7 +108,7 @@ public class WidgetTypeController extends BaseController {
111 108 }
112 109
113 110 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
114   - @RequestMapping(value = "/widgetType", params = { "isSystem", "bundleAlias", "alias" }, method = RequestMethod.GET)
  111 + @RequestMapping(value = "/widgetType", params = {"isSystem", "bundleAlias", "alias"}, method = RequestMethod.GET)
115 112 @ResponseBody
116 113 public WidgetType getWidgetType(
117 114 @RequestParam boolean isSystem,
... ...
... ... @@ -67,11 +67,7 @@ public class WidgetsBundleController extends BaseController {
67 67 widgetsBundle.setTenantId(getCurrentUser().getTenantId());
68 68 }
69 69
70   - Operation operation = widgetsBundle.getId() == null ? Operation.CREATE : Operation.WRITE;
71   -
72   - accessControlService.checkPermission(getCurrentUser(), Resource.WIDGETS_BUNDLE, operation,
73   - widgetsBundle.getId(), widgetsBundle);
74   -
  70 + checkEntity(widgetsBundle.getId(), widgetsBundle, Resource.WIDGETS_BUNDLE);
75 71 return checkNotNull(widgetsBundleService.saveWidgetsBundle(widgetsBundle));
76 72 } catch (Exception e) {
77 73 throw handleException(e);
... ... @@ -93,7 +89,7 @@ public class WidgetsBundleController extends BaseController {
93 89 }
94 90
95 91 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
96   - @RequestMapping(value = "/widgetsBundles", params = { "limit" }, method = RequestMethod.GET)
  92 + @RequestMapping(value = "/widgetsBundles", params = {"limit"}, method = RequestMethod.GET)
97 93 @ResponseBody
98 94 public TextPageData<WidgetsBundle> getWidgetsBundles(
99 95 @RequestParam int limit,
... ...
... ... @@ -133,13 +133,20 @@ public class ThingsboardInstallService {
133 133 databaseEntitiesUpgradeService.upgradeDatabase("2.4.2");
134 134
135 135 case "2.4.3":
136   - log.info("Upgrading ThingsBoard from version 2.4.3 to 2.5 ...");
  136 + log.info("Upgrading ThingsBoard from version 2.4.3 to 2.5.0 ...");
137 137
138 138 if (databaseTsUpgradeService != null) {
139 139 databaseTsUpgradeService.upgradeDatabase("2.4.3");
140 140 }
141 141 databaseEntitiesUpgradeService.upgradeDatabase("2.4.3");
142 142
  143 + case "2.5.0":
  144 + log.info("Upgrading ThingsBoard from version 2.5.0 to 2.5.1 ...");
  145 + if (databaseTsUpgradeService != null) {
  146 + databaseTsUpgradeService.upgradeDatabase("2.5.0");
  147 + }
  148 +
  149 +
143 150 log.info("Updating system data...");
144 151
145 152 systemDataLoaderService.deleteSystemWidgetBundle("charts");
... ...
... ... @@ -554,7 +554,7 @@ public final class EdgeGrpcSession implements Closeable {
554 554 case ENTITY_DELETED_RPC_MESSAGE:
555 555 Device device = ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), deviceName);
556 556 if (device != null) {
557   - ctx.getDeviceService().unassignDeviceFromEdge(edge.getTenantId(), device.getId());
  557 + ctx.getDeviceService().unassignDeviceFromEdge(edge.getTenantId(), device.getId(), edge.getId());
558 558 }
559 559 break;
560 560 }
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.service.edge.rpc.init;
17 17
  18 +import com.google.common.util.concurrent.Futures;
18 19 import io.grpc.stub.StreamObserver;
19 20 import lombok.extern.slf4j.Slf4j;
20 21 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -54,6 +55,7 @@ import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgCo
54 55 import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor;
55 56
56 57 import java.util.UUID;
  58 +import java.util.concurrent.Future;
57 59
58 60 @Service
59 61 @Slf4j
... ... @@ -100,10 +102,10 @@ public class DefaultInitEdgeService implements InitEdgeService {
100 102
101 103 private void initDevices(Edge edge, StreamObserver<ResponseMsg> outputStream) {
102 104 try {
103   - TextPageLink pageLink = new TextPageLink(100);
104   - TextPageData<Device> pageData;
  105 + TimePageLink pageLink = new TimePageLink(100);
  106 + TimePageData<Device> pageData;
105 107 do {
106   - pageData = deviceService.findDevicesByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink);
  108 + pageData = deviceService.findDevicesByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get();
107 109 if (!pageData.getData().isEmpty()) {
108 110 log.trace("[{}] [{}] device(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
109 111 for (Device device : pageData.getData()) {
... ... @@ -130,10 +132,10 @@ public class DefaultInitEdgeService implements InitEdgeService {
130 132
131 133 private void initAssets(Edge edge, StreamObserver<ResponseMsg> outputStream) {
132 134 try {
133   - TextPageLink pageLink = new TextPageLink(100);
134   - TextPageData<Asset> pageData;
  135 + TimePageLink pageLink = new TimePageLink(100);
  136 + TimePageData<Asset> pageData;
135 137 do {
136   - pageData = assetService.findAssetsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink);
  138 + pageData = assetService.findAssetsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get();
137 139 if (!pageData.getData().isEmpty()) {
138 140 log.trace("[{}] [{}] asset(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
139 141 for (Asset asset : pageData.getData()) {
... ... @@ -160,10 +162,10 @@ public class DefaultInitEdgeService implements InitEdgeService {
160 162
161 163 private void initEntityViews(Edge edge, StreamObserver<ResponseMsg> outputStream) {
162 164 try {
163   - TextPageLink pageLink = new TextPageLink(100);
164   - TextPageData<EntityView> pageData;
  165 + TimePageLink pageLink = new TimePageLink(100);
  166 + TimePageData<EntityView> pageData;
165 167 do {
166   - pageData = entityViewService.findEntityViewsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink);
  168 + pageData = entityViewService.findEntityViewsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get();
167 169 if (!pageData.getData().isEmpty()) {
168 170 log.trace("[{}] [{}] entity view(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
169 171 for (EntityView entityView : pageData.getData()) {
... ...
... ... @@ -34,6 +34,9 @@ public abstract class AbstractSqlTsDatabaseUpgradeService {
34 34 protected static final String CALL_REGEX = "call ";
35 35 protected static final String DROP_TABLE = "DROP TABLE ";
36 36 protected static final String DROP_PROCEDURE_IF_EXISTS = "DROP PROCEDURE IF EXISTS ";
  37 + protected static final String TS_KV_SQL = "ts_kv.sql";
  38 + protected static final String PATH_TO_USERS_PUBLIC_FOLDER = "C:\\Users\\Public";
  39 + protected static final String THINGSBOARD_WINDOWS_UPGRADE_DIR = "THINGSBOARD_WINDOWS_UPGRADE_DIR";
37 40
38 41 @Value("${spring.datasource.url}")
39 42 protected String dbUrl;
... ...
... ... @@ -312,18 +312,6 @@ public class CassandraDatabaseUpgradeService extends AbstractCassandraDatabaseUp
312 312 loadCql(schemaUpdateFile);
313 313
314 314 try {
315   - cluster.getSession().execute("alter table asset add edge_id text");
316   - Thread.sleep(2500);
317   - } catch (InvalidQueryException e) {}
318   - try {
319   - cluster.getSession().execute("alter table device add edge_id text");
320   - Thread.sleep(2500);
321   - } catch (InvalidQueryException e) {}
322   - try {
323   - cluster.getSession().execute("alter table entity_view add edge_id text");
324   - Thread.sleep(2500);
325   - } catch (InvalidQueryException e) {}
326   - try {
327 315 cluster.getSession().execute("alter table rule_chain add type text");
328 316 Thread.sleep(2500);
329 317 } catch (InvalidQueryException e) {}
... ...
... ... @@ -48,6 +48,8 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase
48 48 }
49 49 log.info("Schema updated.");
50 50 break;
  51 + case "2.5.0":
  52 + break;
51 53 default:
52 54 throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion);
53 55 }
... ...
... ... @@ -131,6 +131,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
131 131 node.put("username", "");
132 132 node.put("password", "");
133 133 node.put("tlsVersion", "TLSv1.2");//NOSONAR, key used to identify password field (not password value itself)
  134 + node.put("enableProxy", false);
134 135 mailSettings.setJsonValue(node);
135 136 adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, mailSettings);
136 137 }
... ...
... ... @@ -37,8 +37,6 @@ public class PsqlTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaServic
37 37 @Override
38 38 public void createDatabaseSchema() throws Exception {
39 39 super.createDatabaseSchema();
40   - if (partitionType.equals("INDEFINITE")) {
41   - executeQuery("CREATE TABLE ts_kv_indefinite PARTITION OF ts_kv DEFAULT;");
42   - }
  40 + executeQuery("CREATE TABLE IF NOT EXISTS ts_kv_indefinite PARTITION OF ts_kv DEFAULT;");
43 41 }
44 42 }
\ No newline at end of file
... ...
... ... @@ -16,12 +16,17 @@
16 16 package org.thingsboard.server.service.install;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.apache.commons.lang3.StringUtils;
  20 +import org.apache.commons.lang3.SystemUtils;
19 21 import org.springframework.beans.factory.annotation.Value;
20 22 import org.springframework.context.annotation.Profile;
21 23 import org.springframework.stereotype.Service;
22 24 import org.thingsboard.server.dao.util.PsqlDao;
23 25 import org.thingsboard.server.dao.util.SqlTsDao;
24 26
  27 +import java.io.File;
  28 +import java.io.IOException;
  29 +import java.nio.file.Files;
25 30 import java.nio.file.Path;
26 31 import java.nio.file.Paths;
27 32 import java.sql.Connection;
... ... @@ -37,6 +42,7 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
37 42 @Value("${sql.postgres.ts_key_value_partitioning:MONTHS}")
38 43 private String partitionType;
39 44
  45 + private static final String TS_KV_LATEST_SQL = "ts_kv_latest.sql";
40 46 private static final String LOAD_FUNCTIONS_SQL = "schema_update_psql_ts.sql";
41 47 private static final String LOAD_TTL_FUNCTIONS_SQL = "schema_update_ttl.sql";
42 48 private static final String LOAD_DROP_PARTITIONS_FUNCTIONS_SQL = "schema_update_psql_drop_partitions.sql";
... ... @@ -49,15 +55,17 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
49 55 private static final String CREATE_PARTITIONS = "create_partitions(IN partition_type varchar)";
50 56 private static final String CREATE_TS_KV_DICTIONARY_TABLE = "create_ts_kv_dictionary_table()";
51 57 private static final String INSERT_INTO_DICTIONARY = "insert_into_dictionary()";
52   - private static final String INSERT_INTO_TS_KV = "insert_into_ts_kv()";
53   - private static final String INSERT_INTO_TS_KV_LATEST = "insert_into_ts_kv_latest()";
  58 + private static final String INSERT_INTO_TS_KV = "insert_into_ts_kv(IN path_to_file varchar)";
  59 + private static final String INSERT_INTO_TS_KV_LATEST = "insert_into_ts_kv_latest(IN path_to_file varchar)";
  60 + private static final String INSERT_INTO_TS_KV_CURSOR = "insert_into_ts_kv_cursor()";
  61 + private static final String INSERT_INTO_TS_KV_LATEST_CURSOR = "insert_into_ts_kv_latest_cursor()";
54 62
55 63 private static final String CALL_CREATE_PARTITION_TS_KV_TABLE = CALL_REGEX + CREATE_PARTITION_TS_KV_TABLE;
56 64 private static final String CALL_CREATE_NEW_TS_KV_LATEST_TABLE = CALL_REGEX + CREATE_NEW_TS_KV_LATEST_TABLE;
57 65 private static final String CALL_CREATE_TS_KV_DICTIONARY_TABLE = CALL_REGEX + CREATE_TS_KV_DICTIONARY_TABLE;
58 66 private static final String CALL_INSERT_INTO_DICTIONARY = CALL_REGEX + INSERT_INTO_DICTIONARY;
59   - private static final String CALL_INSERT_INTO_TS_KV = CALL_REGEX + INSERT_INTO_TS_KV;
60   - private static final String CALL_INSERT_INTO_TS_KV_LATEST = CALL_REGEX + INSERT_INTO_TS_KV_LATEST;
  67 + private static final String CALL_INSERT_INTO_TS_KV_CURSOR = CALL_REGEX + INSERT_INTO_TS_KV_CURSOR;
  68 + private static final String CALL_INSERT_INTO_TS_KV_LATEST_CURSOR = CALL_REGEX + INSERT_INTO_TS_KV_LATEST_CURSOR;
61 69
62 70 private static final String DROP_TABLE_TS_KV_OLD = DROP_TABLE + TS_KV_OLD;
63 71 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
69 77 private static final String DROP_PROCEDURE_INSERT_INTO_DICTIONARY = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_DICTIONARY;
70 78 private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV;
71 79 private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV_LATEST;
  80 + private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV_CURSOR = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV_CURSOR;
  81 + private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST_CURSOR = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV_LATEST_CURSOR;
72 82 private static final String DROP_FUNCTION_GET_PARTITION_DATA = "DROP FUNCTION IF EXISTS get_partitions_data;";
73 83
74 84 @Override
... ... @@ -89,14 +99,63 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
89 99 executeQuery(conn, CALL_CREATE_PARTITION_TS_KV_TABLE);
90 100 if (!partitionType.equals("INDEFINITE")) {
91 101 executeQuery(conn, "call create_partitions('" + partitionType + "')");
92   - } else {
93   - executeQuery(conn, "CREATE TABLE IF NOT EXISTS ts_kv_indefinite PARTITION OF ts_kv DEFAULT;");
94 102 }
95 103 executeQuery(conn, CALL_CREATE_TS_KV_DICTIONARY_TABLE);
96 104 executeQuery(conn, CALL_INSERT_INTO_DICTIONARY);
97   - executeQuery(conn, CALL_INSERT_INTO_TS_KV);
98   - executeQuery(conn, CALL_CREATE_NEW_TS_KV_LATEST_TABLE);
99   - executeQuery(conn, CALL_INSERT_INTO_TS_KV_LATEST);
  105 +
  106 + Path pathToTempTsKvFile = null;
  107 + Path pathToTempTsKvLatestFile = null;
  108 + if (SystemUtils.IS_OS_WINDOWS) {
  109 + log.info("Lookup for environment variable: {} ...", THINGSBOARD_WINDOWS_UPGRADE_DIR);
  110 + Path pathToDir;
  111 + String thingsboardWindowsUpgradeDir = System.getenv("THINGSBOARD_WINDOWS_UPGRADE_DIR");
  112 + if (StringUtils.isNotEmpty(thingsboardWindowsUpgradeDir)) {
  113 + log.info("Environment variable: {} was found!", THINGSBOARD_WINDOWS_UPGRADE_DIR);
  114 + pathToDir = Paths.get(thingsboardWindowsUpgradeDir);
  115 + } else {
  116 + log.info("Failed to lookup environment variable: {}", THINGSBOARD_WINDOWS_UPGRADE_DIR);
  117 + pathToDir = Paths.get(PATH_TO_USERS_PUBLIC_FOLDER);
  118 + }
  119 + log.info("Directory: {} will be used for creation temporary upgrade files!", pathToDir);
  120 + try {
  121 + Path tsKvFile = Files.createTempFile(pathToDir, "ts_kv", ".sql");
  122 + Path tsKvLatestFile = Files.createTempFile(pathToDir, "ts_kv_latest", ".sql");
  123 + pathToTempTsKvFile = tsKvFile.toAbsolutePath();
  124 + pathToTempTsKvLatestFile = tsKvLatestFile.toAbsolutePath();
  125 + try {
  126 + copyTimeseries(conn, pathToTempTsKvFile, pathToTempTsKvLatestFile);
  127 + } catch (Exception e) {
  128 + insertTimeseries(conn);
  129 + }
  130 + } catch (IOException | SecurityException e) {
  131 + log.warn("Failed to create time-series upgrade files due to: {}", e.getMessage());
  132 + insertTimeseries(conn);
  133 + }
  134 + } else {
  135 + try {
  136 + Path tempDirPath = Files.createTempDirectory("ts_kv");
  137 + File tempDirAsFile = tempDirPath.toFile();
  138 + boolean writable = tempDirAsFile.setWritable(true, false);
  139 + boolean readable = tempDirAsFile.setReadable(true, false);
  140 + boolean executable = tempDirAsFile.setExecutable(true, false);
  141 + pathToTempTsKvFile = tempDirPath.resolve(TS_KV_SQL).toAbsolutePath();
  142 + pathToTempTsKvLatestFile = tempDirPath.resolve(TS_KV_LATEST_SQL).toAbsolutePath();
  143 + try {
  144 + if (writable && readable && executable) {
  145 + copyTimeseries(conn, pathToTempTsKvFile, pathToTempTsKvLatestFile);
  146 + } else {
  147 + throw new RuntimeException("Failed to grant write permissions for the: " + tempDirPath + "folder!");
  148 + }
  149 + } catch (Exception e) {
  150 + insertTimeseries(conn);
  151 + }
  152 + } catch (IOException | SecurityException e) {
  153 + log.warn("Failed to create time-series upgrade files due to: {}", e.getMessage());
  154 + insertTimeseries(conn);
  155 + }
  156 + }
  157 +
  158 + removeUpgradeFiles(pathToTempTsKvFile, pathToTempTsKvLatestFile);
100 159
101 160 executeQuery(conn, DROP_TABLE_TS_KV_OLD);
102 161 executeQuery(conn, DROP_TABLE_TS_KV_LATEST_OLD);
... ... @@ -108,6 +167,8 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
108 167 executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV);
109 168 executeQuery(conn, DROP_PROCEDURE_CREATE_NEW_TS_KV_LATEST_TABLE);
110 169 executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST);
  170 + executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV_CURSOR);
  171 + executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST_CURSOR);
111 172 executeQuery(conn, DROP_FUNCTION_GET_PARTITION_DATA);
112 173
113 174 executeQuery(conn, "ALTER TABLE ts_kv ADD COLUMN IF NOT EXISTS json_v json;");
... ... @@ -128,11 +189,46 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
128 189 }
129 190 }
130 191 break;
  192 + case "2.5.0":
  193 + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
  194 + executeQuery(conn, "CREATE TABLE IF NOT EXISTS ts_kv_indefinite PARTITION OF ts_kv DEFAULT;");
  195 + executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001");
  196 + }
  197 + break;
131 198 default:
132 199 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
133 200 }
134 201 }
135 202
  203 + private void removeUpgradeFiles(Path pathToTempTsKvFile, Path pathToTempTsKvLatestFile) {
  204 + if (pathToTempTsKvFile != null && pathToTempTsKvFile.toFile().exists()) {
  205 + boolean deleteTsKvFile = pathToTempTsKvFile.toFile().delete();
  206 + if (deleteTsKvFile) {
  207 + log.info("Successfully deleted the temp file for ts_kv table upgrade!");
  208 + }
  209 + }
  210 + if (pathToTempTsKvLatestFile != null && pathToTempTsKvLatestFile.toFile().exists()) {
  211 + boolean deleteTsKvLatestFile = pathToTempTsKvLatestFile.toFile().delete();
  212 + if (deleteTsKvLatestFile) {
  213 + log.info("Successfully deleted the temp file for ts_kv_latest table upgrade!");
  214 + }
  215 + }
  216 + }
  217 +
  218 + private void copyTimeseries(Connection conn, Path pathToTempTsKvFile, Path pathToTempTsKvLatestFile) {
  219 + executeQuery(conn, "call insert_into_ts_kv('" + pathToTempTsKvFile + "')");
  220 + executeQuery(conn, CALL_CREATE_NEW_TS_KV_LATEST_TABLE);
  221 + executeQuery(conn, "call insert_into_ts_kv_latest('" + pathToTempTsKvLatestFile + "')");
  222 + }
  223 +
  224 + private void insertTimeseries(Connection conn) {
  225 + log.warn("Upgrade script failed using the copy to/from files strategy!" +
  226 + " Trying to perfrom the upgrade using Inserts strategy ...");
  227 + executeQuery(conn, CALL_INSERT_INTO_TS_KV_CURSOR);
  228 + executeQuery(conn, CALL_CREATE_NEW_TS_KV_LATEST_TABLE);
  229 + executeQuery(conn, CALL_INSERT_INTO_TS_KV_LATEST_CURSOR);
  230 + }
  231 +
136 232 @Override
137 233 protected void loadSql(Connection conn, String fileName) {
138 234 Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.4.3", fileName);
... ...
... ... @@ -239,15 +239,6 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
239 239 schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.6.0", SCHEMA_UPDATE_SQL);
240 240 loadSql(schemaUpdateFile, conn);
241 241 try {
242   - conn.createStatement().execute("ALTER TABLE asset ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
243   - } catch (Exception e) {}
244   - try {
245   - conn.createStatement().execute("ALTER TABLE device ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
246   - } catch (Exception e) {}
247   - try {
248   - conn.createStatement().execute("ALTER TABLE entity_view ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
249   - } catch (Exception e) {}
250   - try {
251 242 conn.createStatement().execute("ALTER TABLE rule_chain ADD type varchar(255) DEFAULT 'SYSTEM'"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
252 243 } catch (Exception e) {}
253 244 log.info("Schema updated.");
... ...
... ... @@ -16,6 +16,8 @@
16 16 package org.thingsboard.server.service.install;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.apache.commons.lang3.StringUtils;
  20 +import org.apache.commons.lang3.SystemUtils;
19 21 import org.springframework.beans.factory.annotation.Autowired;
20 22 import org.springframework.beans.factory.annotation.Value;
21 23 import org.springframework.context.annotation.Profile;
... ... @@ -23,6 +25,9 @@ import org.springframework.stereotype.Service;
23 25 import org.thingsboard.server.dao.util.PsqlDao;
24 26 import org.thingsboard.server.dao.util.TimescaleDBTsDao;
25 27
  28 +import java.io.File;
  29 +import java.io.IOException;
  30 +import java.nio.file.Files;
26 31 import java.nio.file.Path;
27 32 import java.nio.file.Paths;
28 33 import java.sql.Connection;
... ... @@ -47,15 +52,16 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
47 52 private static final String CREATE_NEW_TS_KV_TABLE = "create_new_ts_kv_table()";
48 53 private static final String CREATE_TS_KV_DICTIONARY_TABLE = "create_ts_kv_dictionary_table()";
49 54 private static final String INSERT_INTO_DICTIONARY = "insert_into_dictionary()";
50   - private static final String INSERT_INTO_TS_KV = "insert_into_ts_kv()";
  55 + private static final String INSERT_INTO_TS_KV = "insert_into_ts_kv(IN path_to_file varchar)";
  56 + private static final String INSERT_INTO_TS_KV_CURSOR = "insert_into_ts_kv_cursor()";
51 57 private static final String INSERT_INTO_TS_KV_LATEST = "insert_into_ts_kv_latest()";
52 58
53 59 private static final String CALL_CREATE_TS_KV_LATEST_TABLE = CALL_REGEX + CREATE_TS_KV_LATEST_TABLE;
54 60 private static final String CALL_CREATE_NEW_TENANT_TS_KV_TABLE = CALL_REGEX + CREATE_NEW_TS_KV_TABLE;
55 61 private static final String CALL_CREATE_TS_KV_DICTIONARY_TABLE = CALL_REGEX + CREATE_TS_KV_DICTIONARY_TABLE;
56 62 private static final String CALL_INSERT_INTO_DICTIONARY = CALL_REGEX + INSERT_INTO_DICTIONARY;
57   - private static final String CALL_INSERT_INTO_TS_KV = CALL_REGEX + INSERT_INTO_TS_KV;
58 63 private static final String CALL_INSERT_INTO_TS_KV_LATEST = CALL_REGEX + INSERT_INTO_TS_KV_LATEST;
  64 + private static final String CALL_INSERT_INTO_TS_KV_CURSOR = CALL_REGEX + INSERT_INTO_TS_KV_CURSOR;
59 65
60 66 private static final String DROP_OLD_TENANT_TS_KV_TABLE = DROP_TABLE + TENANT_TS_KV_OLD_TABLE;
61 67
... ... @@ -63,7 +69,8 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
63 69 private static final String DROP_PROCEDURE_CREATE_TENANT_TS_KV_TABLE_COPY = DROP_PROCEDURE_IF_EXISTS + CREATE_NEW_TS_KV_TABLE;
64 70 private static final String DROP_PROCEDURE_CREATE_TS_KV_DICTIONARY_TABLE = DROP_PROCEDURE_IF_EXISTS + CREATE_TS_KV_DICTIONARY_TABLE;
65 71 private static final String DROP_PROCEDURE_INSERT_INTO_DICTIONARY = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_DICTIONARY;
66   - private static final String DROP_PROCEDURE_INSERT_INTO_TENANT_TS_KV = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV;
  72 + private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV;
  73 + private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV_CURSOR = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV_CURSOR;
67 74 private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV_LATEST;
68 75
69 76 @Autowired
... ... @@ -91,7 +98,56 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
91 98
92 99 executeQuery(conn, CALL_CREATE_TS_KV_DICTIONARY_TABLE);
93 100 executeQuery(conn, CALL_INSERT_INTO_DICTIONARY);
94   - executeQuery(conn, CALL_INSERT_INTO_TS_KV);
  101 +
  102 + Path pathToTempTsKvFile = null;
  103 + if (SystemUtils.IS_OS_WINDOWS) {
  104 + Path pathToDir;
  105 + log.info("Lookup for environment variable: {} ...", THINGSBOARD_WINDOWS_UPGRADE_DIR);
  106 + String thingsboardWindowsUpgradeDir = System.getenv(THINGSBOARD_WINDOWS_UPGRADE_DIR);
  107 + if (StringUtils.isNotEmpty(thingsboardWindowsUpgradeDir)) {
  108 + log.info("Environment variable: {} was found!", THINGSBOARD_WINDOWS_UPGRADE_DIR);
  109 + pathToDir = Paths.get(thingsboardWindowsUpgradeDir);
  110 + } else {
  111 + log.info("Failed to lookup environment variable: {}", THINGSBOARD_WINDOWS_UPGRADE_DIR);
  112 + pathToDir = Paths.get(PATH_TO_USERS_PUBLIC_FOLDER);
  113 + }
  114 + log.info("Directory: {} will be used for creation temporary upgrade file!", pathToDir);
  115 + try {
  116 + Path tsKvFile = Files.createTempFile(pathToDir, "ts_kv", ".sql");
  117 + pathToTempTsKvFile = tsKvFile.toAbsolutePath();
  118 + try {
  119 + executeQuery(conn, "call insert_into_ts_kv('" + pathToTempTsKvFile + "')");
  120 + } catch (Exception e) {
  121 + insertTimeseries(conn);
  122 + }
  123 + } catch (IOException | SecurityException e) {
  124 + log.warn("Failed to create time-series upgrade files due to: {}", e.getMessage());
  125 + insertTimeseries(conn);
  126 + }
  127 + } else {
  128 + try {
  129 + Path tempDirPath = Files.createTempDirectory("ts_kv");
  130 + File tempDirAsFile = tempDirPath.toFile();
  131 + boolean writable = tempDirAsFile.setWritable(true, false);
  132 + boolean readable = tempDirAsFile.setReadable(true, false);
  133 + boolean executable = tempDirAsFile.setExecutable(true, false);
  134 + pathToTempTsKvFile = tempDirPath.resolve(TS_KV_SQL).toAbsolutePath();
  135 + try {
  136 + if (writable && readable && executable) {
  137 + executeQuery(conn, "call insert_into_ts_kv('" + pathToTempTsKvFile + "')");
  138 + } else {
  139 + throw new RuntimeException("Failed to grant write permissions for the: " + tempDirPath + "folder!");
  140 + }
  141 + } catch (Exception e) {
  142 + insertTimeseries(conn);
  143 + }
  144 + } catch (IOException | SecurityException e) {
  145 + log.warn("Failed to create time-series upgrade files due to: {}", e.getMessage());
  146 + insertTimeseries(conn);
  147 + }
  148 + }
  149 + removeUpgradeFile(pathToTempTsKvFile);
  150 +
95 151 executeQuery(conn, CALL_INSERT_INTO_TS_KV_LATEST);
96 152
97 153 executeQuery(conn, DROP_OLD_TENANT_TS_KV_TABLE);
... ... @@ -100,7 +156,8 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
100 156 executeQuery(conn, DROP_PROCEDURE_CREATE_TENANT_TS_KV_TABLE_COPY);
101 157 executeQuery(conn, DROP_PROCEDURE_CREATE_TS_KV_DICTIONARY_TABLE);
102 158 executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_DICTIONARY);
103   - executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TENANT_TS_KV);
  159 + executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV);
  160 + executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV_CURSOR);
104 161 executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST);
105 162
106 163 executeQuery(conn, "ALTER TABLE ts_kv ADD COLUMN IF NOT EXISTS json_v json;");
... ... @@ -115,11 +172,31 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
115 172 }
116 173 }
117 174 break;
  175 + case "2.5.0":
  176 + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
  177 + executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001");
  178 + }
  179 + break;
118 180 default:
119 181 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
120 182 }
121 183 }
122 184
  185 + private void insertTimeseries(Connection conn) {
  186 + log.warn("Upgrade script failed using the copy to/from files strategy!" +
  187 + " Trying to perfrom the upgrade using Inserts strategy ...");
  188 + executeQuery(conn, CALL_INSERT_INTO_TS_KV_CURSOR);
  189 + }
  190 +
  191 + private void removeUpgradeFile(Path pathToTempTsKvFile) {
  192 + if (pathToTempTsKvFile != null && pathToTempTsKvFile.toFile().exists()) {
  193 + boolean deleteTsKvFile = pathToTempTsKvFile.toFile().delete();
  194 + if (deleteTsKvFile) {
  195 + log.info("Successfully deleted the temp file for ts_kv table upgrade!");
  196 + }
  197 + }
  198 + }
  199 +
123 200 @Override
124 201 protected void loadSql(Connection conn, String fileName) {
125 202 Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.4.3", fileName);
... ...
... ... @@ -115,6 +115,21 @@ public class DefaultMailService implements MailService {
115 115 if (enableTls && jsonConfig.has("tlsVersion") && StringUtils.isNoneEmpty(jsonConfig.get("tlsVersion").asText())) {
116 116 javaMailProperties.put(MAIL_PROP + protocol + ".ssl.protocols", jsonConfig.get("tlsVersion").asText());
117 117 }
  118 +
  119 + boolean enableProxy = jsonConfig.has("enableProxy") && jsonConfig.get("enableProxy").asBoolean();
  120 +
  121 + if (enableProxy) {
  122 + javaMailProperties.put(MAIL_PROP + protocol + ".proxy.host", jsonConfig.get("proxyHost").asText());
  123 + javaMailProperties.put(MAIL_PROP + protocol + ".proxy.port", jsonConfig.get("proxyPort").asText());
  124 + String proxyUser = jsonConfig.get("proxyUser").asText();
  125 + if (StringUtils.isNoneEmpty(proxyUser)) {
  126 + javaMailProperties.put(MAIL_PROP + protocol + ".proxy.user", proxyUser);
  127 + }
  128 + String proxyPassword = jsonConfig.get("proxyPassword").asText();
  129 + if (StringUtils.isNoneEmpty(proxyPassword)) {
  130 + javaMailProperties.put(MAIL_PROP + protocol + ".proxy.password", proxyPassword);
  131 + }
  132 + }
118 133 return javaMailProperties;
119 134 }
120 135
... ...
... ... @@ -46,6 +46,9 @@ import java.util.concurrent.atomic.AtomicInteger;
46 46 @Service
47 47 public class RemoteJsInvokeService extends AbstractJsInvokeService {
48 48
  49 + @Value("${queue.js.max_eval_requests_timeout}")
  50 + private long maxEvalRequestsTimeout;
  51 +
49 52 @Value("${queue.js.max_requests_timeout}")
50 53 private long maxRequestsTimeout;
51 54
... ... @@ -59,22 +62,22 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
59 62 @Value("${js.remote.stats.enabled:false}")
60 63 private boolean statsEnabled;
61 64
62   - private final AtomicInteger kafkaPushedMsgs = new AtomicInteger(0);
63   - private final AtomicInteger kafkaInvokeMsgs = new AtomicInteger(0);
64   - private final AtomicInteger kafkaEvalMsgs = new AtomicInteger(0);
65   - private final AtomicInteger kafkaFailedMsgs = new AtomicInteger(0);
66   - private final AtomicInteger kafkaTimeoutMsgs = new AtomicInteger(0);
  65 + private final AtomicInteger queuePushedMsgs = new AtomicInteger(0);
  66 + private final AtomicInteger queueInvokeMsgs = new AtomicInteger(0);
  67 + private final AtomicInteger queueEvalMsgs = new AtomicInteger(0);
  68 + private final AtomicInteger queueFailedMsgs = new AtomicInteger(0);
  69 + private final AtomicInteger queueTimeoutMsgs = new AtomicInteger(0);
67 70
68 71 @Scheduled(fixedDelayString = "${js.remote.stats.print_interval_ms}")
69 72 public void printStats() {
70 73 if (statsEnabled) {
71   - int pushedMsgs = kafkaPushedMsgs.getAndSet(0);
72   - int invokeMsgs = kafkaInvokeMsgs.getAndSet(0);
73   - int evalMsgs = kafkaEvalMsgs.getAndSet(0);
74   - int failed = kafkaFailedMsgs.getAndSet(0);
75   - int timedOut = kafkaTimeoutMsgs.getAndSet(0);
  74 + int pushedMsgs = queuePushedMsgs.getAndSet(0);
  75 + int invokeMsgs = queueInvokeMsgs.getAndSet(0);
  76 + int evalMsgs = queueEvalMsgs.getAndSet(0);
  77 + int failed = queueFailedMsgs.getAndSet(0);
  78 + int timedOut = queueTimeoutMsgs.getAndSet(0);
76 79 if (pushedMsgs > 0 || invokeMsgs > 0 || evalMsgs > 0 || failed > 0 || timedOut > 0) {
77   - log.info("Kafka JS Invoke Stats: pushed [{}] received [{}] invoke [{}] eval [{}] failed [{}] timedOut [{}]",
  80 + log.info("Queue JS Invoke Stats: pushed [{}] received [{}] invoke [{}] eval [{}] failed [{}] timedOut [{}]",
78 81 pushedMsgs, invokeMsgs + evalMsgs, invokeMsgs, evalMsgs, failed, timedOut);
79 82 }
80 83 }
... ... @@ -113,22 +116,22 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
113 116
114 117 log.trace("Post compile request for scriptId [{}]", scriptId);
115 118 ListenableFuture<TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse>> future = requestTemplate.send(new TbProtoJsQueueMsg<>(UUID.randomUUID(), jsRequestWrapper));
116   - if (maxRequestsTimeout > 0) {
117   - future = Futures.withTimeout(future, maxRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService);
  119 + if (maxEvalRequestsTimeout > 0) {
  120 + future = Futures.withTimeout(future, maxEvalRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService);
118 121 }
119   - kafkaPushedMsgs.incrementAndGet();
  122 + queuePushedMsgs.incrementAndGet();
120 123 Futures.addCallback(future, new FutureCallback<TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse>>() {
121 124 @Override
122 125 public void onSuccess(@Nullable TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse> result) {
123   - kafkaEvalMsgs.incrementAndGet();
  126 + queueEvalMsgs.incrementAndGet();
124 127 }
125 128
126 129 @Override
127 130 public void onFailure(Throwable t) {
128 131 if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) {
129   - kafkaTimeoutMsgs.incrementAndGet();
  132 + queueTimeoutMsgs.incrementAndGet();
130 133 }
131   - kafkaFailedMsgs.incrementAndGet();
  134 + queueFailedMsgs.incrementAndGet();
132 135 }
133 136 }, MoreExecutors.directExecutor());
134 137 return Futures.transform(future, response -> {
... ... @@ -170,20 +173,20 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
170 173 if (maxRequestsTimeout > 0) {
171 174 future = Futures.withTimeout(future, maxRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService);
172 175 }
173   - kafkaPushedMsgs.incrementAndGet();
  176 + queuePushedMsgs.incrementAndGet();
174 177 Futures.addCallback(future, new FutureCallback<TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse>>() {
175 178 @Override
176 179 public void onSuccess(@Nullable TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse> result) {
177   - kafkaInvokeMsgs.incrementAndGet();
  180 + queueInvokeMsgs.incrementAndGet();
178 181 }
179 182
180 183 @Override
181 184 public void onFailure(Throwable t) {
182 185 onScriptExecutionError(scriptId);
183 186 if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) {
184   - kafkaTimeoutMsgs.incrementAndGet();
  187 + queueTimeoutMsgs.incrementAndGet();
185 188 }
186   - kafkaFailedMsgs.incrementAndGet();
  189 + queueFailedMsgs.incrementAndGet();
187 190 }
188 191 }, MoreExecutors.directExecutor());
189 192 return Futures.transform(future, response -> {
... ...
... ... @@ -15,6 +15,9 @@
15 15 */
16 16 package org.thingsboard.server.service.security.auth.oauth2;
17 17
  18 +import com.fasterxml.jackson.databind.ObjectMapper;
  19 +import com.fasterxml.jackson.databind.node.ObjectNode;
  20 +import com.google.common.base.Strings;
18 21 import lombok.extern.slf4j.Slf4j;
19 22 import org.springframework.beans.factory.annotation.Autowired;
20 23 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
... ... @@ -22,14 +25,21 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
22 25 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
23 26 import org.springframework.util.StringUtils;
24 27 import org.thingsboard.server.common.data.Customer;
  28 +import org.thingsboard.server.common.data.DashboardInfo;
25 29 import org.thingsboard.server.common.data.Tenant;
26 30 import org.thingsboard.server.common.data.User;
27 31 import org.thingsboard.server.common.data.id.CustomerId;
  32 +import org.thingsboard.server.common.data.id.DashboardId;
  33 +import org.thingsboard.server.common.data.id.IdBased;
28 34 import org.thingsboard.server.common.data.id.TenantId;
  35 +import org.thingsboard.server.common.data.page.TextPageData;
29 36 import org.thingsboard.server.common.data.page.TextPageLink;
  37 +import org.thingsboard.server.common.data.page.TimePageData;
  38 +import org.thingsboard.server.common.data.page.TimePageLink;
30 39 import org.thingsboard.server.common.data.security.Authority;
31 40 import org.thingsboard.server.common.data.security.UserCredentials;
32 41 import org.thingsboard.server.dao.customer.CustomerService;
  42 +import org.thingsboard.server.dao.dashboard.DashboardService;
33 43 import org.thingsboard.server.dao.oauth2.OAuth2User;
34 44 import org.thingsboard.server.dao.tenant.TenantService;
35 45 import org.thingsboard.server.dao.user.UserService;
... ... @@ -40,11 +50,15 @@ import org.thingsboard.server.service.security.model.UserPrincipal;
40 50 import java.io.IOException;
41 51 import java.util.List;
42 52 import java.util.Optional;
  53 +import java.util.concurrent.ExecutionException;
43 54 import java.util.concurrent.locks.Lock;
44 55 import java.util.concurrent.locks.ReentrantLock;
45 56
46 57 @Slf4j
47 58 public abstract class AbstractOAuth2ClientMapper {
  59 + private static final int DASHBOARDS_REQUEST_LIMIT = 10;
  60 +
  61 + private static final ObjectMapper objectMapper = new ObjectMapper();
48 62
49 63 @Autowired
50 64 private UserService userService;
... ... @@ -59,6 +73,9 @@ public abstract class AbstractOAuth2ClientMapper {
59 73 private CustomerService customerService;
60 74
61 75 @Autowired
  76 + private DashboardService dashboardService;
  77 +
  78 + @Autowired
62 79 private InstallScripts installScripts;
63 80
64 81 private final Lock userCreationLock = new ReentrantLock();
... ... @@ -92,6 +109,20 @@ public abstract class AbstractOAuth2ClientMapper {
92 109 user.setEmail(oauth2User.getEmail());
93 110 user.setFirstName(oauth2User.getFirstName());
94 111 user.setLastName(oauth2User.getLastName());
  112 +
  113 + if (!StringUtils.isEmpty(oauth2User.getDefaultDashboardName())) {
  114 + Optional<DashboardId> dashboardIdOpt =
  115 + user.getAuthority() == Authority.TENANT_ADMIN ?
  116 + getDashboardId(tenantId, oauth2User.getDefaultDashboardName())
  117 + : getDashboardId(tenantId, customerId, oauth2User.getDefaultDashboardName());
  118 + if (dashboardIdOpt.isPresent()) {
  119 + ObjectNode additionalInfo = objectMapper.createObjectNode();
  120 + additionalInfo.put("defaultDashboardFullscreen", oauth2User.isAlwaysFullScreen());
  121 + additionalInfo.put("defaultDashboardId", dashboardIdOpt.get().getId().toString());
  122 + user.setAdditionalInfo(additionalInfo);
  123 + }
  124 + }
  125 +
95 126 user = userService.saveUser(user);
96 127 if (activateUser) {
97 128 UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());
... ... @@ -143,4 +174,32 @@ public abstract class AbstractOAuth2ClientMapper {
143 174 return customerService.saveCustomer(customer).getId();
144 175 }
145 176 }
  177 +
  178 + private Optional<DashboardId> getDashboardId(TenantId tenantId, String dashboardName) {
  179 + TextPageLink searchTextLink = new TextPageLink(1, dashboardName);
  180 + TextPageData<DashboardInfo> dashboardsPage = dashboardService.findDashboardsByTenantId(tenantId, searchTextLink);
  181 + return dashboardsPage.getData().stream()
  182 + .findAny()
  183 + .map(IdBased::getId);
  184 + }
  185 +
  186 + private Optional<DashboardId> getDashboardId(TenantId tenantId, CustomerId customerId, String dashboardName) {
  187 + TimePageData<DashboardInfo> dashboardsPage = null;
  188 + do {
  189 + TimePageLink timePageLink = dashboardsPage != null ?
  190 + dashboardsPage.getNextPageLink() : new TimePageLink(DASHBOARDS_REQUEST_LIMIT);
  191 + try {
  192 + dashboardsPage = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, timePageLink).get();
  193 + } catch (InterruptedException | ExecutionException e) {
  194 + throw new RuntimeException("Failed to get customer's dashboards.", e);
  195 + }
  196 + Optional<DashboardInfo> dashboardInfoOpt = dashboardsPage.getData().stream()
  197 + .filter(dashboardInfo -> dashboardName.equals(dashboardInfo.getName()))
  198 + .findAny();
  199 + if (dashboardInfoOpt.isPresent()) {
  200 + return dashboardInfoOpt.map(DashboardInfo::getId);
  201 + }
  202 + } while (dashboardsPage.hasNext());
  203 + return Optional.empty();
  204 + }
146 205 }
... ...
... ... @@ -56,6 +56,10 @@ public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implemen
56 56 String customerName = sub.replace(config.getBasic().getCustomerNamePattern());
57 57 oauth2User.setCustomerName(customerName);
58 58 }
  59 + oauth2User.setAlwaysFullScreen(config.getBasic().isAlwaysFullScreen());
  60 + if (!StringUtils.isEmpty(config.getBasic().getDefaultDashboardName())) {
  61 + oauth2User.setDefaultDashboardName(config.getBasic().getDefaultDashboardName());
  62 + }
59 63
60 64 return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser());
61 65 }
... ...
... ... @@ -56,8 +56,8 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme
56 56 try {
57 57 return restTemplate.postForEntity(custom.getUrl(), request, OAuth2User.class).getBody();
58 58 } catch (Exception e) {
59   - log.error("Can't connect to custom mapper endpoint", e);
60   - throw new RuntimeException("Can't connect to custom mapper endpoint", e);
  59 + log.error("There was an error during connection to custom mapper endpoint", e);
  60 + throw new RuntimeException("Unable to login. Please contact your Administrator!");
61 61 }
62 62 }
63 63 }
... ...
... ... @@ -32,6 +32,8 @@ import org.thingsboard.server.utils.MiscUtils;
32 32 import javax.servlet.http.HttpServletRequest;
33 33 import javax.servlet.http.HttpServletResponse;
34 34 import java.io.IOException;
  35 +import java.net.URLEncoder;
  36 +import java.nio.charset.StandardCharsets;
35 37
36 38 @Component(value = "oauth2AuthenticationSuccessHandler")
37 39 @ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true")
... ... @@ -57,16 +59,22 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
57 59 public void onAuthenticationSuccess(HttpServletRequest request,
58 60 HttpServletResponse response,
59 61 Authentication authentication) throws IOException {
60   - OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
61 62
62   - OAuth2Client oauth2Client = oauth2Configuration.getClientByRegistrationId(token.getAuthorizedClientRegistrationId());
63   - OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(oauth2Client.getMapperConfig().getType());
64   - SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oauth2Client.getMapperConfig());
  63 + String baseUrl = MiscUtils.constructBaseUrl(request);
  64 + try {
  65 + OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
65 66
66   - JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
67   - JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
  67 + OAuth2Client oauth2Client = oauth2Configuration.getClientByRegistrationId(token.getAuthorizedClientRegistrationId());
  68 + OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(oauth2Client.getMapperConfig().getType());
  69 + SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oauth2Client.getMapperConfig());
68 70
69   - String baseUrl = MiscUtils.constructBaseUrl(request);
70   - getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken());
  71 + JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
  72 + JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
  73 +
  74 + getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken());
  75 + } catch (Exception e) {
  76 + getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" +
  77 + URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.toString()));
  78 + }
71 79 }
72 80 }
\ No newline at end of file
... ...
... ... @@ -47,4 +47,13 @@ public enum Resource {
47 47 public Optional<EntityType> getEntityType() {
48 48 return Optional.ofNullable(entityType);
49 49 }
  50 +
  51 + public static Resource of(EntityType entityType) {
  52 + for (Resource resource : Resource.values()) {
  53 + if (resource.getEntityType().get() == entityType) {
  54 + return resource;
  55 + }
  56 + }
  57 + throw new IllegalArgumentException("Unknown EntityType: " + entityType.name());
  58 + }
50 59 }
... ...
... ... @@ -292,28 +292,38 @@ public class DefaultDeviceStateService implements DeviceStateService {
292 292 }
293 293 }
294 294
  295 + volatile Set<TopicPartitionInfo> pendingPartitions;
  296 +
295 297 @Override
296 298 public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
297 299 if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) {
298 300 synchronized (this) {
  301 + pendingPartitions = partitionChangeEvent.getPartitions();
299 302 if (!clusterUpdatePending) {
300 303 clusterUpdatePending = true;
301 304 queueExecutor.submit(() -> {
302 305 clusterUpdatePending = false;
303   - initStateFromDB(partitionChangeEvent.getPartitions());
  306 + initStateFromDB();
304 307 });
305 308 }
306 309 }
307 310 }
308 311 }
309 312
310   - private void initStateFromDB(Set<TopicPartitionInfo> partitions) {
  313 + private void initStateFromDB() {
311 314 try {
312   - Set<TopicPartitionInfo> addedPartitions = new HashSet<>(partitions);
  315 + log.info("CURRENT PARTITIONS: {}", partitionedDevices.keySet());
  316 + log.info("NEW PARTITIONS: {}", pendingPartitions);
  317 +
  318 + Set<TopicPartitionInfo> addedPartitions = new HashSet<>(pendingPartitions);
313 319 addedPartitions.removeAll(partitionedDevices.keySet());
314 320
  321 + log.info("ADDED PARTITIONS: {}", addedPartitions);
  322 +
315 323 Set<TopicPartitionInfo> removedPartitions = new HashSet<>(partitionedDevices.keySet());
316   - removedPartitions.removeAll(partitions);
  324 + removedPartitions.removeAll(pendingPartitions);
  325 +
  326 + log.info("REMOVED PARTITIONS: {}", removedPartitions);
317 327
318 328 // We no longer manage current partition of devices;
319 329 removedPartitions.forEach(partition -> {
... ...
... ... @@ -224,7 +224,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer
224 224 return null;
225 225 }
226 226 },
227   - s -> (StringUtils.isEmpty(s.getScope()) || scope.equals(s.getScope().name())),
  227 + s -> (TbAttributeSubscriptionScope.ANY_SCOPE.equals(s.getScope()) || scope.equals(s.getScope().name())),
228 228 s -> {
229 229 List<TsKvEntry> subscriptionUpdate = null;
230 230 for (AttributeKvEntry kv : attributes) {
... ...
... ... @@ -17,6 +17,6 @@ package org.thingsboard.server.service.subscription;
17 17
18 18 public enum TbAttributeSubscriptionScope {
19 19
20   - CLIENT_SCOPE, SHARED_SCOPE, SERVER_SCOPE
  20 + ANY_SCOPE, CLIENT_SCOPE, SHARED_SCOPE, SERVER_SCOPE
21 21
22 22 }
... ...
... ... @@ -345,7 +345,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
345 345 keys.forEach(key -> subState.put(key, 0L));
346 346 attributesData.forEach(v -> subState.put(v.getKey(), v.getTs()));
347 347
348   - TbAttributeSubscriptionScope scope = StringUtils.isEmpty(cmd.getScope()) ? TbAttributeSubscriptionScope.SERVER_SCOPE : TbAttributeSubscriptionScope.valueOf(cmd.getScope());
  348 + TbAttributeSubscriptionScope scope = StringUtils.isEmpty(cmd.getScope()) ? TbAttributeSubscriptionScope.ANY_SCOPE : TbAttributeSubscriptionScope.valueOf(cmd.getScope());
349 349
350 350 TbAttributeSubscription sub = TbAttributeSubscription.builder()
351 351 .serviceId(serviceId)
... ... @@ -442,7 +442,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
442 442 Map<String, Long> subState = new HashMap<>(attributesData.size());
443 443 attributesData.forEach(v -> subState.put(v.getKey(), v.getTs()));
444 444
445   - TbAttributeSubscriptionScope scope = StringUtils.isEmpty(cmd.getScope()) ? TbAttributeSubscriptionScope.SERVER_SCOPE : TbAttributeSubscriptionScope.valueOf(cmd.getScope());
  445 + TbAttributeSubscriptionScope scope = StringUtils.isEmpty(cmd.getScope()) ? TbAttributeSubscriptionScope.ANY_SCOPE : TbAttributeSubscriptionScope.valueOf(cmd.getScope());
446 446
447 447 TbAttributeSubscription sub = TbAttributeSubscription.builder()
448 448 .serviceId(serviceId)
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.service.transport;
17 17
18 18 import com.fasterxml.jackson.core.JsonProcessingException;
19 19 import com.fasterxml.jackson.databind.ObjectMapper;
  20 +import com.fasterxml.jackson.databind.node.ObjectNode;
20 21 import com.google.common.util.concurrent.Futures;
21 22 import com.google.common.util.concurrent.ListenableFuture;
22 23 import com.google.common.util.concurrent.MoreExecutors;
... ... @@ -24,13 +25,18 @@ import lombok.extern.slf4j.Slf4j;
24 25 import org.springframework.beans.factory.annotation.Autowired;
25 26 import org.springframework.stereotype.Service;
26 27 import org.springframework.util.StringUtils;
  28 +import org.thingsboard.server.common.data.DataConstants;
27 29 import org.thingsboard.server.common.data.Device;
28 30 import org.thingsboard.server.common.data.Tenant;
  31 +import org.thingsboard.server.common.data.id.CustomerId;
29 32 import org.thingsboard.server.common.data.id.DeviceId;
30 33 import org.thingsboard.server.common.data.id.TenantId;
31 34 import org.thingsboard.server.common.data.relation.EntityRelation;
32 35 import org.thingsboard.server.common.data.security.DeviceCredentials;
33 36 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
  37 +import org.thingsboard.server.common.msg.TbMsg;
  38 +import org.thingsboard.server.common.msg.TbMsgDataType;
  39 +import org.thingsboard.server.common.msg.TbMsgMetaData;
34 40 import org.thingsboard.server.dao.device.DeviceCredentialsService;
35 41 import org.thingsboard.server.dao.device.DeviceService;
36 42 import org.thingsboard.server.dao.relation.RelationService;
... ... @@ -48,6 +54,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509Ce
48 54 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
49 55 import org.thingsboard.server.queue.util.TbCoreComponent;
50 56 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
  57 +import org.thingsboard.server.service.queue.TbClusterService;
51 58 import org.thingsboard.server.service.state.DeviceStateService;
52 59
53 60 import java.util.UUID;
... ... @@ -82,6 +89,9 @@ public class DefaultTransportApiService implements TransportApiService {
82 89 @Autowired
83 90 private DbCallbackExecutorService dbCallbackExecutorService;
84 91
  92 + @Autowired
  93 + protected TbClusterService tbClusterService;
  94 +
85 95 private ReentrantLock deviceCreationLock = new ReentrantLock();
86 96
87 97 @Override
... ... @@ -119,14 +129,27 @@ public class DefaultTransportApiService implements TransportApiService {
119 129 try {
120 130 Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), requestMsg.getDeviceName());
121 131 if (device == null) {
  132 + TenantId tenantId = gateway.getTenantId();
122 133 device = new Device();
123   - device.setTenantId(gateway.getTenantId());
  134 + device.setTenantId(tenantId);
124 135 device.setName(requestMsg.getDeviceName());
125 136 device.setType(requestMsg.getDeviceType());
126 137 device.setCustomerId(gateway.getCustomerId());
127 138 device = deviceService.saveDevice(device);
128 139 relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created"));
129 140 deviceStateService.onDeviceAdded(device);
  141 +
  142 + TbMsgMetaData metaData = new TbMsgMetaData();
  143 + CustomerId customerId = gateway.getCustomerId();
  144 + if (customerId != null && !customerId.isNullUid()) {
  145 + metaData.putValue("customerId", customerId.toString());
  146 + }
  147 + metaData.putValue("gatewayId", gatewayId.toString());
  148 +
  149 + DeviceId deviceId = device.getId();
  150 + ObjectNode entityNode = mapper.valueToTree(device);
  151 + TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, metaData, TbMsgDataType.JSON, mapper.writeValueAsString(entityNode));
  152 + tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, null);
130 153 }
131 154 return TransportApiResponseMsg.newBuilder()
132 155 .setGetOrCreateDeviceResponseMsg(GetOrCreateDeviceFromGatewayResponseMsg.newBuilder().setDeviceInfo(getDeviceInfoProto(device)).build()).build();
... ...
... ... @@ -54,6 +54,13 @@ server:
54 54 customer:
55 55 enabled: "${TB_SERVER_REST_LIMITS_CUSTOMER_ENABLED:false}"
56 56 configuration: "${TB_SERVER_REST_LIMITS_CUSTOMER_CONFIGURATION:50:1,1000:60}"
  57 + server_side_rpc:
  58 + # Minimum value of the server side RPC timeout. May override value provided in the REST API call.
  59 + # Since 2.5 migration to queues, the RPC delay depends on the size of the pending messages in the queue,
  60 + # so default UI parameter of 500ms may not be sufficient for loaded environments.
  61 + min_timeout: "${MIN_SERVER_SIDE_RPC_TIMEOUT:5000}"
  62 + # Default value of the server side RPC timeout.
  63 + default_timeout: "${DEFAULT_SERVER_SIDE_RPC_TIMEOUT:10000}"
57 64
58 65 # Zookeeper connection parameters. Used for service discovery.
59 66 zk:
... ... @@ -148,6 +155,10 @@ security:
148 155 # If this field is not empty, user will be created as a user under defined Customer
149 156 # %{attribute_key} as placeholder for attribute value of attributes of external user object
150 157 customerNamePattern: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_CUSTOMER_NAME_PATTERN:}"
  158 + # If this field is not empty, user will be created with default defined Dashboard
  159 + defaultDashboardName: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_DEFAULT_DASHBOARD_NAME:}"
  160 + # If this field is set 'true' along with non-empty 'defaultDashboardName', user will start from the defined Dashboard in fullscreen mode
  161 + alwaysFullScreen: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_ALWAYS_FULL_SCREEN:false}"
151 162 custom:
152 163 url: "${SECURITY_OAUTH2_DEFAULT_MAPPER_CUSTOM_URL:}"
153 164 username: "${SECURITY_OAUTH2_DEFAULT_MAPPER_CUSTOM_USERNAME:}"
... ... @@ -676,7 +687,7 @@ queue:
676 687 topic: "${TB_QUEUE_CORE_TOPIC:tb_core}"
677 688 poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
678 689 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
679   - pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}"
  690 + pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:2000}"
680 691 stats:
681 692 enabled: "${TB_QUEUE_CORE_STATS_ENABLED:true}"
682 693 print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:60000}"
... ... @@ -688,6 +699,8 @@ queue:
688 699 # JS Eval max pending requests
689 700 max_pending_requests: "${REMOTE_JS_MAX_PENDING_REQUESTS:10000}"
690 701 # JS Eval max request timeout
  702 + max_eval_requests_timeout: "${REMOTE_JS_MAX_EVAL_REQUEST_TIMEOUT:60000}"
  703 + # JS max request timeout
691 704 max_requests_timeout: "${REMOTE_JS_MAX_REQUEST_TIMEOUT:10000}"
692 705 # JS response poll interval
693 706 response_poll_interval: "${REMOTE_JS_RESPONSE_POLL_INTERVAL_MS:25}"
... ... @@ -696,7 +709,7 @@ queue:
696 709 rule-engine:
697 710 topic: "${TB_QUEUE_RULE_ENGINE_TOPIC:tb_rule_engine}"
698 711 poll-interval: "${TB_QUEUE_RULE_ENGINE_POLL_INTERVAL_MS:25}"
699   - pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:60000}"
  712 + pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:2000}"
700 713 stats:
701 714 enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}"
702 715 print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:60000}"
... ... @@ -705,7 +718,7 @@ queue:
705 718 topic: "${TB_QUEUE_RE_MAIN_TOPIC:tb_rule_engine.main}"
706 719 poll-interval: "${TB_QUEUE_RE_MAIN_POLL_INTERVAL_MS:25}"
707 720 partitions: "${TB_QUEUE_RE_MAIN_PARTITIONS:10}"
708   - pack-processing-timeout: "${TB_QUEUE_RE_MAIN_PACK_PROCESSING_TIMEOUT_MS:60000}"
  721 + pack-processing-timeout: "${TB_QUEUE_RE_MAIN_PACK_PROCESSING_TIMEOUT_MS:2000}"
709 722 submit-strategy:
710 723 type: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
711 724 # For BATCH only
... ... @@ -720,7 +733,7 @@ queue:
720 733 topic: "${TB_QUEUE_RE_HP_TOPIC:tb_rule_engine.hp}"
721 734 poll-interval: "${TB_QUEUE_RE_HP_POLL_INTERVAL_MS:25}"
722 735 partitions: "${TB_QUEUE_RE_HP_PARTITIONS:10}"
723   - pack-processing-timeout: "${TB_QUEUE_RE_HP_PACK_PROCESSING_TIMEOUT_MS:60000}"
  736 + pack-processing-timeout: "${TB_QUEUE_RE_HP_PACK_PROCESSING_TIMEOUT_MS:2000}"
724 737 submit-strategy:
725 738 type: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
726 739 # For BATCH only
... ... @@ -735,7 +748,7 @@ queue:
735 748 topic: "${TB_QUEUE_RE_SQ_TOPIC:tb_rule_engine.sq}"
736 749 poll-interval: "${TB_QUEUE_RE_SQ_POLL_INTERVAL_MS:25}"
737 750 partitions: "${TB_QUEUE_RE_SQ_PARTITIONS:10}"
738   - pack-processing-timeout: "${TB_QUEUE_RE_SQ_PACK_PROCESSING_TIMEOUT_MS:60000}"
  751 + pack-processing-timeout: "${TB_QUEUE_RE_SQ_PACK_PROCESSING_TIMEOUT_MS:2000}"
739 752 submit-strategy:
740 753 type: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_TYPE:SEQUENTIAL_BY_ORIGINATOR}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
741 754 # For BATCH only
... ...
... ... @@ -33,7 +33,6 @@ import org.junit.rules.TestRule;
33 33 import org.junit.rules.TestWatcher;
34 34 import org.junit.runner.Description;
35 35 import org.junit.runner.RunWith;
36   -import org.mockito.Mockito;
37 36 import org.springframework.beans.factory.annotation.Autowired;
38 37 import org.springframework.boot.test.context.SpringBootContextLoader;
39 38 import org.springframework.boot.test.context.SpringBootTest;
... ... @@ -49,7 +48,6 @@ import org.springframework.mock.http.MockHttpOutputMessage;
49 48 import org.springframework.test.annotation.DirtiesContext;
50 49 import org.springframework.test.context.ActiveProfiles;
51 50 import org.springframework.test.context.ContextConfiguration;
52   -import org.springframework.test.context.TestPropertySource;
53 51 import org.springframework.test.context.junit4.SpringRunner;
54 52 import org.springframework.test.context.web.WebAppConfiguration;
55 53 import org.springframework.test.web.servlet.MockMvc;
... ... @@ -75,7 +73,6 @@ import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRequest;
75 73 import org.thingsboard.server.service.security.auth.rest.LoginRequest;
76 74
77 75 import java.io.IOException;
78   -import java.nio.charset.Charset;
79 76 import java.util.ArrayList;
80 77 import java.util.Arrays;
81 78 import java.util.Comparator;
... ... @@ -223,6 +220,27 @@ public abstract class AbstractControllerTest {
223 220 login(CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD);
224 221 }
225 222
  223 + private Tenant savedDifferentTenant;
  224 + protected void loginDifferentTenant() throws Exception {
  225 + loginSysAdmin();
  226 + Tenant tenant = new Tenant();
  227 + tenant.setTitle("Different tenant");
  228 + savedDifferentTenant = doPost("/api/tenant", tenant, Tenant.class);
  229 + Assert.assertNotNull(savedDifferentTenant);
  230 + User differentTenantAdmin = new User();
  231 + differentTenantAdmin.setAuthority(Authority.TENANT_ADMIN);
  232 + differentTenantAdmin.setTenantId(savedDifferentTenant.getId());
  233 + differentTenantAdmin.setEmail("different_tenant@thingsboard.org");
  234 +
  235 + createUserAndLogin(differentTenantAdmin, "testPassword");
  236 + }
  237 +
  238 + protected void deleteDifferentTenant() throws Exception {
  239 + loginSysAdmin();
  240 + doDelete("/api/tenant/" + savedDifferentTenant.getId().getId().toString())
  241 + .andExpect(status().isOk());
  242 + }
  243 +
226 244 protected User createUserAndLogin(User user, String password) throws Exception {
227 245 User savedUser = doPost("/api/user", user, User.class);
228 246 logout();
... ...
... ... @@ -99,6 +99,18 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
99 99 }
100 100
101 101 @Test
  102 + public void testUpdateAssetFromDifferentTenant() throws Exception {
  103 + Asset asset = new Asset();
  104 + asset.setName("My asset");
  105 + asset.setType("default");
  106 + Asset savedAsset = doPost("/api/asset", asset, Asset.class);
  107 +
  108 + loginDifferentTenant();
  109 + doPost("/api/asset", savedAsset, Asset.class, status().isForbidden());
  110 + deleteDifferentTenant();
  111 + }
  112 +
  113 + @Test
102 114 public void testFindAssetById() throws Exception {
103 115 Asset asset = new Asset();
104 116 asset.setName("My asset");
... ...
... ... @@ -23,6 +23,8 @@ import java.util.Collections;
23 23 import java.util.List;
24 24
25 25 import org.apache.commons.lang3.RandomStringUtils;
  26 +import org.junit.After;
  27 +import org.junit.Before;
26 28 import org.thingsboard.server.common.data.Customer;
27 29 import org.thingsboard.server.common.data.Tenant;
28 30 import org.thingsboard.server.common.data.User;
... ... @@ -38,25 +40,39 @@ import com.fasterxml.jackson.core.type.TypeReference;
38 40 public abstract class BaseCustomerControllerTest extends AbstractControllerTest {
39 41
40 42 private IdComparator<Customer> idComparator = new IdComparator<>();
41   -
42   - @Test
43   - public void testSaveCustomer() throws Exception {
  43 +
  44 + private Tenant savedTenant;
  45 + private User tenantAdmin;
  46 +
  47 + @Before
  48 + public void beforeTest() throws Exception {
44 49 loginSysAdmin();
45 50
46 51 Tenant tenant = new Tenant();
47 52 tenant.setTitle("My tenant");
48   - Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
  53 + savedTenant = doPost("/api/tenant", tenant, Tenant.class);
49 54 Assert.assertNotNull(savedTenant);
50   -
51   - User tenantAdmin = new User();
  55 +
  56 + tenantAdmin = new User();
52 57 tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
53 58 tenantAdmin.setTenantId(savedTenant.getId());
54 59 tenantAdmin.setEmail("tenant2@thingsboard.org");
55 60 tenantAdmin.setFirstName("Joe");
56 61 tenantAdmin.setLastName("Downs");
57   -
  62 +
58 63 tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
59   -
  64 + }
  65 +
  66 + @After
  67 + public void afterTest() throws Exception {
  68 + loginSysAdmin();
  69 +
  70 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  71 + .andExpect(status().isOk());
  72 + }
  73 +
  74 + @Test
  75 + public void testSaveCustomer() throws Exception {
60 76 Customer customer = new Customer();
61 77 customer.setTitle("My customer");
62 78 Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
... ... @@ -66,266 +82,159 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
66 82 Assert.assertEquals(customer.getTitle(), savedCustomer.getTitle());
67 83 savedCustomer.setTitle("My new customer");
68 84 doPost("/api/customer", savedCustomer, Customer.class);
69   -
70   - Customer foundCustomer = doGet("/api/customer/"+savedCustomer.getId().getId().toString(), Customer.class);
  85 +
  86 + Customer foundCustomer = doGet("/api/customer/"+savedCustomer.getId().getId().toString(), Customer.class);
71 87 Assert.assertEquals(foundCustomer.getTitle(), savedCustomer.getTitle());
72   -
  88 +
73 89 doDelete("/api/customer/"+savedCustomer.getId().getId().toString())
74 90 .andExpect(status().isOk());
75   -
76   - loginSysAdmin();
77   -
78   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
79   - .andExpect(status().isOk());
80 91 }
81   -
  92 +
82 93 @Test
83   - public void testFindCustomerById() throws Exception {
84   -
85   - loginSysAdmin();
  94 + public void testUpdateCustomerFromDifferentTenant() throws Exception {
  95 + Customer customer = new Customer();
  96 + customer.setTitle("My customer");
  97 + Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
  98 + doPost("/api/customer", savedCustomer, Customer.class);
86 99
87   - Tenant tenant = new Tenant();
88   - tenant.setTitle("My tenant");
89   - Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
90   - Assert.assertNotNull(savedTenant);
91   -
92   - User tenantAdmin = new User();
93   - tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
94   - tenantAdmin.setTenantId(savedTenant.getId());
95   - tenantAdmin.setEmail("tenant2@thingsboard.org");
96   - tenantAdmin.setFirstName("Joe");
97   - tenantAdmin.setLastName("Downs");
98   -
99   - tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
100   -
  100 + loginDifferentTenant();
  101 + doPost("/api/customer", savedCustomer, Customer.class, status().isForbidden());
  102 + deleteDifferentTenant();
  103 +
  104 + login(tenantAdmin.getName(), "testPassword1");
  105 + doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
  106 + .andExpect(status().isOk());
  107 + }
  108 +
  109 + @Test
  110 + public void testFindCustomerById() throws Exception {
101 111 Customer customer = new Customer();
102 112 customer.setTitle("My customer");
103 113 Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
104   -
105   - Customer foundCustomer = doGet("/api/customer/"+savedCustomer.getId().getId().toString(), Customer.class);
  114 +
  115 + Customer foundCustomer = doGet("/api/customer/" + savedCustomer.getId().getId().toString(), Customer.class);
106 116 Assert.assertNotNull(foundCustomer);
107 117 Assert.assertEquals(savedCustomer, foundCustomer);
108   -
109   - doDelete("/api/customer/"+savedCustomer.getId().getId().toString())
110   - .andExpect(status().isOk());
111   -
112   - loginSysAdmin();
113   -
114   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
115   - .andExpect(status().isOk());
  118 +
  119 + doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
  120 + .andExpect(status().isOk());
116 121 }
117   -
  122 +
118 123 @Test
119 124 public void testDeleteCustomer() throws Exception {
120   -
121   - loginSysAdmin();
122   -
123   - Tenant tenant = new Tenant();
124   - tenant.setTitle("My tenant");
125   - Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
126   - Assert.assertNotNull(savedTenant);
127   -
128   - User tenantAdmin = new User();
129   - tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
130   - tenantAdmin.setTenantId(savedTenant.getId());
131   - tenantAdmin.setEmail("tenant2@thingsboard.org");
132   - tenantAdmin.setFirstName("Joe");
133   - tenantAdmin.setLastName("Downs");
134   -
135   - tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
136   -
137 125 Customer customer = new Customer();
138 126 customer.setTitle("My customer");
139 127 Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
140   -
141   - doDelete("/api/customer/"+savedCustomer.getId().getId().toString())
142   - .andExpect(status().isOk());
143 128
144   - doGet("/api/customer/"+savedCustomer.getId().getId().toString())
145   - .andExpect(status().isNotFound());
146   -
147   - loginSysAdmin();
148   -
149   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
150   - .andExpect(status().isOk());
  129 + doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
  130 + .andExpect(status().isOk());
  131 +
  132 + doGet("/api/customer/" + savedCustomer.getId().getId().toString())
  133 + .andExpect(status().isNotFound());
151 134 }
152   -
  135 +
153 136 @Test
154 137 public void testSaveCustomerWithEmptyTitle() throws Exception {
155   -
156   - loginSysAdmin();
157   -
158   - Tenant tenant = new Tenant();
159   - tenant.setTitle("My tenant");
160   - Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
161   - Assert.assertNotNull(savedTenant);
162   -
163   - User tenantAdmin = new User();
164   - tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
165   - tenantAdmin.setTenantId(savedTenant.getId());
166   - tenantAdmin.setEmail("tenant2@thingsboard.org");
167   - tenantAdmin.setFirstName("Joe");
168   - tenantAdmin.setLastName("Downs");
169   -
170   - tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
171   -
172 138 Customer customer = new Customer();
173 139 doPost("/api/customer", customer)
174   - .andExpect(status().isBadRequest())
175   - .andExpect(statusReason(containsString("Customer title should be specified")));
176   -
177   - loginSysAdmin();
178   -
179   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
180   - .andExpect(status().isOk());
  140 + .andExpect(status().isBadRequest())
  141 + .andExpect(statusReason(containsString("Customer title should be specified")));
181 142 }
182   -
  143 +
183 144 @Test
184 145 public void testSaveCustomerWithInvalidEmail() throws Exception {
185   -
186   - loginSysAdmin();
187   -
188   - Tenant tenant = new Tenant();
189   - tenant.setTitle("My tenant");
190   - Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
191   - Assert.assertNotNull(savedTenant);
192   -
193   - User tenantAdmin = new User();
194   - tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
195   - tenantAdmin.setTenantId(savedTenant.getId());
196   - tenantAdmin.setEmail("tenant2@thingsboard.org");
197   - tenantAdmin.setFirstName("Joe");
198   - tenantAdmin.setLastName("Downs");
199   -
200   - tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
201   -
202 146 Customer customer = new Customer();
203 147 customer.setTitle("My customer");
204 148 customer.setEmail("invalid@mail");
205 149 doPost("/api/customer", customer)
206   - .andExpect(status().isBadRequest())
207   - .andExpect(statusReason(containsString("Invalid email address format 'invalid@mail'")));
208   -
209   - loginSysAdmin();
210   -
211   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
212   - .andExpect(status().isOk());
  150 + .andExpect(status().isBadRequest())
  151 + .andExpect(statusReason(containsString("Invalid email address format 'invalid@mail'")));
  152 +
  153 +// loginSysAdmin();
  154 +//
  155 +// doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
  156 +// .andExpect(status().isOk());
213 157 }
214   -
  158 +
215 159 @Test
216 160 public void testFindCustomers() throws Exception {
217   - loginSysAdmin();
218   -
219   - Tenant tenant = new Tenant();
220   - tenant.setTitle("My tenant");
221   - Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
222   - Assert.assertNotNull(savedTenant);
223   -
224 161 TenantId tenantId = savedTenant.getId();
225   -
226   - User tenantAdmin = new User();
227   - tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
228   - tenantAdmin.setTenantId(tenantId);
229   - tenantAdmin.setEmail("tenant2@thingsboard.org");
230   - tenantAdmin.setFirstName("Joe");
231   - tenantAdmin.setLastName("Downs");
232   -
233   - tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
234   -
  162 +
235 163 List<Customer> customers = new ArrayList<>();
236   - for (int i=0;i<135;i++) {
  164 + for (int i = 0; i < 135; i++) {
237 165 Customer customer = new Customer();
238 166 customer.setTenantId(tenantId);
239   - customer.setTitle("Customer"+i);
  167 + customer.setTitle("Customer" + i);
240 168 customers.add(doPost("/api/customer", customer, Customer.class));
241 169 }
242   -
  170 +
243 171 List<Customer> loadedCustomers = new ArrayList<>();
244 172 TextPageLink pageLink = new TextPageLink(23);
245 173 TextPageData<Customer> pageData = null;
246 174 do {
247   - pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<TextPageData<Customer>>(){}, pageLink);
  175 + pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<TextPageData<Customer>>() {
  176 + }, pageLink);
248 177 loadedCustomers.addAll(pageData.getData());
249 178 if (pageData.hasNext()) {
250 179 pageLink = pageData.getNextPageLink();
251 180 }
252 181 } while (pageData.hasNext());
253   -
  182 +
254 183 Collections.sort(customers, idComparator);
255 184 Collections.sort(loadedCustomers, idComparator);
256   -
  185 +
257 186 Assert.assertEquals(customers, loadedCustomers);
258   -
259   - loginSysAdmin();
260   -
261   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
262   - .andExpect(status().isOk());
263 187 }
264   -
  188 +
265 189 @Test
266 190 public void testFindCustomersByTitle() throws Exception {
267   -
268   - loginSysAdmin();
269   -
270   - Tenant tenant = new Tenant();
271   - tenant.setTitle("My tenant");
272   - Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
273   - Assert.assertNotNull(savedTenant);
274   -
275 191 TenantId tenantId = savedTenant.getId();
276   -
277   - User tenantAdmin = new User();
278   - tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
279   - tenantAdmin.setTenantId(tenantId);
280   - tenantAdmin.setEmail("tenant2@thingsboard.org");
281   - tenantAdmin.setFirstName("Joe");
282   - tenantAdmin.setLastName("Downs");
283   -
284   - tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
285   -
  192 +
286 193 String title1 = "Customer title 1";
287 194 List<Customer> customersTitle1 = new ArrayList<>();
288   - for (int i=0;i<143;i++) {
  195 + for (int i = 0; i < 143; i++) {
289 196 Customer customer = new Customer();
290 197 customer.setTenantId(tenantId);
291   - String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
292   - String title = title1+suffix;
  198 + String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
  199 + String title = title1 + suffix;
293 200 title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
294 201 customer.setTitle(title);
295 202 customersTitle1.add(doPost("/api/customer", customer, Customer.class));
296 203 }
297 204 String title2 = "Customer title 2";
298 205 List<Customer> customersTitle2 = new ArrayList<>();
299   - for (int i=0;i<175;i++) {
  206 + for (int i = 0; i < 175; i++) {
300 207 Customer customer = new Customer();
301 208 customer.setTenantId(tenantId);
302   - String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
303   - String title = title2+suffix;
  209 + String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
  210 + String title = title2 + suffix;
304 211 title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
305 212 customer.setTitle(title);
306 213 customersTitle2.add(doPost("/api/customer", customer, Customer.class));
307 214 }
308   -
  215 +
309 216 List<Customer> loadedCustomersTitle1 = new ArrayList<>();
310 217 TextPageLink pageLink = new TextPageLink(15, title1);
311 218 TextPageData<Customer> pageData = null;
312 219 do {
313   - pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<TextPageData<Customer>>(){}, pageLink);
  220 + pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<TextPageData<Customer>>() {
  221 + }, pageLink);
314 222 loadedCustomersTitle1.addAll(pageData.getData());
315 223 if (pageData.hasNext()) {
316 224 pageLink = pageData.getNextPageLink();
317 225 }
318 226 } while (pageData.hasNext());
319   -
  227 +
320 228 Collections.sort(customersTitle1, idComparator);
321 229 Collections.sort(loadedCustomersTitle1, idComparator);
322   -
  230 +
323 231 Assert.assertEquals(customersTitle1, loadedCustomersTitle1);
324   -
  232 +
325 233 List<Customer> loadedCustomersTitle2 = new ArrayList<>();
326 234 pageLink = new TextPageLink(4, title2);
327 235 do {
328   - pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<TextPageData<Customer>>(){}, pageLink);
  236 + pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<TextPageData<Customer>>() {
  237 + }, pageLink);
329 238 loadedCustomersTitle2.addAll(pageData.getData());
330 239 if (pageData.hasNext()) {
331 240 pageLink = pageData.getNextPageLink();
... ... @@ -334,33 +243,30 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
334 243
335 244 Collections.sort(customersTitle2, idComparator);
336 245 Collections.sort(loadedCustomersTitle2, idComparator);
337   -
  246 +
338 247 Assert.assertEquals(customersTitle2, loadedCustomersTitle2);
339   -
  248 +
340 249 for (Customer customer : loadedCustomersTitle1) {
341   - doDelete("/api/customer/"+customer.getId().getId().toString())
342   - .andExpect(status().isOk());
  250 + doDelete("/api/customer/" + customer.getId().getId().toString())
  251 + .andExpect(status().isOk());
343 252 }
344   -
  253 +
345 254 pageLink = new TextPageLink(4, title1);
346   - pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<TextPageData<Customer>>(){}, pageLink);
  255 + pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<TextPageData<Customer>>() {
  256 + }, pageLink);
347 257 Assert.assertFalse(pageData.hasNext());
348 258 Assert.assertEquals(0, pageData.getData().size());
349   -
  259 +
350 260 for (Customer customer : loadedCustomersTitle2) {
351   - doDelete("/api/customer/"+customer.getId().getId().toString())
352   - .andExpect(status().isOk());
  261 + doDelete("/api/customer/" + customer.getId().getId().toString())
  262 + .andExpect(status().isOk());
353 263 }
354   -
  264 +
355 265 pageLink = new TextPageLink(4, title2);
356   - pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<TextPageData<Customer>>(){}, pageLink);
  266 + pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<TextPageData<Customer>>() {
  267 + }, pageLink);
357 268 Assert.assertFalse(pageData.hasNext());
358 269 Assert.assertEquals(0, pageData.getData().size());
359   -
360   - loginSysAdmin();
361   -
362   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
363   - .andExpect(status().isOk());
364 270 }
365   -
  271 +
366 272 }
... ...
... ... @@ -16,10 +16,8 @@
16 16 package org.thingsboard.server.controller;
17 17
18 18 import static org.hamcrest.Matchers.containsString;
19   -import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
20 19 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
21 20
22   -import java.sql.Time;
23 21 import java.util.ArrayList;
24 22 import java.util.Collections;
25 23 import java.util.List;
... ... @@ -33,7 +31,6 @@ import org.thingsboard.server.common.data.page.TextPageLink;
33 31 import org.thingsboard.server.common.data.page.TimePageData;
34 32 import org.thingsboard.server.common.data.page.TimePageLink;
35 33 import org.thingsboard.server.common.data.security.Authority;
36   -import org.thingsboard.server.dao.model.ModelConstants;
37 34 import org.junit.After;
38 35 import org.junit.Assert;
39 36 import org.junit.Before;
... ... @@ -93,6 +90,17 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
93 90 Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class);
94 91 Assert.assertEquals(foundDashboard.getTitle(), savedDashboard.getTitle());
95 92 }
  93 +
  94 + @Test
  95 + public void testUpdateDashboardFromDifferentTenant() throws Exception {
  96 + Dashboard dashboard = new Dashboard();
  97 + dashboard.setTitle("My dashboard");
  98 + Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class);
  99 +
  100 + loginDifferentTenant();
  101 + doPost("/api/dashboard", savedDashboard, Dashboard.class, status().isForbidden());
  102 + deleteDifferentTenant();
  103 + }
96 104
97 105 @Test
98 106 public void testFindDashboardById() throws Exception {
... ...
... ... @@ -107,6 +107,17 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
107 107 Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
108 108 Assert.assertEquals(foundDevice.getName(), savedDevice.getName());
109 109 }
  110 +
  111 + @Test
  112 + public void testUpdateDeviceFromDifferentTenant() throws Exception {
  113 + Device device = new Device();
  114 + device.setName("My device");
  115 + device.setType("default");
  116 + Device savedDevice = doPost("/api/device", device, Device.class);
  117 + loginDifferentTenant();
  118 + doPost("/api/device", savedDevice, Device.class, status().isForbidden());
  119 + deleteDifferentTenant();
  120 + }
110 121
111 122 @Test
112 123 public void testFindDeviceById() throws Exception {
... ...
... ... @@ -25,7 +25,6 @@ import org.eclipse.paho.client.mqttv3.MqttMessage;
25 25 import org.junit.After;
26 26 import org.junit.Assert;
27 27 import org.junit.Before;
28   -import org.junit.Ignore;
29 28 import org.junit.Test;
30 29 import org.thingsboard.server.common.data.Customer;
31 30 import org.thingsboard.server.common.data.Device;
... ... @@ -132,6 +131,15 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
132 131 assertEquals(foundEntityView.getKeys(), telemetry);
133 132 }
134 133
  134 +
  135 + @Test
  136 + public void testUpdateEntityViewFromDifferentTenant() throws Exception {
  137 + EntityView savedView = getNewSavedEntityView("Test entity view");
  138 + loginDifferentTenant();
  139 + doPost("/api/entityView", savedView, EntityView.class, status().isForbidden());
  140 + deleteDifferentTenant();
  141 + }
  142 +
135 143 @Test
136 144 public void testDeleteEntityView() throws Exception {
137 145 EntityView view = getNewSavedEntityView("Test entity view");
... ...
... ... @@ -38,21 +38,23 @@ import java.util.List;
38 38
39 39 import static org.hamcrest.Matchers.containsString;
40 40 import static org.hamcrest.Matchers.is;
41   -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
  41 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
  42 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
  43 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
42 44
43 45 public abstract class BaseUserControllerTest extends AbstractControllerTest {
44   -
  46 +
45 47 private IdComparator<User> idComparator = new IdComparator<>();
46 48
47 49 @Test
48 50 public void testSaveUser() throws Exception {
49 51 loginSysAdmin();
50   -
  52 +
51 53 Tenant tenant = new Tenant();
52 54 tenant.setTitle("My tenant");
53 55 Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
54 56 Assert.assertNotNull(savedTenant);
55   -
  57 +
56 58 String email = "tenant2@thingsboard.org";
57 59 User user = new User();
58 60 user.setAuthority(Authority.TENANT_ADMIN);
... ... @@ -66,13 +68,13 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
66 68 Assert.assertTrue(savedUser.getCreatedTime() > 0);
67 69 Assert.assertEquals(user.getEmail(), savedUser.getEmail());
68 70
69   - User foundUser = doGet("/api/user/"+savedUser.getId().getId().toString(), User.class);
  71 + User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
70 72 Assert.assertEquals(foundUser, savedUser);
71   -
  73 +
72 74 logout();
73 75 doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken)
74   - .andExpect(status().isSeeOther())
75   - .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken));
  76 + .andExpect(status().isSeeOther())
  77 + .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken));
76 78
77 79 JsonNode activateRequest = new ObjectMapper().createObjectNode()
78 80 .put("activateToken", TestMailService.currentActivateToken)
... ... @@ -82,36 +84,61 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
82 84 validateAndSetJwtToken(tokenInfo, email);
83 85
84 86 doGet("/api/auth/user")
85   - .andExpect(status().isOk())
86   - .andExpect(jsonPath("$.authority",is(Authority.TENANT_ADMIN.name())))
87   - .andExpect(jsonPath("$.email",is(email)));
88   -
  87 + .andExpect(status().isOk())
  88 + .andExpect(jsonPath("$.authority", is(Authority.TENANT_ADMIN.name())))
  89 + .andExpect(jsonPath("$.email", is(email)));
  90 +
89 91 logout();
90   -
  92 +
91 93 login(email, "testPassword");
92   -
  94 +
93 95 doGet("/api/auth/user")
94   - .andExpect(status().isOk())
95   - .andExpect(jsonPath("$.authority",is(Authority.TENANT_ADMIN.name())))
96   - .andExpect(jsonPath("$.email",is(email)));
97   -
  96 + .andExpect(status().isOk())
  97 + .andExpect(jsonPath("$.authority", is(Authority.TENANT_ADMIN.name())))
  98 + .andExpect(jsonPath("$.email", is(email)));
  99 +
98 100 loginSysAdmin();
99   - doDelete("/api/user/"+savedUser.getId().getId().toString())
100   - .andExpect(status().isOk());
101   -
102   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
103   - .andExpect(status().isOk());
  101 + doDelete("/api/user/" + savedUser.getId().getId().toString())
  102 + .andExpect(status().isOk());
  103 +
  104 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  105 + .andExpect(status().isOk());
104 106 }
105   -
  107 +
  108 + @Test
  109 + public void testUpdateUserFromDifferentTenant() throws Exception {
  110 + loginSysAdmin();
  111 + Tenant tenant = new Tenant();
  112 + tenant.setTitle("My tenant");
  113 + Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
  114 + Assert.assertNotNull(savedTenant);
  115 +
  116 + User tenantAdmin = new User();
  117 + tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
  118 + tenantAdmin.setTenantId(savedTenant.getId());
  119 + tenantAdmin.setEmail("tenant2@thingsboard.org");
  120 + tenantAdmin.setFirstName("Joe");
  121 + tenantAdmin.setLastName("Downs");
  122 + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
  123 +
  124 + loginDifferentTenant();
  125 + doPost("/api/user", tenantAdmin, User.class, status().isForbidden());
  126 + deleteDifferentTenant();
  127 +
  128 + loginSysAdmin();
  129 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  130 + .andExpect(status().isOk());
  131 + }
  132 +
106 133 @Test
107 134 public void testResetPassword() throws Exception {
108 135 loginSysAdmin();
109   -
  136 +
110 137 Tenant tenant = new Tenant();
111 138 tenant.setTitle("My tenant");
112 139 Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
113 140 Assert.assertNotNull(savedTenant);
114   -
  141 +
115 142 String email = "tenant2@thingsboard.org";
116 143 User user = new User();
117 144 user.setAuthority(Authority.TENANT_ADMIN);
... ... @@ -119,7 +146,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
119 146 user.setEmail(email);
120 147 user.setFirstName("Joe");
121 148 user.setLastName("Downs");
122   -
  149 +
123 150 User savedUser = createUserAndLogin(user, "testPassword1");
124 151 logout();
125 152
... ... @@ -127,10 +154,10 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
127 154 .put("email", email);
128 155
129 156 doPost("/api/noauth/resetPasswordByEmail", resetPasswordByEmailRequest)
130   - .andExpect(status().isOk());
  157 + .andExpect(status().isOk());
131 158 doGet("/api/noauth/resetPassword?resetToken={resetToken}", TestMailService.currentResetPasswordToken)
132   - .andExpect(status().isSeeOther())
133   - .andExpect(header().string(HttpHeaders.LOCATION, "/login/resetPassword?resetToken=" + TestMailService.currentResetPasswordToken));
  159 + .andExpect(status().isSeeOther())
  160 + .andExpect(header().string(HttpHeaders.LOCATION, "/login/resetPassword?resetToken=" + TestMailService.currentResetPasswordToken));
134 161
135 162 JsonNode resetPasswordRequest = new ObjectMapper().createObjectNode()
136 163 .put("resetToken", TestMailService.currentResetPasswordToken)
... ... @@ -140,35 +167,35 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
140 167 validateAndSetJwtToken(tokenInfo, email);
141 168
142 169 doGet("/api/auth/user")
143   - .andExpect(status().isOk())
144   - .andExpect(jsonPath("$.authority",is(Authority.TENANT_ADMIN.name())))
145   - .andExpect(jsonPath("$.email",is(email)));
146   -
  170 + .andExpect(status().isOk())
  171 + .andExpect(jsonPath("$.authority", is(Authority.TENANT_ADMIN.name())))
  172 + .andExpect(jsonPath("$.email", is(email)));
  173 +
147 174 logout();
148   -
  175 +
149 176 login(email, "testPassword2");
150 177 doGet("/api/auth/user")
151   - .andExpect(status().isOk())
152   - .andExpect(jsonPath("$.authority",is(Authority.TENANT_ADMIN.name())))
153   - .andExpect(jsonPath("$.email",is(email)));
154   -
  178 + .andExpect(status().isOk())
  179 + .andExpect(jsonPath("$.authority", is(Authority.TENANT_ADMIN.name())))
  180 + .andExpect(jsonPath("$.email", is(email)));
  181 +
155 182 loginSysAdmin();
156   - doDelete("/api/user/"+savedUser.getId().getId().toString())
157   - .andExpect(status().isOk());
158   -
159   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
160   - .andExpect(status().isOk());
  183 + doDelete("/api/user/" + savedUser.getId().getId().toString())
  184 + .andExpect(status().isOk());
  185 +
  186 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  187 + .andExpect(status().isOk());
161 188 }
162   -
  189 +
163 190 @Test
164 191 public void testFindUserById() throws Exception {
165 192 loginSysAdmin();
166   -
  193 +
167 194 Tenant tenant = new Tenant();
168 195 tenant.setTitle("My tenant");
169 196 Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
170 197 Assert.assertNotNull(savedTenant);
171   -
  198 +
172 199 String email = "tenant2@thingsboard.org";
173 200 User user = new User();
174 201 user.setAuthority(Authority.TENANT_ADMIN);
... ... @@ -176,25 +203,25 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
176 203 user.setEmail(email);
177 204 user.setFirstName("Joe");
178 205 user.setLastName("Downs");
179   -
  206 +
180 207 User savedUser = doPost("/api/user", user, User.class);
181   - User foundUser = doGet("/api/user/"+savedUser.getId().getId().toString(), User.class);
  208 + User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
182 209 Assert.assertNotNull(foundUser);
183 210 Assert.assertEquals(savedUser, foundUser);
184   -
185   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
186   - .andExpect(status().isOk());
  211 +
  212 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  213 + .andExpect(status().isOk());
187 214 }
188   -
  215 +
189 216 @Test
190 217 public void testSaveUserWithSameEmail() throws Exception {
191 218 loginSysAdmin();
192   -
  219 +
193 220 Tenant tenant = new Tenant();
194 221 tenant.setTitle("My tenant");
195 222 Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
196 223 Assert.assertNotNull(savedTenant);
197   -
  224 +
198 225 String email = TENANT_ADMIN_EMAIL;
199 226 User user = new User();
200 227 user.setAuthority(Authority.TENANT_ADMIN);
... ... @@ -202,24 +229,24 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
202 229 user.setEmail(email);
203 230 user.setFirstName("Joe");
204 231 user.setLastName("Downs");
205   -
  232 +
206 233 doPost("/api/user", user)
207   - .andExpect(status().isBadRequest())
208   - .andExpect(statusReason(containsString("User with email '" + email + "' already present in database")));
209   -
210   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
211   - .andExpect(status().isOk());
  234 + .andExpect(status().isBadRequest())
  235 + .andExpect(statusReason(containsString("User with email '" + email + "' already present in database")));
  236 +
  237 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  238 + .andExpect(status().isOk());
212 239 }
213   -
  240 +
214 241 @Test
215 242 public void testSaveUserWithInvalidEmail() throws Exception {
216 243 loginSysAdmin();
217   -
  244 +
218 245 Tenant tenant = new Tenant();
219 246 tenant.setTitle("My tenant");
220 247 Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
221 248 Assert.assertNotNull(savedTenant);
222   -
  249 +
223 250 String email = "tenant_thingsboard.org";
224 251 User user = new User();
225 252 user.setAuthority(Authority.TENANT_ADMIN);
... ... @@ -227,62 +254,62 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
227 254 user.setEmail(email);
228 255 user.setFirstName("Joe");
229 256 user.setLastName("Downs");
230   -
  257 +
231 258 doPost("/api/user", user)
232   - .andExpect(status().isBadRequest())
233   - .andExpect(statusReason(containsString("Invalid email address format '" + email + "'")));
234   -
235   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
236   - .andExpect(status().isOk());
  259 + .andExpect(status().isBadRequest())
  260 + .andExpect(statusReason(containsString("Invalid email address format '" + email + "'")));
  261 +
  262 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  263 + .andExpect(status().isOk());
237 264 }
238   -
  265 +
239 266 @Test
240 267 public void testSaveUserWithEmptyEmail() throws Exception {
241 268 loginSysAdmin();
242   -
  269 +
243 270 Tenant tenant = new Tenant();
244 271 tenant.setTitle("My tenant");
245 272 Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
246 273 Assert.assertNotNull(savedTenant);
247   -
  274 +
248 275 User user = new User();
249 276 user.setAuthority(Authority.TENANT_ADMIN);
250 277 user.setTenantId(savedTenant.getId());
251 278 user.setFirstName("Joe");
252 279 user.setLastName("Downs");
253   -
  280 +
254 281 doPost("/api/user", user)
255   - .andExpect(status().isBadRequest())
256   - .andExpect(statusReason(containsString("User email should be specified")));
257   -
258   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
259   - .andExpect(status().isOk());
  282 + .andExpect(status().isBadRequest())
  283 + .andExpect(statusReason(containsString("User email should be specified")));
  284 +
  285 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  286 + .andExpect(status().isOk());
260 287 }
261   -
  288 +
262 289 @Test
263 290 public void testSaveUserWithoutTenant() throws Exception {
264 291 loginSysAdmin();
265   -
  292 +
266 293 User user = new User();
267 294 user.setAuthority(Authority.TENANT_ADMIN);
268 295 user.setEmail("tenant2@thingsboard.org");
269 296 user.setFirstName("Joe");
270 297 user.setLastName("Downs");
271   -
  298 +
272 299 doPost("/api/user", user)
273   - .andExpect(status().isBadRequest())
274   - .andExpect(statusReason(containsString("Tenant administrator should be assigned to tenant")));
  300 + .andExpect(status().isBadRequest())
  301 + .andExpect(statusReason(containsString("Tenant administrator should be assigned to tenant")));
275 302 }
276   -
  303 +
277 304 @Test
278 305 public void testDeleteUser() throws Exception {
279 306 loginSysAdmin();
280   -
  307 +
281 308 Tenant tenant = new Tenant();
282 309 tenant.setTitle("My tenant");
283 310 Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
284 311 Assert.assertNotNull(savedTenant);
285   -
  312 +
286 313 String email = "tenant2@thingsboard.org";
287 314 User user = new User();
288 315 user.setAuthority(Authority.TENANT_ADMIN);
... ... @@ -290,176 +317,182 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
290 317 user.setEmail(email);
291 318 user.setFirstName("Joe");
292 319 user.setLastName("Downs");
293   -
  320 +
294 321 User savedUser = doPost("/api/user", user, User.class);
295   - User foundUser = doGet("/api/user/"+savedUser.getId().getId().toString(), User.class);
  322 + User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
296 323 Assert.assertNotNull(foundUser);
297   -
298   - doDelete("/api/user/"+savedUser.getId().getId().toString())
299   - .andExpect(status().isOk());
300   -
301   - doGet("/api/user/"+savedUser.getId().getId().toString())
302   - .andExpect(status().isNotFound());
303   -
304   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
305   - .andExpect(status().isOk());
  324 +
  325 + doDelete("/api/user/" + savedUser.getId().getId().toString())
  326 + .andExpect(status().isOk());
  327 +
  328 + doGet("/api/user/" + savedUser.getId().getId().toString())
  329 + .andExpect(status().isNotFound());
  330 +
  331 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  332 + .andExpect(status().isOk());
306 333 }
307   -
  334 +
308 335 @Test
309 336 public void testFindTenantAdmins() throws Exception {
310 337 loginSysAdmin();
311   -
  338 +
312 339 Tenant tenant = new Tenant();
313 340 tenant.setTitle("My tenant");
314 341 Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
315 342 Assert.assertNotNull(savedTenant);
316   -
  343 +
317 344 TenantId tenantId = savedTenant.getId();
318   -
  345 +
319 346 List<User> tenantAdmins = new ArrayList<>();
320   - for (int i=0;i<64;i++) {
  347 + for (int i = 0; i < 64; i++) {
321 348 User user = new User();
322 349 user.setAuthority(Authority.TENANT_ADMIN);
323 350 user.setTenantId(tenantId);
324 351 user.setEmail("testTenant" + i + "@thingsboard.org");
325 352 tenantAdmins.add(doPost("/api/user", user, User.class));
326 353 }
327   -
  354 +
328 355 List<User> loadedTenantAdmins = new ArrayList<>();
329 356 TextPageLink pageLink = new TextPageLink(33);
330 357 TextPageData<User> pageData = null;
331 358 do {
332   - pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
333   - new TypeReference<TextPageData<User>>(){}, pageLink);
  359 + pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
  360 + new TypeReference<TextPageData<User>>() {
  361 + }, pageLink);
334 362 loadedTenantAdmins.addAll(pageData.getData());
335 363 if (pageData.hasNext()) {
336 364 pageLink = pageData.getNextPageLink();
337 365 }
338 366 } while (pageData.hasNext());
339   -
  367 +
340 368 Collections.sort(tenantAdmins, idComparator);
341 369 Collections.sort(loadedTenantAdmins, idComparator);
342   -
  370 +
343 371 Assert.assertEquals(tenantAdmins, loadedTenantAdmins);
344   -
345   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
346   - .andExpect(status().isOk());
347   -
  372 +
  373 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  374 + .andExpect(status().isOk());
  375 +
348 376 pageLink = new TextPageLink(33);
349   - pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
350   - new TypeReference<TextPageData<User>>(){}, pageLink);
  377 + pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
  378 + new TypeReference<TextPageData<User>>() {
  379 + }, pageLink);
351 380 Assert.assertFalse(pageData.hasNext());
352 381 Assert.assertTrue(pageData.getData().isEmpty());
353 382 }
354   -
  383 +
355 384 @Test
356 385 public void testFindTenantAdminsByEmail() throws Exception {
357   -
  386 +
358 387 loginSysAdmin();
359   -
  388 +
360 389 Tenant tenant = new Tenant();
361 390 tenant.setTitle("My tenant");
362 391 Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
363 392 Assert.assertNotNull(savedTenant);
364   -
  393 +
365 394 TenantId tenantId = savedTenant.getId();
366   -
367   - String email1 = "testEmail1";
  395 +
  396 + String email1 = "testEmail1";
368 397 List<User> tenantAdminsEmail1 = new ArrayList<>();
369   -
370   - for (int i=0;i<124;i++) {
  398 +
  399 + for (int i = 0; i < 124; i++) {
371 400 User user = new User();
372 401 user.setAuthority(Authority.TENANT_ADMIN);
373 402 user.setTenantId(tenantId);
374   - String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
375   - String email = email1+suffix+ "@thingsboard.org";
  403 + String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
  404 + String email = email1 + suffix + "@thingsboard.org";
376 405 email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase();
377 406 user.setEmail(email);
378 407 tenantAdminsEmail1.add(doPost("/api/user", user, User.class));
379 408 }
380   -
381   - String email2 = "testEmail2";
  409 +
  410 + String email2 = "testEmail2";
382 411 List<User> tenantAdminsEmail2 = new ArrayList<>();
383   -
384   - for (int i=0;i<112;i++) {
  412 +
  413 + for (int i = 0; i < 112; i++) {
385 414 User user = new User();
386 415 user.setAuthority(Authority.TENANT_ADMIN);
387 416 user.setTenantId(tenantId);
388   - String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
389   - String email = email2+suffix+ "@thingsboard.org";
  417 + String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
  418 + String email = email2 + suffix + "@thingsboard.org";
390 419 email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase();
391 420 user.setEmail(email);
392 421 tenantAdminsEmail2.add(doPost("/api/user", user, User.class));
393 422 }
394   -
  423 +
395 424 List<User> loadedTenantAdminsEmail1 = new ArrayList<>();
396 425 TextPageLink pageLink = new TextPageLink(33, email1);
397 426 TextPageData<User> pageData = null;
398 427 do {
399   - pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
400   - new TypeReference<TextPageData<User>>(){}, pageLink);
  428 + pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
  429 + new TypeReference<TextPageData<User>>() {
  430 + }, pageLink);
401 431 loadedTenantAdminsEmail1.addAll(pageData.getData());
402 432 if (pageData.hasNext()) {
403 433 pageLink = pageData.getNextPageLink();
404 434 }
405 435 } while (pageData.hasNext());
406   -
  436 +
407 437 Collections.sort(tenantAdminsEmail1, idComparator);
408 438 Collections.sort(loadedTenantAdminsEmail1, idComparator);
409   -
  439 +
410 440 Assert.assertEquals(tenantAdminsEmail1, loadedTenantAdminsEmail1);
411   -
  441 +
412 442 List<User> loadedTenantAdminsEmail2 = new ArrayList<>();
413 443 pageLink = new TextPageLink(16, email2);
414 444 do {
415   - pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
416   - new TypeReference<TextPageData<User>>(){}, pageLink);
  445 + pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
  446 + new TypeReference<TextPageData<User>>() {
  447 + }, pageLink);
417 448 loadedTenantAdminsEmail2.addAll(pageData.getData());
418 449 if (pageData.hasNext()) {
419 450 pageLink = pageData.getNextPageLink();
420 451 }
421 452 } while (pageData.hasNext());
422   -
  453 +
423 454 Collections.sort(tenantAdminsEmail2, idComparator);
424 455 Collections.sort(loadedTenantAdminsEmail2, idComparator);
425   -
  456 +
426 457 Assert.assertEquals(tenantAdminsEmail2, loadedTenantAdminsEmail2);
427   -
  458 +
428 459 for (User user : loadedTenantAdminsEmail1) {
429   - doDelete("/api/user/"+user.getId().getId().toString())
430   - .andExpect(status().isOk());
  460 + doDelete("/api/user/" + user.getId().getId().toString())
  461 + .andExpect(status().isOk());
431 462 }
432   -
  463 +
433 464 pageLink = new TextPageLink(4, email1);
434   - pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
435   - new TypeReference<TextPageData<User>>(){}, pageLink);
  465 + pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
  466 + new TypeReference<TextPageData<User>>() {
  467 + }, pageLink);
436 468 Assert.assertFalse(pageData.hasNext());
437 469 Assert.assertEquals(0, pageData.getData().size());
438   -
  470 +
439 471 for (User user : loadedTenantAdminsEmail2) {
440   - doDelete("/api/user/"+user.getId().getId().toString())
441   - .andExpect(status().isOk());
  472 + doDelete("/api/user/" + user.getId().getId().toString())
  473 + .andExpect(status().isOk());
442 474 }
443   -
  475 +
444 476 pageLink = new TextPageLink(4, email2);
445   - pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
446   - new TypeReference<TextPageData<User>>(){}, pageLink);
  477 + pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
  478 + new TypeReference<TextPageData<User>>() {
  479 + }, pageLink);
447 480 Assert.assertFalse(pageData.hasNext());
448 481 Assert.assertEquals(0, pageData.getData().size());
449   -
450   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
451   - .andExpect(status().isOk());
  482 +
  483 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  484 + .andExpect(status().isOk());
452 485 }
453   -
  486 +
454 487 @Test
455 488 public void testFindCustomerUsers() throws Exception {
456   -
  489 +
457 490 loginSysAdmin();
458 491 Tenant tenant = new Tenant();
459 492 tenant.setTitle("My tenant");
460 493 Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
461 494 Assert.assertNotNull(savedTenant);
462   -
  495 +
463 496 TenantId tenantId = savedTenant.getId();
464 497 User tenantAdmin = new User();
465 498 tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
... ... @@ -467,59 +500,60 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
467 500 tenantAdmin.setEmail("tenant2@thingsboard.org");
468 501 tenantAdmin.setFirstName("Joe");
469 502 tenantAdmin.setLastName("Downs");
470   -
  503 +
471 504 tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
472   -
  505 +
473 506 Customer customer = new Customer();
474 507 customer.setTitle("My customer");
475 508 Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
476 509
477 510 CustomerId customerId = savedCustomer.getId();
478   -
  511 +
479 512 List<User> customerUsers = new ArrayList<>();
480   - for (int i=0;i<56;i++) {
  513 + for (int i = 0; i < 56; i++) {
481 514 User user = new User();
482 515 user.setAuthority(Authority.CUSTOMER_USER);
483 516 user.setCustomerId(customerId);
484 517 user.setEmail("testCustomer" + i + "@thingsboard.org");
485 518 customerUsers.add(doPost("/api/user", user, User.class));
486 519 }
487   -
  520 +
488 521 List<User> loadedCustomerUsers = new ArrayList<>();
489 522 TextPageLink pageLink = new TextPageLink(33);
490 523 TextPageData<User> pageData = null;
491 524 do {
492   - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
493   - new TypeReference<TextPageData<User>>(){}, pageLink);
  525 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
  526 + new TypeReference<TextPageData<User>>() {
  527 + }, pageLink);
494 528 loadedCustomerUsers.addAll(pageData.getData());
495 529 if (pageData.hasNext()) {
496 530 pageLink = pageData.getNextPageLink();
497 531 }
498 532 } while (pageData.hasNext());
499   -
  533 +
500 534 Collections.sort(customerUsers, idComparator);
501 535 Collections.sort(loadedCustomerUsers, idComparator);
502   -
  536 +
503 537 Assert.assertEquals(customerUsers, loadedCustomerUsers);
504   -
505   - doDelete("/api/customer/"+customerId.getId().toString())
506   - .andExpect(status().isOk());
507   -
  538 +
  539 + doDelete("/api/customer/" + customerId.getId().toString())
  540 + .andExpect(status().isOk());
  541 +
508 542 loginSysAdmin();
509   -
510   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
511   - .andExpect(status().isOk());
  543 +
  544 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  545 + .andExpect(status().isOk());
512 546 }
513   -
  547 +
514 548 @Test
515 549 public void testFindCustomerUsersByEmail() throws Exception {
516   -
  550 +
517 551 loginSysAdmin();
518 552 Tenant tenant = new Tenant();
519 553 tenant.setTitle("My tenant");
520 554 Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
521 555 Assert.assertNotNull(savedTenant);
522   -
  556 +
523 557 TenantId tenantId = savedTenant.getId();
524 558 User tenantAdmin = new User();
525 559 tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
... ... @@ -527,105 +561,109 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
527 561 tenantAdmin.setEmail("tenant2@thingsboard.org");
528 562 tenantAdmin.setFirstName("Joe");
529 563 tenantAdmin.setLastName("Downs");
530   -
  564 +
531 565 tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
532   -
  566 +
533 567 Customer customer = new Customer();
534 568 customer.setTitle("My customer");
535 569 Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
536 570
537 571 CustomerId customerId = savedCustomer.getId();
538   -
539   - String email1 = "testEmail1";
  572 +
  573 + String email1 = "testEmail1";
540 574 List<User> customerUsersEmail1 = new ArrayList<>();
541   -
542   - for (int i=0;i<74;i++) {
  575 +
  576 + for (int i = 0; i < 74; i++) {
543 577 User user = new User();
544 578 user.setAuthority(Authority.CUSTOMER_USER);
545 579 user.setCustomerId(customerId);
546   - String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
547   - String email = email1+suffix+ "@thingsboard.org";
  580 + String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
  581 + String email = email1 + suffix + "@thingsboard.org";
548 582 email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase();
549 583 user.setEmail(email);
550 584 customerUsersEmail1.add(doPost("/api/user", user, User.class));
551 585 }
552   -
553   - String email2 = "testEmail2";
  586 +
  587 + String email2 = "testEmail2";
554 588 List<User> customerUsersEmail2 = new ArrayList<>();
555   -
556   - for (int i=0;i<92;i++) {
  589 +
  590 + for (int i = 0; i < 92; i++) {
557 591 User user = new User();
558 592 user.setAuthority(Authority.CUSTOMER_USER);
559 593 user.setCustomerId(customerId);
560   - String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
561   - String email = email2+suffix+ "@thingsboard.org";
  594 + String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
  595 + String email = email2 + suffix + "@thingsboard.org";
562 596 email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase();
563 597 user.setEmail(email);
564 598 customerUsersEmail2.add(doPost("/api/user", user, User.class));
565 599 }
566   -
  600 +
567 601 List<User> loadedCustomerUsersEmail1 = new ArrayList<>();
568 602 TextPageLink pageLink = new TextPageLink(33, email1);
569 603 TextPageData<User> pageData = null;
570 604 do {
571   - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
572   - new TypeReference<TextPageData<User>>(){}, pageLink);
  605 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
  606 + new TypeReference<TextPageData<User>>() {
  607 + }, pageLink);
573 608 loadedCustomerUsersEmail1.addAll(pageData.getData());
574 609 if (pageData.hasNext()) {
575 610 pageLink = pageData.getNextPageLink();
576 611 }
577 612 } while (pageData.hasNext());
578   -
  613 +
579 614 Collections.sort(customerUsersEmail1, idComparator);
580 615 Collections.sort(loadedCustomerUsersEmail1, idComparator);
581   -
  616 +
582 617 Assert.assertEquals(customerUsersEmail1, loadedCustomerUsersEmail1);
583   -
  618 +
584 619 List<User> loadedCustomerUsersEmail2 = new ArrayList<>();
585 620 pageLink = new TextPageLink(16, email2);
586 621 do {
587   - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
588   - new TypeReference<TextPageData<User>>(){}, pageLink);
  622 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
  623 + new TypeReference<TextPageData<User>>() {
  624 + }, pageLink);
589 625 loadedCustomerUsersEmail2.addAll(pageData.getData());
590 626 if (pageData.hasNext()) {
591 627 pageLink = pageData.getNextPageLink();
592 628 }
593 629 } while (pageData.hasNext());
594   -
  630 +
595 631 Collections.sort(customerUsersEmail2, idComparator);
596 632 Collections.sort(loadedCustomerUsersEmail2, idComparator);
597   -
  633 +
598 634 Assert.assertEquals(customerUsersEmail2, loadedCustomerUsersEmail2);
599   -
  635 +
600 636 for (User user : loadedCustomerUsersEmail1) {
601   - doDelete("/api/user/"+user.getId().getId().toString())
602   - .andExpect(status().isOk());
  637 + doDelete("/api/user/" + user.getId().getId().toString())
  638 + .andExpect(status().isOk());
603 639 }
604   -
  640 +
605 641 pageLink = new TextPageLink(4, email1);
606   - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
607   - new TypeReference<TextPageData<User>>(){}, pageLink);
  642 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
  643 + new TypeReference<TextPageData<User>>() {
  644 + }, pageLink);
608 645 Assert.assertFalse(pageData.hasNext());
609 646 Assert.assertEquals(0, pageData.getData().size());
610   -
  647 +
611 648 for (User user : loadedCustomerUsersEmail2) {
612   - doDelete("/api/user/"+user.getId().getId().toString())
613   - .andExpect(status().isOk());
  649 + doDelete("/api/user/" + user.getId().getId().toString())
  650 + .andExpect(status().isOk());
614 651 }
615   -
  652 +
616 653 pageLink = new TextPageLink(4, email2);
617   - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
618   - new TypeReference<TextPageData<User>>(){}, pageLink);
  654 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
  655 + new TypeReference<TextPageData<User>>() {
  656 + }, pageLink);
619 657 Assert.assertFalse(pageData.hasNext());
620 658 Assert.assertEquals(0, pageData.getData().size());
621   -
622   - doDelete("/api/customer/"+customerId.getId().toString())
623   - .andExpect(status().isOk());
624   -
  659 +
  660 + doDelete("/api/customer/" + customerId.getId().toString())
  661 + .andExpect(status().isOk());
  662 +
625 663 loginSysAdmin();
626   -
627   - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
628   - .andExpect(status().isOk());
  664 +
  665 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  666 + .andExpect(status().isOk());
629 667 }
630   -
  668 +
631 669 }
... ...
... ... @@ -64,7 +64,6 @@ public abstract class BaseWidgetTypeControllerTest extends AbstractControllerTes
64 64 WidgetsBundle widgetsBundle = new WidgetsBundle();
65 65 widgetsBundle.setTitle("My widgets bundle");
66 66 savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class);
67   -
68 67 }
69 68
70 69 @After
... ... @@ -101,6 +100,19 @@ public abstract class BaseWidgetTypeControllerTest extends AbstractControllerTes
101 100 }
102 101
103 102 @Test
  103 + public void testUpdateWidgetTypeFromDifferentTenant() throws Exception {
  104 + WidgetType widgetType = new WidgetType();
  105 + widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
  106 + widgetType.setName("Widget Type");
  107 + widgetType.setDescriptor(new ObjectMapper().readValue("{ \"someKey\": \"someValue\" }", JsonNode.class));
  108 + WidgetType savedWidgetType = doPost("/api/widgetType", widgetType, WidgetType.class);
  109 +
  110 + loginDifferentTenant();
  111 + doPost("/api/widgetType", savedWidgetType, WidgetType.class, status().isForbidden());
  112 + deleteDifferentTenant();
  113 + }
  114 +
  115 + @Test
104 116 public void testFindWidgetTypeById() throws Exception {
105 117 WidgetType widgetType = new WidgetType();
106 118 widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
... ...
... ... @@ -89,6 +89,17 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
89 89 }
90 90
91 91 @Test
  92 + public void testUpdateWidgetsBundleFromDifferentTenant() throws Exception {
  93 + WidgetsBundle widgetsBundle = new WidgetsBundle();
  94 + widgetsBundle.setTitle("My widgets bundle");
  95 + WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class);
  96 +
  97 + loginDifferentTenant();
  98 + doPost("/api/widgetsBundle", savedWidgetsBundle, WidgetsBundle.class, status().isForbidden());
  99 + deleteDifferentTenant();
  100 + }
  101 +
  102 + @Test
92 103 public void testFindWidgetsBundleById() throws Exception {
93 104 WidgetsBundle widgetsBundle = new WidgetsBundle();
94 105 widgetsBundle.setTitle("My widgets bundle");
... ...
1   -/**
2   - * Copyright © 2016-2020 The Thingsboard Authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -package org.thingsboard.server.rules;
17   -
18   -import org.cassandraunit.dataset.cql.ClassPathCQLDataSet;
19   -import org.junit.BeforeClass;
20   -import org.junit.ClassRule;
21   -import org.junit.extensions.cpsuite.ClasspathSuite;
22   -import org.junit.runner.RunWith;
23   -import org.thingsboard.server.dao.CustomCassandraCQLUnit;
24   -import org.thingsboard.server.dao.CustomSqlUnit;
25   -import org.thingsboard.server.queue.memory.InMemoryStorage;
26   -
27   -import java.util.Arrays;
28   -
29   -@RunWith(ClasspathSuite.class)
30   -@ClasspathSuite.ClassnameFilters({
31   - "org.thingsboard.server.rules.flow.nosql.*Test",
32   - "org.thingsboard.server.rules.lifecycle.nosql.*Test"
33   -})
34   -public class RuleEngineNoSqlTestSuite {
35   -
36   - @ClassRule
37   - public static CustomCassandraCQLUnit cassandraUnit =
38   - new CustomCassandraCQLUnit(
39   - Arrays.asList(
40   - new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false),
41   - new ClassPathCQLDataSet("cassandra/schema-entities.cql", false, false),
42   - new ClassPathCQLDataSet("cassandra/system-data.cql", false, false)),
43   - "cassandra-test.yaml", 30000l);
44   -
45   - @BeforeClass
46   - public static void cleanupInMemStorage(){
47   - InMemoryStorage.getInstance().cleanup();
48   - }
49   -
50   -}
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.0-SNAPSHOT</version>
  23 + <version>2.5.1-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -25,6 +25,8 @@ import org.thingsboard.server.common.data.id.EdgeId;
25 25 import org.thingsboard.server.common.data.id.TenantId;
26 26 import org.thingsboard.server.common.data.page.TextPageData;
27 27 import org.thingsboard.server.common.data.page.TextPageLink;
  28 +import org.thingsboard.server.common.data.page.TimePageData;
  29 +import org.thingsboard.server.common.data.page.TimePageLink;
28 30
29 31 import java.util.List;
30 32 import java.util.Optional;
... ... @@ -67,9 +69,7 @@ public interface AssetService {
67 69
68 70 Asset assignAssetToEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId);
69 71
70   - Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId);
  72 + Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId);
71 73
72   - TextPageData<Asset> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink);
73   -
74   - TextPageData<Asset> findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink);
  74 + ListenableFuture<TimePageData<Asset>> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
75 75 }
... ...
... ... @@ -25,11 +25,13 @@ import org.thingsboard.server.common.data.id.EdgeId;
25 25 import org.thingsboard.server.common.data.id.TenantId;
26 26 import org.thingsboard.server.common.data.page.TextPageData;
27 27 import org.thingsboard.server.common.data.page.TextPageLink;
  28 +import org.thingsboard.server.common.data.page.TimePageData;
  29 +import org.thingsboard.server.common.data.page.TimePageLink;
28 30
29 31 import java.util.List;
30 32
31 33 public interface DeviceService {
32   -
  34 +
33 35 Device findDeviceById(TenantId tenantId, DeviceId deviceId);
34 36
35 37 ListenableFuture<Device> findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId);
... ... @@ -68,10 +70,7 @@ public interface DeviceService {
68 70
69 71 Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
70 72
71   - Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId);
72   -
73   - TextPageData<Device> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink);
74   -
75   - TextPageData<Device> findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink);
  73 + Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
76 74
  75 + ListenableFuture<TimePageData<Device>> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
77 76 }
... ...
... ... @@ -82,6 +82,8 @@ public interface EdgeService {
82 82
83 83 Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException;
84 84
  85 + void assignDefaultRuleChainsToEdge(TenantId tenantId, EdgeId edgeId);
  86 +
85 87 ListenableFuture<TimePageData<Edge>> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId, TimePageLink pageLink);
86 88
87 89 ListenableFuture<TimePageData<Edge>> findEdgesByTenantIdAndDashboardId(TenantId tenantId, DashboardId dashboardId, TimePageLink pageLink);
... ...
... ... @@ -26,6 +26,8 @@ import org.thingsboard.server.common.data.id.EntityViewId;
26 26 import org.thingsboard.server.common.data.id.TenantId;
27 27 import org.thingsboard.server.common.data.page.TextPageData;
28 28 import org.thingsboard.server.common.data.page.TextPageLink;
  29 +import org.thingsboard.server.common.data.page.TimePageData;
  30 +import org.thingsboard.server.common.data.page.TimePageLink;
29 31
30 32 import java.util.List;
31 33
... ... @@ -68,11 +70,7 @@ public interface EntityViewService {
68 70
69 71 EntityView assignEntityViewToEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId);
70 72
71   - EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId);
72   -
73   - TextPageData<EntityView> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink);
74   -
75   - TextPageData<EntityView> findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink);
76   -
  73 + EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId);
77 74
  75 + ListenableFuture<TimePageData<EntityView>> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
78 76 }
... ...
... ... @@ -28,4 +28,6 @@ public class OAuth2User {
28 28 private String email;
29 29 private String firstName;
30 30 private String lastName;
  31 + private boolean alwaysFullScreen;
  32 + private String defaultDashboardName;
31 33 }
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.0-SNAPSHOT</version>
  23 + <version>2.5.1-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -18,7 +18,6 @@ package org.thingsboard.server.common.data;
18 18 import lombok.EqualsAndHashCode;
19 19 import org.thingsboard.server.common.data.id.CustomerId;
20 20 import org.thingsboard.server.common.data.id.DeviceId;
21   -import org.thingsboard.server.common.data.id.EdgeId;
22 21 import org.thingsboard.server.common.data.id.TenantId;
23 22
24 23 @EqualsAndHashCode(callSuper = true)
... ... @@ -28,7 +27,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
28 27
29 28 private TenantId tenantId;
30 29 private CustomerId customerId;
31   - private EdgeId edgeId;
32 30 private String name;
33 31 private String type;
34 32 private String label;
... ... @@ -48,7 +46,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
48 46 this.name = device.getName();
49 47 this.type = device.getType();
50 48 this.label = device.getLabel();
51   - this.edgeId = device.getEdgeId();
52 49 }
53 50
54 51 public TenantId getTenantId() {
... ... @@ -67,14 +64,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
67 64 this.customerId = customerId;
68 65 }
69 66
70   - public EdgeId getEdgeId() {
71   - return edgeId;
72   - }
73   -
74   - public void setEdgeId(EdgeId edgeId) {
75   - this.edgeId = edgeId;
76   - }
77   -
78 67 @Override
79 68 public String getName() {
80 69 return name;
... ... @@ -112,8 +101,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
112 101 builder.append(tenantId);
113 102 builder.append(", customerId=");
114 103 builder.append(customerId);
115   - builder.append(", edgeId=");
116   - builder.append(edgeId);
117 104 builder.append(", name=");
118 105 builder.append(name);
119 106 builder.append(", type=");
... ...
... ... @@ -19,7 +19,6 @@ import lombok.AllArgsConstructor;
19 19 import lombok.Data;
20 20 import lombok.EqualsAndHashCode;
21 21 import org.thingsboard.server.common.data.id.CustomerId;
22   -import org.thingsboard.server.common.data.id.EdgeId;
23 22 import org.thingsboard.server.common.data.id.EntityId;
24 23 import org.thingsboard.server.common.data.id.EntityViewId;
25 24 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -40,7 +39,6 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId>
40 39 private EntityId entityId;
41 40 private TenantId tenantId;
42 41 private CustomerId customerId;
43   - private EdgeId edgeId;
44 42 private String name;
45 43 private String type;
46 44 private TelemetryEntityView keys;
... ...
... ... @@ -15,13 +15,10 @@
15 15 */
16 16 package org.thingsboard.server.common.data.asset;
17 17
18   -import com.fasterxml.jackson.databind.JsonNode;
19 18 import lombok.EqualsAndHashCode;
20 19 import org.thingsboard.server.common.data.*;
21 20 import org.thingsboard.server.common.data.id.AssetId;
22 21 import org.thingsboard.server.common.data.id.CustomerId;
23   -import org.thingsboard.server.common.data.id.EdgeId;
24   -import org.thingsboard.server.common.data.id.EdgeId;
25 22 import org.thingsboard.server.common.data.id.TenantId;
26 23
27 24 @EqualsAndHashCode(callSuper = true)
... ... @@ -34,7 +31,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements
34 31 private String name;
35 32 private String type;
36 33 private String label;
37   - private EdgeId edgeId;
38 34
39 35 public Asset() {
40 36 super();
... ... @@ -51,7 +47,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements
51 47 this.name = asset.getName();
52 48 this.type = asset.getType();
53 49 this.label = asset.getLabel();
54   - this.edgeId = asset.getEdgeId();
55 50 }
56 51
57 52 public TenantId getTenantId() {
... ... @@ -70,14 +65,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements
70 65 this.customerId = customerId;
71 66 }
72 67
73   - public EdgeId getEdgeId() {
74   - return edgeId;
75   - }
76   -
77   - public void setEdgeId(EdgeId edgeId) {
78   - this.edgeId = edgeId;
79   - }
80   -
81 68 @Override
82 69 public String getName() {
83 70 return name;
... ... @@ -115,8 +102,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements
115 102 builder.append(tenantId);
116 103 builder.append(", customerId=");
117 104 builder.append(customerId);
118   - builder.append(", edgeId=");
119   - builder.append(edgeId);
120 105 builder.append(", name=");
121 106 builder.append(name);
122 107 builder.append(", type=");
... ...
... ... @@ -22,6 +22,7 @@ public enum RelationTypeGroup {
22 22 DASHBOARD,
23 23 RULE_CHAIN,
24 24 RULE_NODE,
25   - EDGE
  25 + EDGE,
  26 + EDGE_DEFAULT_RULE_CHAIN
26 27
27 28 }
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.0-SNAPSHOT</version>
  23 + <version>2.5.1-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.0-SNAPSHOT</version>
  23 + <version>2.5.1-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -40,6 +40,7 @@ import java.util.UUID;
40 40 public final class TbMsg implements Serializable {
41 41
42 42 private final UUID id;
  43 + private final long ts;
43 44 private final String type;
44 45 private final EntityId originator;
45 46 private final TbMsgMetaData metaData;
... ... @@ -51,38 +52,43 @@ public final class TbMsg implements Serializable {
51 52 transient private final TbMsgCallback callback;
52 53
53 54 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) {
54   - return new TbMsg(UUID.randomUUID(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, TbMsgCallback.EMPTY);
  55 + return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, TbMsgCallback.EMPTY);
55 56 }
56 57
57 58 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
58   - return new TbMsg(UUID.randomUUID(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
  59 + return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
59 60 }
60 61
61 62 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) {
62   - return new TbMsg(UUID.randomUUID(), type, originator, metaData.copy(), dataType, data, null, null, TbMsgCallback.EMPTY);
  63 + return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, null, null, TbMsgCallback.EMPTY);
63 64 }
64 65
65 66 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
66   - return new TbMsg(UUID.randomUUID(), type, originator, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
  67 + return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
67 68 }
68 69
69 70 public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) {
70   - return new TbMsg(UUID.randomUUID(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, callback);
  71 + return new TbMsg(UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, callback);
71 72 }
72 73
73 74 public static TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) {
74   - return new TbMsg(origMsg.getId(), type, originator, metaData.copy(), origMsg.getDataType(),
  75 + return new TbMsg(origMsg.getId(), origMsg.getTs(), type, originator, metaData.copy(), origMsg.getDataType(),
75 76 data, origMsg.getRuleChainId(), origMsg.getRuleNodeId(), origMsg.getCallback());
76 77 }
77 78
78 79 public static TbMsg newMsg(TbMsg tbMsg, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
79   - return new TbMsg(UUID.randomUUID(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(),
  80 + return new TbMsg(UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(),
80 81 tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, TbMsgCallback.EMPTY);
81 82 }
82 83
83   - private TbMsg(UUID id, String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data,
  84 + private TbMsg(UUID id, long ts, String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data,
84 85 RuleChainId ruleChainId, RuleNodeId ruleNodeId, TbMsgCallback callback) {
85 86 this.id = id;
  87 + if (ts > 0) {
  88 + this.ts = ts;
  89 + } else {
  90 + this.ts = System.currentTimeMillis();
  91 + }
86 92 this.type = type;
87 93 this.originator = originator;
88 94 this.metaData = metaData;
... ... @@ -105,6 +111,7 @@ public final class TbMsg implements Serializable {
105 111 public static byte[] toByteArray(TbMsg msg) {
106 112 MsgProtos.TbMsgProto.Builder builder = MsgProtos.TbMsgProto.newBuilder();
107 113 builder.setId(msg.getId().toString());
  114 + builder.setTs(msg.getTs());
108 115 builder.setType(msg.getType());
109 116 builder.setEntityType(msg.getOriginator().getEntityType().name());
110 117 builder.setEntityIdMSB(msg.getOriginator().getId().getMostSignificantBits());
... ... @@ -124,7 +131,6 @@ public final class TbMsg implements Serializable {
124 131 builder.setMetaData(MsgProtos.TbMsgMetaDataProto.newBuilder().putAllData(msg.getMetaData().getData()).build());
125 132 }
126 133
127   -
128 134 builder.setDataType(msg.getDataType().ordinal());
129 135 builder.setData(msg.getData());
130 136 return builder.build().toByteArray();
... ... @@ -144,18 +150,18 @@ public final class TbMsg implements Serializable {
144 150 ruleNodeId = new RuleNodeId(new UUID(proto.getRuleNodeIdMSB(), proto.getRuleNodeIdLSB()));
145 151 }
146 152 TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()];
147   - return new TbMsg(UUID.fromString(proto.getId()), proto.getType(), entityId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, callback);
  153 + return new TbMsg(UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, callback);
148 154 } catch (InvalidProtocolBufferException e) {
149 155 throw new IllegalStateException("Could not parse protobuf for TbMsg", e);
150 156 }
151 157 }
152 158
153 159 public TbMsg copyWithRuleChainId(RuleChainId ruleChainId) {
154   - return new TbMsg(this.id, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, null, callback);
  160 + return new TbMsg(this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, null, callback);
155 161 }
156 162
157 163 public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
158   - return new TbMsg(this.id, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, callback);
  164 + return new TbMsg(this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, callback);
159 165 }
160 166
161 167 public TbMsgCallback getCallback() {
... ...
... ... @@ -44,4 +44,5 @@ message TbMsgProto {
44 44 int32 dataType = 13;
45 45 string data = 14;
46 46
  47 + int64 ts = 15;
47 48 }
\ No newline at end of file
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.0-SNAPSHOT</version>
  23 + <version>2.5.1-SNAPSHOT</version>
24 24 <artifactId>thingsboard</artifactId>
25 25 </parent>
26 26 <artifactId>common</artifactId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.0-SNAPSHOT</version>
  23 + <version>2.5.1-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -101,7 +101,7 @@ public class TbServiceBusConsumerTemplate<T extends TbQueueMsg> extends Abstract
101 101 @Override
102 102 protected void doSubscribe(List<String> topicNames) {
103 103 createReceivers();
104   - messagesPerQueue = receivers.size() / partitions.size();
  104 + messagesPerQueue = receivers.size() / Math.max(partitions.size(), 1);
105 105 }
106 106
107 107 @Override
... ...
... ... @@ -85,7 +85,12 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i
85 85 subscribed = true;
86 86 }
87 87
88   - List<R> records = doPoll(durationInMillis);
  88 + List<R> records;
  89 + if (partitions.isEmpty()) {
  90 + records = Collections.emptyList();
  91 + } else {
  92 + records = doPoll(durationInMillis);
  93 + }
89 94 if (!records.isEmpty()) {
90 95 List<T> result = new ArrayList<>(records.size());
91 96 records.forEach(record -> {
... ...
... ... @@ -36,6 +36,7 @@ import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
36 36 import javax.annotation.PostConstruct;
37 37 import java.nio.charset.StandardCharsets;
38 38 import java.util.ArrayList;
  39 +import java.util.Collections;
39 40 import java.util.Comparator;
40 41 import java.util.HashMap;
41 42 import java.util.HashSet;
... ... @@ -148,6 +149,14 @@ public class HashPartitionService implements PartitionService {
148 149 }
149 150 }
150 151 });
  152 +
  153 + oldPartitions.forEach((serviceQueueKey, partitions) -> {
  154 + if (!myPartitions.containsKey(serviceQueueKey)) {
  155 + log.info("[{}] NO MORE PARTITIONS FOR CURRENT KEY", serviceQueueKey);
  156 + applicationEventPublisher.publishEvent(new PartitionChangeEvent(this, serviceQueueKey, Collections.emptySet()));
  157 + }
  158 + });
  159 +
151 160 myPartitions.forEach((serviceQueueKey, partitions) -> {
152 161 if (!partitions.equals(oldPartitions.get(serviceQueueKey))) {
153 162 log.info("[{}] NEW PARTITIONS: {}", serviceQueueKey, partitions);
... ...
... ... @@ -71,8 +71,12 @@ public class TbKafkaConsumerTemplate<T extends TbQueueMsg> extends AbstractTbQue
71 71
72 72 @Override
73 73 protected void doSubscribe(List<String> topicNames) {
74   - topicNames.forEach(admin::createTopicIfNotExists);
75   - consumer.subscribe(topicNames);
  74 + if (!topicNames.isEmpty()) {
  75 + topicNames.forEach(admin::createTopicIfNotExists);
  76 + consumer.subscribe(topicNames);
  77 + } else {
  78 + consumer.unsubscribe();
  79 + }
76 80 }
77 81
78 82 @Override
... ...
... ... @@ -200,7 +200,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi
200 200 consumerBuilder.settings(kafkaSettings);
201 201 consumerBuilder.topic(transportApiSettings.getRequestsTopic());
202 202 consumerBuilder.clientId("monolith-transport-api-consumer-" + serviceInfoProvider.getServiceId());
203   - consumerBuilder.groupId("monolith-transport-api-consumer-" + serviceInfoProvider.getServiceId());
  203 + consumerBuilder.groupId("monolith-transport-api-consumer");
204 204 consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders()));
205 205 consumerBuilder.admin(transportApiAdmin);
206 206 return consumerBuilder.build();
... ...
... ... @@ -170,7 +170,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory {
170 170 consumerBuilder.settings(kafkaSettings);
171 171 consumerBuilder.topic(transportApiSettings.getRequestsTopic());
172 172 consumerBuilder.clientId("tb-core-transport-api-consumer-" + serviceInfoProvider.getServiceId());
173   - consumerBuilder.groupId("tb-core-transport-api-consumer-" + serviceInfoProvider.getServiceId());
  173 + consumerBuilder.groupId("tb-core-transport-api-consumer");
174 174 consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders()));
175 175 consumerBuilder.admin(transportApiAdmin);
176 176 return consumerBuilder.build();
... ...
... ... @@ -106,7 +106,7 @@ public class TbPubSubConsumerTemplate<T extends TbQueueMsg> extends AbstractPara
106 106 subscriptionNames = new LinkedHashSet<>(topicNames);
107 107 subscriptionNames.forEach(admin::createTopicIfNotExists);
108 108 initNewExecutor(subscriptionNames.size() + 1);
109   - messagesPerTopic = pubSubSettings.getMaxMessages() / subscriptionNames.size();
  109 + messagesPerTopic = pubSubSettings.getMaxMessages() / Math.max(subscriptionNames.size(), 1);
110 110 }
111 111
112 112 @Override
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard.common</groupId>
23   - <version>2.5.0-SNAPSHOT</version>
  23 + <version>2.5.1-SNAPSHOT</version>
24 24 <artifactId>transport</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common.transport</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard.common</groupId>
23   - <version>2.5.0-SNAPSHOT</version>
  23 + <version>2.5.1-SNAPSHOT</version>
24 24 <artifactId>transport</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common.transport</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard.common</groupId>
23   - <version>2.5.0-SNAPSHOT</version>
  23 + <version>2.5.1-SNAPSHOT</version>
24 24 <artifactId>transport</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common.transport</groupId>
... ...
... ... @@ -294,7 +294,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
294 294 break;
295 295 }
296 296 } catch (Exception e) {
297   - log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS);
  297 + log.warn("[{}] Failed to subscribe to [{}][{}]", sessionId, topic, reqQoS, e);
298 298 grantedQoSList.add(FAILURE.value());
299 299 }
300 300 }
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.0-SNAPSHOT</version>
  23 + <version>2.5.1-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard.common</groupId>
23   - <version>2.5.0-SNAPSHOT</version>
  23 + <version>2.5.1-SNAPSHOT</version>
24 24 <artifactId>transport</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common.transport</groupId>
... ... @@ -53,10 +53,6 @@
53 53 <artifactId>util</artifactId>
54 54 </dependency>
55 55 <dependency>
56   - <groupId>org.thingsboard.common</groupId>
57   - <artifactId>queue</artifactId>
58   - </dependency>
59   - <dependency>
60 56 <groupId>com.google.code.gson</groupId>
61 57 <artifactId>gson</artifactId>
62 58 </dependency>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.0-SNAPSHOT</version>
  23 + <version>2.5.1-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.5.0-SNAPSHOT</version>
  23 + <version>2.5.1-SNAPSHOT</version>
24 24 <artifactId>thingsboard</artifactId>
25 25 </parent>
26 26 <artifactId>dao</artifactId>
... ...
... ... @@ -341,16 +341,6 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
341 341 }
342 342 }
343 343
344   - private void deleteRelation(TenantId tenantId, EntityRelation alarmRelation) {
345   - log.debug("Deleting Alarm relation: {}", alarmRelation);
346   - relationService.deleteRelation(tenantId, alarmRelation);
347   - }
348   -
349   - private void createRelation(TenantId tenantId, EntityRelation alarmRelation) {
350   - log.debug("Creating Alarm relation: {}", alarmRelation);
351   - relationService.saveRelation(tenantId, alarmRelation);
352   - }
353   -
354 344 private Alarm merge(Alarm existing, Alarm alarm) {
355 345 if (alarm.getStartTs() > existing.getEndTs()) {
356 346 existing.setEndTs(alarm.getStartTs());
... ... @@ -395,7 +385,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
395 385 }
396 386 }
397 387
398   - private void createAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status, boolean createAnyRelation) {
  388 + private void createAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status, boolean createAnyRelation) throws ExecutionException, InterruptedException {
399 389 if (createAnyRelation) {
400 390 createRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + AlarmSearchStatus.ANY.name(), RelationTypeGroup.ALARM));
401 391 }
... ... @@ -404,13 +394,13 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
404 394 createRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
405 395 }
406 396
407   - private void deleteAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status) {
  397 + private void deleteAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status) throws ExecutionException, InterruptedException {
408 398 deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM));
409 399 deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM));
410 400 deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
411 401 }
412 402
413   - private void updateAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) {
  403 + private void updateAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) throws ExecutionException, InterruptedException {
414 404 deleteAlarmRelation(tenantId, entityId, alarmId, oldStatus);
415 405 createAlarmRelation(tenantId, entityId, alarmId, newStatus, false);
416 406 }
... ...
... ... @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.EntitySubtype;
20 20 import org.thingsboard.server.common.data.asset.Asset;
21 21 import org.thingsboard.server.common.data.id.TenantId;
22 22 import org.thingsboard.server.common.data.page.TextPageLink;
  23 +import org.thingsboard.server.common.data.page.TimePageLink;
23 24 import org.thingsboard.server.dao.Dao;
24 25
25 26 import java.util.List;
... ... @@ -116,23 +117,12 @@ public interface AssetDao extends Dao<Asset> {
116 117 ListenableFuture<List<EntitySubtype>> findTenantAssetTypesAsync(UUID tenantId);
117 118
118 119 /**
119   - * Find assets by tenantId, customerId and page link.
  120 + * Find assets by tenantId, edgeId and page link.
120 121 *
121 122 * @param tenantId the tenantId
122 123 * @param edgeId the edgeId
123 124 * @param pageLink the page link
124 125 * @return the list of asset objects
125 126 */
126   - List<Asset> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink);
127   -
128   - /**
129   - * Find assets by tenantId, customerId, type and page link.
130   - *
131   - * @param tenantId the tenantId
132   - * @param edgeId the edgeId
133   - * @param type the type
134   - * @param pageLink the page link
135   - * @return the list of asset objects
136   - */
137   - List<Asset> findAssetsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink);
  127 + ListenableFuture<List<Asset>> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink);
138 128 }
... ...