Commit 3d3dd8871044a0733a7016eafc4a2e09f28a26c1

Authored by Volodymyr Babak
2 parents 2748a755 4df37fc0

Merge remote-tracking branch 'origin/develop/2.5.1' into feature/edge

Showing 99 changed files with 1691 additions and 1091 deletions

Too many changes to show.

To preserve performance only 99 of 283 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 }
... ...
... ... @@ -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(),
... ...
... ... @@ -82,18 +82,15 @@ public class AssetController extends BaseController {
82 82 try {
83 83 asset.setTenantId(getCurrentUser().getTenantId());
84 84
85   - Operation operation = asset.getId() == null ? Operation.CREATE : Operation.WRITE;
  85 + checkEntity(asset.getId(), asset, Resource.ASSET);
86 86
87   - accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, operation,
88   - asset.getId(), asset);
89   -
90   - Asset savedAsset = checkNotNull(assetService.saveAsset(asset));
  87 + Asset savedAsset = checkNotNull(assetService.saveAsset(asset));
91 88
92 89 logEntityAction(savedAsset.getId(), savedAsset,
93 90 savedAsset.getCustomerId(),
94 91 asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
95 92
96   - return savedAsset;
  93 + return savedAsset;
97 94 } catch (Exception e) {
98 95 logEntityAction(emptyId(EntityType.ASSET), asset,
99 96 null, asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
... ... @@ -144,7 +141,7 @@ public class AssetController extends BaseController {
144 141 savedAsset.getCustomerId(),
145 142 ActionType.ASSIGNED_TO_CUSTOMER, null, strAssetId, strCustomerId, customer.getName());
146 143
147   - return savedAsset;
  144 + return savedAsset;
148 145 } catch (Exception e) {
149 146
150 147 logEntityAction(emptyId(EntityType.ASSET), null,
... ... @@ -224,7 +221,7 @@ public class AssetController extends BaseController {
224 221 try {
225 222 TenantId tenantId = getCurrentUser().getTenantId();
226 223 TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
227   - if (type != null && type.trim().length()>0) {
  224 + if (type != null && type.trim().length() > 0) {
228 225 return checkNotNull(assetService.findAssetsByTenantIdAndType(tenantId, type, pageLink));
229 226 } else {
230 227 return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink));
... ... @@ -263,7 +260,7 @@ public class AssetController extends BaseController {
263 260 CustomerId customerId = new CustomerId(toUUID(strCustomerId));
264 261 checkCustomerId(customerId, Operation.READ);
265 262 TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
266   - if (type != null && type.trim().length()>0) {
  263 + if (type != null && type.trim().length() > 0) {
267 264 return checkNotNull(assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
268 265 } else {
269 266 return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink));
... ...
... ... @@ -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,
... ...
... ... @@ -99,10 +99,7 @@ public class DeviceController extends BaseController {
99 99 try {
100 100 device.setTenantId(getCurrentUser().getTenantId());
101 101
102   - Operation operation = device.getId() == null ? Operation.CREATE : Operation.WRITE;
103   -
104   - accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, operation,
105   - device.getId(), device);
  102 + checkEntity(device.getId(), device, Resource.DEVICE);
106 103
107 104 Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken));
108 105
... ...
... ... @@ -97,10 +97,7 @@ public class EntityViewController extends BaseController {
97 97 try {
98 98 entityView.setTenantId(getCurrentUser().getTenantId());
99 99
100   - Operation operation = entityView.getId() == null ? Operation.CREATE : Operation.WRITE;
101   -
102   - accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, operation,
103   - entityView.getId(), entityView);
  100 + checkEntity(entityView.getId(), entityView, Resource.ENTITY_VIEW);
104 101
105 102 EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView));
106 103 List<ListenableFuture<List<Void>>> futures = new ArrayList<>();
... ...
... ... @@ -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,10 +130,7 @@ 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
... ...
... ... @@ -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");
... ...
... ... @@ -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;
... ...
... ... @@ -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);
... ...
... ... @@ -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>
... ...
... ... @@ -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>
... ...
... ... @@ -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>
... ...
... ... @@ -112,7 +112,7 @@ public class EventEntity extends BaseSqlEntity<Event> implements BaseEntity<Eve
112 112 return event;
113 113 }
114 114
115   - private long getTs(UUID uuid) {
  115 + private static long getTs(UUID uuid) {
116 116 return (uuid.timestamp() - EPOCH_DIFF) / 10000;
117 117 }
118 118 }
... ...
... ... @@ -34,6 +34,8 @@ public class OAuth2ClientMapperConfig {
34 34 private String tenantNameStrategy;
35 35 private String tenantNamePattern;
36 36 private String customerNamePattern;
  37 + private boolean alwaysFullScreen;
  38 + private String defaultDashboardName;
37 39 }
38 40
39 41 @Data
... ...
... ... @@ -90,7 +90,7 @@ public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa
90 90 }
91 91
92 92 private void savePartitionIfNotExist(long ts) {
93   - if (!tsFormat.equals(SqlTsPartitionDate.INDEFINITE)) {
  93 + if (!tsFormat.equals(SqlTsPartitionDate.INDEFINITE) && ts >= 0) {
94 94 LocalDateTime time = LocalDateTime.ofInstant(Instant.ofEpochMilli(ts), ZoneOffset.UTC);
95 95 LocalDateTime localDateTimeStart = tsFormat.trancateTo(time);
96 96 long partitionStartTs = toMills(localDateTimeStart);
... ...
... ... @@ -52,7 +52,7 @@ CREATE TABLE IF NOT EXISTS tb_schema_settings
52 52 CONSTRAINT tb_schema_settings_pkey PRIMARY KEY (schema_version)
53 53 );
54 54
55   -INSERT INTO tb_schema_settings (schema_version) VALUES (2005000) ON CONFLICT (schema_version) DO UPDATE SET schema_version = 2005000;
  55 +INSERT INTO tb_schema_settings (schema_version) VALUES (2005001) ON CONFLICT (schema_version) DO UPDATE SET schema_version = 2005001;
56 56
57 57 CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS
58 58 $$
... ...
... ... @@ -53,7 +53,7 @@ CREATE TABLE IF NOT EXISTS tb_schema_settings
53 53 CONSTRAINT tb_schema_settings_pkey PRIMARY KEY (schema_version)
54 54 );
55 55
56   -INSERT INTO tb_schema_settings (schema_version) VALUES (2005000) ON CONFLICT (schema_version) DO UPDATE SET schema_version = 2005000;
  56 +INSERT INTO tb_schema_settings (schema_version) VALUES (2005001) ON CONFLICT (schema_version) DO UPDATE SET schema_version = 2005001;
57 57
58 58 CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar, IN system_ttl bigint, INOUT deleted bigint)
59 59 LANGUAGE plpgsql AS
... ...
  1 +TB_QUEUE_TYPE=kafka
1 2
2 3 DOCKER_REPO=thingsboard
3 4
... ...
... ... @@ -32,6 +32,32 @@ function additionalComposeArgs() {
32 32 echo $ADDITIONAL_COMPOSE_ARGS
33 33 }
34 34
  35 +function additionalComposeQueueArgs() {
  36 + source .env
  37 + ADDITIONAL_COMPOSE_QUEUE_ARGS=""
  38 + case $TB_QUEUE_TYPE in
  39 + kafka)
  40 + ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.kafka.yml"
  41 + ;;
  42 + aws-sqs)
  43 + ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.aws-sqs.yml"
  44 + ;;
  45 + pubsub)
  46 + ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.pubsub.yml"
  47 + ;;
  48 + rabbitmq)
  49 + ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.rabbitmq.yml"
  50 + ;;
  51 + service-bus)
  52 + ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.service-bus.yml"
  53 + ;;
  54 + *)
  55 + echo "Unknown Queue service value specified: '${TB_QUEUE_TYPE}'. Should be either kafka or aws-sqs or pubsub or rabbitmq or service-bus." >&2
  56 + exit 1
  57 + esac
  58 + echo $ADDITIONAL_COMPOSE_QUEUE_ARGS
  59 +}
  60 +
35 61 function additionalStartupServices() {
36 62 source .env
37 63 ADDITIONAL_STARTUP_SERVICES=""
... ...
  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 +
  17 +version: '2.2'
  18 +
  19 +services:
  20 + tb-js-executor:
  21 + env_file:
  22 + - queue-aws-sqs.env
  23 + tb-core1:
  24 + env_file:
  25 + - queue-aws-sqs.env
  26 + depends_on:
  27 + - zookeeper
  28 + - redis
  29 + tb-core2:
  30 + env_file:
  31 + - queue-aws-sqs.env
  32 + depends_on:
  33 + - zookeeper
  34 + - redis
  35 + tb-rule-engine1:
  36 + env_file:
  37 + - queue-aws-sqs.env
  38 + depends_on:
  39 + - zookeeper
  40 + - redis
  41 + tb-rule-engine2:
  42 + env_file:
  43 + - queue-aws-sqs.env
  44 + depends_on:
  45 + - zookeeper
  46 + - redis
  47 + tb-mqtt-transport1:
  48 + env_file:
  49 + - queue-aws-sqs.env
  50 + depends_on:
  51 + - zookeeper
  52 + tb-mqtt-transport2:
  53 + env_file:
  54 + - queue-aws-sqs.env
  55 + depends_on:
  56 + - zookeeper
  57 + tb-http-transport1:
  58 + env_file:
  59 + - queue-aws-sqs.env
  60 + depends_on:
  61 + - zookeeper
  62 + tb-http-transport2:
  63 + env_file:
  64 + - queue-aws-sqs.env
  65 + depends_on:
  66 + - zookeeper
  67 + tb-coap-transport:
  68 + env_file:
  69 + - queue-aws-sqs.env
  70 + depends_on:
  71 + - zookeeper
\ No newline at end of file
... ...
... ... @@ -28,27 +28,27 @@ services:
28 28 env_file:
29 29 - tb-node.cassandra.env
30 30 depends_on:
31   - - kafka
  31 + - zookeeper
32 32 - redis
33 33 - cassandra
34 34 tb-core2:
35 35 env_file:
36 36 - tb-node.cassandra.env
37 37 depends_on:
38   - - kafka
  38 + - zookeeper
39 39 - redis
40 40 - cassandra
41 41 tb-rule-engine1:
42 42 env_file:
43 43 - tb-node.cassandra.env
44 44 depends_on:
45   - - kafka
  45 + - zookeeper
46 46 - redis
47 47 - cassandra
48 48 tb-rule-engine2:
49 49 env_file:
50 50 - tb-node.cassandra.env
51 51 depends_on:
52   - - kafka
  52 + - zookeeper
53 53 - redis
54 54 - cassandra
... ...
  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 +
  17 +version: '2.2'
  18 +
  19 +services:
  20 + kafka:
  21 + restart: always
  22 + image: "wurstmeister/kafka:2.12-2.3.0"
  23 + ports:
  24 + - "9092:9092"
  25 + env_file:
  26 + - kafka.env
  27 + depends_on:
  28 + - zookeeper
  29 + tb-js-executor:
  30 + env_file:
  31 + - queue-kafka.env
  32 + depends_on:
  33 + - kafka
  34 + tb-core1:
  35 + env_file:
  36 + - queue-kafka.env
  37 + depends_on:
  38 + - kafka
  39 + - redis
  40 + tb-core2:
  41 + env_file:
  42 + - queue-kafka.env
  43 + depends_on:
  44 + - kafka
  45 + - redis
  46 + tb-rule-engine1:
  47 + env_file:
  48 + - queue-kafka.env
  49 + depends_on:
  50 + - kafka
  51 + - redis
  52 + tb-rule-engine2:
  53 + env_file:
  54 + - queue-kafka.env
  55 + depends_on:
  56 + - kafka
  57 + - redis
  58 + tb-mqtt-transport1:
  59 + env_file:
  60 + - queue-kafka.env
  61 + depends_on:
  62 + - kafka
  63 + tb-mqtt-transport2:
  64 + env_file:
  65 + - queue-kafka.env
  66 + depends_on:
  67 + - kafka
  68 + tb-http-transport1:
  69 + env_file:
  70 + - queue-kafka.env
  71 + depends_on:
  72 + - kafka
  73 + tb-http-transport2:
  74 + env_file:
  75 + - queue-kafka.env
  76 + depends_on:
  77 + - kafka
  78 + tb-coap-transport:
  79 + env_file:
  80 + - queue-kafka.env
  81 + depends_on:
  82 + - kafka
... ...
... ... @@ -31,27 +31,27 @@ services:
31 31 env_file:
32 32 - tb-node.postgres.env
33 33 depends_on:
34   - - kafka
  34 + - zookeeper
35 35 - redis
36 36 - postgres
37 37 tb-core2:
38 38 env_file:
39 39 - tb-node.postgres.env
40 40 depends_on:
41   - - kafka
  41 + - zookeeper
42 42 - redis
43 43 - postgres
44 44 tb-rule-engine1:
45 45 env_file:
46 46 - tb-node.postgres.env
47 47 depends_on:
48   - - kafka
  48 + - zookeeper
49 49 - redis
50 50 - postgres
51 51 tb-rule-engine2:
52 52 env_file:
53 53 - tb-node.postgres.env
54 54 depends_on:
55   - - kafka
  55 + - zookeeper
56 56 - redis
57 57 - postgres
... ...
  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 +
  17 +version: '2.2'
  18 +
  19 +services:
  20 + tb-js-executor:
  21 + env_file:
  22 + - queue-pubsub.env
  23 + tb-core1:
  24 + env_file:
  25 + - queue-pubsub.env
  26 + depends_on:
  27 + - zookeeper
  28 + - redis
  29 + tb-core2:
  30 + env_file:
  31 + - queue-pubsub.env
  32 + depends_on:
  33 + - zookeeper
  34 + - redis
  35 + tb-rule-engine1:
  36 + env_file:
  37 + - queue-pubsub.env
  38 + depends_on:
  39 + - zookeeper
  40 + - redis
  41 + tb-rule-engine2:
  42 + env_file:
  43 + - queue-pubsub.env
  44 + depends_on:
  45 + - zookeeper
  46 + - redis
  47 + tb-mqtt-transport1:
  48 + env_file:
  49 + - queue-pubsub.env
  50 + depends_on:
  51 + - zookeeper
  52 + tb-mqtt-transport2:
  53 + env_file:
  54 + - queue-pubsub.env
  55 + depends_on:
  56 + - zookeeper
  57 + tb-http-transport1:
  58 + env_file:
  59 + - queue-pubsub.env
  60 + depends_on:
  61 + - zookeeper
  62 + tb-http-transport2:
  63 + env_file:
  64 + - queue-pubsub.env
  65 + depends_on:
  66 + - zookeeper
  67 + tb-coap-transport:
  68 + env_file:
  69 + - queue-pubsub.env
  70 + depends_on:
  71 + - zookeeper
... ...
  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 +
  17 +version: '2.2'
  18 +
  19 +services:
  20 + tb-js-executor:
  21 + env_file:
  22 + - queue-rabbitmq.env
  23 + tb-core1:
  24 + env_file:
  25 + - queue-rabbitmq.env
  26 + depends_on:
  27 + - zookeeper
  28 + - redis
  29 + tb-core2:
  30 + env_file:
  31 + - queue-rabbitmq.env
  32 + depends_on:
  33 + - zookeeper
  34 + - redis
  35 + tb-rule-engine1:
  36 + env_file:
  37 + - queue-rabbitmq.env
  38 + depends_on:
  39 + - zookeeper
  40 + - redis
  41 + tb-rule-engine2:
  42 + env_file:
  43 + - queue-rabbitmq.env
  44 + depends_on:
  45 + - zookeeper
  46 + - redis
  47 + tb-mqtt-transport1:
  48 + env_file:
  49 + - queue-rabbitmq.env
  50 + depends_on:
  51 + - zookeeper
  52 + tb-mqtt-transport2:
  53 + env_file:
  54 + - queue-rabbitmq.env
  55 + depends_on:
  56 + - zookeeper
  57 + tb-http-transport1:
  58 + env_file:
  59 + - queue-rabbitmq.env
  60 + depends_on:
  61 + - zookeeper
  62 + tb-http-transport2:
  63 + env_file:
  64 + - queue-rabbitmq.env
  65 + depends_on:
  66 + - zookeeper
  67 + tb-coap-transport:
  68 + env_file:
  69 + - queue-rabbitmq.env
  70 + depends_on:
  71 + - zookeeper
\ No newline at end of file
... ...
  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 +
  17 +version: '2.2'
  18 +
  19 +services:
  20 + tb-js-executor:
  21 + env_file:
  22 + - queue-service-bus.env
  23 + tb-core1:
  24 + env_file:
  25 + - queue-service-bus.env
  26 + depends_on:
  27 + - zookeeper
  28 + - redis
  29 + tb-core2:
  30 + env_file:
  31 + - queue-service-bus.env
  32 + depends_on:
  33 + - zookeeper
  34 + - redis
  35 + tb-rule-engine1:
  36 + env_file:
  37 + - queue-service-bus.env
  38 + depends_on:
  39 + - zookeeper
  40 + - redis
  41 + tb-rule-engine2:
  42 + env_file:
  43 + - queue-service-bus.env
  44 + depends_on:
  45 + - zookeeper
  46 + - redis
  47 + tb-mqtt-transport1:
  48 + env_file:
  49 + - queue-service-bus.env
  50 + depends_on:
  51 + - zookeeper
  52 + tb-mqtt-transport2:
  53 + env_file:
  54 + - queue-service-bus.env
  55 + depends_on:
  56 + - zookeeper
  57 + tb-http-transport1:
  58 + env_file:
  59 + - queue-service-bus.env
  60 + depends_on:
  61 + - zookeeper
  62 + tb-http-transport2:
  63 + env_file:
  64 + - queue-service-bus.env
  65 + depends_on:
  66 + - zookeeper
  67 + tb-coap-transport:
  68 + env_file:
  69 + - queue-service-bus.env
  70 + depends_on:
  71 + - zookeeper
\ No newline at end of file
... ...
... ... @@ -26,15 +26,6 @@ services:
26 26 environment:
27 27 ZOO_MY_ID: 1
28 28 ZOO_SERVERS: server.1=zookeeper:2888:3888;zookeeper:2181
29   - kafka:
30   - restart: always
31   - image: "wurstmeister/kafka:2.12-2.3.0"
32   - ports:
33   - - "9092:9092"
34   - env_file:
35   - - kafka.env
36   - depends_on:
37   - - zookeeper
38 29 redis:
39 30 restart: always
40 31 image: redis:4.0
... ... @@ -46,8 +37,6 @@ services:
46 37 scale: 20
47 38 env_file:
48 39 - tb-js-executor.env
49   - depends_on:
50   - - kafka
51 40 tb-core1:
52 41 restart: always
53 42 image: "${DOCKER_REPO}/${TB_NODE_DOCKER_NAME}:${TB_VERSION}"
... ... @@ -67,7 +56,7 @@ services:
67 56 - ./tb-node/conf:/config
68 57 - ./tb-node/log:/var/log/thingsboard
69 58 depends_on:
70   - - kafka
  59 + - zookeeper
71 60 - redis
72 61 - tb-js-executor
73 62 - tb-rule-engine1
... ... @@ -91,7 +80,7 @@ services:
91 80 - ./tb-node/conf:/config
92 81 - ./tb-node/log:/var/log/thingsboard
93 82 depends_on:
94   - - kafka
  83 + - zookeeper
95 84 - redis
96 85 - tb-js-executor
97 86 - tb-rule-engine1
... ... @@ -115,7 +104,7 @@ services:
115 104 - ./tb-node/conf:/config
116 105 - ./tb-node/log:/var/log/thingsboard
117 106 depends_on:
118   - - kafka
  107 + - zookeeper
119 108 - redis
120 109 - tb-js-executor
121 110 tb-rule-engine2:
... ... @@ -137,7 +126,7 @@ services:
137 126 - ./tb-node/conf:/config
138 127 - ./tb-node/log:/var/log/thingsboard
139 128 depends_on:
140   - - kafka
  129 + - zookeeper
141 130 - redis
142 131 - tb-js-executor
143 132 tb-mqtt-transport1:
... ... @@ -153,7 +142,7 @@ services:
153 142 - ./tb-transports/mqtt/conf:/config
154 143 - ./tb-transports/mqtt/log:/var/log/tb-mqtt-transport
155 144 depends_on:
156   - - kafka
  145 + - zookeeper
157 146 tb-mqtt-transport2:
158 147 restart: always
159 148 image: "${DOCKER_REPO}/${MQTT_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
... ... @@ -167,7 +156,7 @@ services:
167 156 - ./tb-transports/mqtt/conf:/config
168 157 - ./tb-transports/mqtt/log:/var/log/tb-mqtt-transport
169 158 depends_on:
170   - - kafka
  159 + - zookeeper
171 160 tb-http-transport1:
172 161 restart: always
173 162 image: "${DOCKER_REPO}/${HTTP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
... ... @@ -181,7 +170,7 @@ services:
181 170 - ./tb-transports/http/conf:/config
182 171 - ./tb-transports/http/log:/var/log/tb-http-transport
183 172 depends_on:
184   - - kafka
  173 + - zookeeper
185 174 tb-http-transport2:
186 175 restart: always
187 176 image: "${DOCKER_REPO}/${HTTP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
... ... @@ -195,7 +184,7 @@ services:
195 184 - ./tb-transports/http/conf:/config
196 185 - ./tb-transports/http/log:/var/log/tb-http-transport
197 186 depends_on:
198   - - kafka
  187 + - zookeeper
199 188 tb-coap-transport:
200 189 restart: always
201 190 image: "${DOCKER_REPO}/${COAP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
... ... @@ -209,7 +198,7 @@ services:
209 198 - ./tb-transports/coap/conf:/config
210 199 - ./tb-transports/coap/log:/var/log/tb-coap-transport
211 200 depends_on:
212   - - kafka
  201 + - zookeeper
213 202 tb-web-ui1:
214 203 restart: always
215 204 image: "${DOCKER_REPO}/${WEB_UI_DOCKER_NAME}:${TB_VERSION}"
... ...
... ... @@ -41,14 +41,16 @@ set -e
41 41
42 42 source compose-utils.sh
43 43
  44 +ADDITIONAL_COMPOSE_QUEUE_ARGS=$(additionalComposeQueueArgs) || exit $?
  45 +
44 46 ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $?
45 47
46 48 ADDITIONAL_STARTUP_SERVICES=$(additionalStartupServices) || exit $?
47 49
48 50 if [ ! -z "${ADDITIONAL_STARTUP_SERVICES// }" ]; then
49   - docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS up -d redis $ADDITIONAL_STARTUP_SERVICES
  51 + docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS up -d redis $ADDITIONAL_STARTUP_SERVICES
50 52 fi
51 53
52   -docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS run --no-deps --rm -e INSTALL_TB=true -e LOAD_DEMO=${loadDemo} tb-core1
  54 +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS run --no-deps --rm -e INSTALL_TB=true -e LOAD_DEMO=${loadDemo} tb-core1
53 55
54 56
... ...
... ... @@ -19,6 +19,8 @@ set -e
19 19
20 20 source compose-utils.sh
21 21
  22 +ADDITIONAL_COMPOSE_QUEUE_ARGS=$(additionalComposeQueueArgs) || exit $?
  23 +
22 24 ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $?
23 25
24   -docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS down -v
  26 +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS down -v
... ...
... ... @@ -19,6 +19,8 @@ set -e
19 19
20 20 source compose-utils.sh
21 21
  22 +ADDITIONAL_COMPOSE_QUEUE_ARGS=$(additionalComposeQueueArgs) || exit $?
  23 +
22 24 ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $?
23 25
24   -docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS up -d
  26 +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS up -d
... ...
... ... @@ -19,6 +19,8 @@ set -e
19 19
20 20 source compose-utils.sh
21 21
  22 +ADDITIONAL_COMPOSE_QUEUE_ARGS=$(additionalComposeQueueArgs) || exit $?
  23 +
22 24 ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $?
23 25
24   -docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS stop
  26 +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS stop
... ...
... ... @@ -19,7 +19,9 @@ set -e
19 19
20 20 source compose-utils.sh
21 21
  22 +ADDITIONAL_COMPOSE_QUEUE_ARGS=$(additionalComposeQueueArgs) || exit $?
  23 +
22 24 ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $?
23 25
24   -docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS pull $@
25   -docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS up -d --no-deps --build $@
  26 +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS pull $@
  27 +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS up -d --no-deps --build $@
... ...
... ... @@ -40,12 +40,14 @@ set -e
40 40
41 41 source compose-utils.sh
42 42
  43 +ADDITIONAL_COMPOSE_QUEUE_ARGS=$(additionalComposeQueueArgs) || exit $?
  44 +
43 45 ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $?
44 46
45 47 ADDITIONAL_STARTUP_SERVICES=$(additionalStartupServices) || exit $?
46 48
47   -docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS pull tb-core1
  49 +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS pull tb-core1
48 50
49   -docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS up -d redis $ADDITIONAL_STARTUP_SERVICES
  51 +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS up -d redis $ADDITIONAL_STARTUP_SERVICES
50 52
51   -docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS run --no-deps --rm -e UPGRADE_TB=true -e FROM_VERSION=${fromVersion} tb-core1
  53 +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS run --no-deps --rm -e UPGRADE_TB=true -e FROM_VERSION=${fromVersion} tb-core1
... ...
  1 +TB_QUEUE_TYPE=aws-sqs
  2 +TB_QUEUE_AWS_SQS_ACCESS_KEY_ID=YOUR_KEY
  3 +TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY=YOUR_SECRET
  4 +TB_QUEUE_AWS_SQS_REGION=YOUR_REGION
... ...
  1 +TB_QUEUE_TYPE=kafka
  2 +TB_KAFKA_SERVERS=kafka:9092
... ...
  1 +TB_QUEUE_TYPE=pubsub
  2 +TB_QUEUE_PUBSUB_PROJECT_ID=YOUR_PROJECT_ID
  3 +TB_QUEUE_PUBSUB_SERVICE_ACCOUNT=YOUR_SERVICE_ACCOUNT
... ...
  1 +TB_QUEUE_TYPE=rabbitmq
  2 +TB_QUEUE_RABBIT_MQ_HOST=localhost
  3 +TB_QUEUE_RABBIT_MQ_PORT=5672
  4 +TB_QUEUE_RABBIT_MQ_USERNAME=YOUR_USERNAME
  5 +TB_QUEUE_RABBIT_MQ_PASSWORD=YOUR_PASSWORD
\ No newline at end of file
... ...
  1 +TB_QUEUE_TYPE=service-bus
  2 +TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME=YOUR_NAMESPACE_NAME
  3 +TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME=YOUR_SAS_KEY_NAME
  4 +TB_QUEUE_SERVICE_BUS_SAS_KEY=YOUR_SAS_KEY
... ...
... ... @@ -4,6 +4,3 @@ ZOOKEEPER_URL=zookeeper:2181
4 4 COAP_BIND_ADDRESS=0.0.0.0
5 5 COAP_BIND_PORT=5683
6 6 COAP_TIMEOUT=10000
7   -
8   -TB_QUEUE_TYPE=kafka
9   -TB_KAFKA_SERVERS=kafka:9092
\ No newline at end of file
... ...