diff --git a/application/src/main/data/lwm2m-registry/0.xml b/application/src/main/data/lwm2m-registry/0.xml
new file mode 100644
index 0000000000..60744d35c7
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/0.xml
@@ -0,0 +1,405 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/1.xml b/application/src/main/data/lwm2m-registry/1.xml
new file mode 100644
index 0000000000..ace9efcc6a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/1.xml
@@ -0,0 +1,360 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10.xml b/application/src/main/data/lwm2m-registry/10.xml
new file mode 100644
index 0000000000..af87413a94
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10.xml
@@ -0,0 +1,217 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10241.xml b/application/src/main/data/lwm2m-registry/10241.xml
new file mode 100644
index 0000000000..5a499100f3
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10241.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10242.xml b/application/src/main/data/lwm2m-registry/10242.xml
new file mode 100644
index 0000000000..09a62806d3
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10242.xml
@@ -0,0 +1,659 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10243.xml b/application/src/main/data/lwm2m-registry/10243.xml
new file mode 100644
index 0000000000..b73ce20021
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10243.xml
@@ -0,0 +1,263 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10244.xml b/application/src/main/data/lwm2m-registry/10244.xml
new file mode 100644
index 0000000000..9f55cd47de
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10244.xml
@@ -0,0 +1,303 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10245.xml b/application/src/main/data/lwm2m-registry/10245.xml
new file mode 100644
index 0000000000..b29349130d
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10245.xml
@@ -0,0 +1,217 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10246.xml b/application/src/main/data/lwm2m-registry/10246.xml
new file mode 100644
index 0000000000..c3977d9538
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10246.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10247.xml b/application/src/main/data/lwm2m-registry/10247.xml
new file mode 100644
index 0000000000..eba05417e8
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10247.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10248.xml b/application/src/main/data/lwm2m-registry/10248.xml
new file mode 100644
index 0000000000..e9f69f7889
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10248.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10249.xml b/application/src/main/data/lwm2m-registry/10249.xml
new file mode 100644
index 0000000000..e1257c3dda
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10249.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10250.xml b/application/src/main/data/lwm2m-registry/10250.xml
new file mode 100644
index 0000000000..015149fae2
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10250.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10251.xml b/application/src/main/data/lwm2m-registry/10251.xml
new file mode 100644
index 0000000000..3fb1c00154
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10251.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10252.xml b/application/src/main/data/lwm2m-registry/10252.xml
new file mode 100644
index 0000000000..ddaf7cb1f2
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10252.xml
@@ -0,0 +1,229 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10253.xml b/application/src/main/data/lwm2m-registry/10253.xml
new file mode 100644
index 0000000000..27da37d16a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10253.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10254.xml b/application/src/main/data/lwm2m-registry/10254.xml
new file mode 100644
index 0000000000..ea37cf8cd8
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10254.xml
@@ -0,0 +1,119 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10255.xml b/application/src/main/data/lwm2m-registry/10255.xml
new file mode 100644
index 0000000000..fd63016952
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10255.xml
@@ -0,0 +1,165 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10256.xml b/application/src/main/data/lwm2m-registry/10256.xml
new file mode 100644
index 0000000000..dbcad740d0
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10256.xml
@@ -0,0 +1,117 @@
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10257.xml b/application/src/main/data/lwm2m-registry/10257.xml
new file mode 100644
index 0000000000..75864d542b
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10257.xml
@@ -0,0 +1,275 @@
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10258.xml b/application/src/main/data/lwm2m-registry/10258.xml
new file mode 100644
index 0000000000..e2a9438170
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10258.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10259.xml b/application/src/main/data/lwm2m-registry/10259.xml
new file mode 100644
index 0000000000..d75869a122
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10259.xml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10260.xml b/application/src/main/data/lwm2m-registry/10260.xml
new file mode 100644
index 0000000000..5cd084af73
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10260.xml
@@ -0,0 +1,95 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10262.xml b/application/src/main/data/lwm2m-registry/10262.xml
new file mode 100644
index 0000000000..9fd47ac3af
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10262.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10263.xml b/application/src/main/data/lwm2m-registry/10263.xml
new file mode 100644
index 0000000000..07b9cde3ca
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10263.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10264.xml b/application/src/main/data/lwm2m-registry/10264.xml
new file mode 100644
index 0000000000..b11c42a21d
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10264.xml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10265.xml b/application/src/main/data/lwm2m-registry/10265.xml
new file mode 100644
index 0000000000..2604ae86d7
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10265.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10266.xml b/application/src/main/data/lwm2m-registry/10266.xml
new file mode 100644
index 0000000000..48ee41319a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10266.xml
@@ -0,0 +1,246 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10267.xml b/application/src/main/data/lwm2m-registry/10267.xml
new file mode 100644
index 0000000000..ffe70b5f2f
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10267.xml
@@ -0,0 +1,246 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10268.xml b/application/src/main/data/lwm2m-registry/10268.xml
new file mode 100644
index 0000000000..a1ccd43888
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10268.xml
@@ -0,0 +1,246 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10269.xml b/application/src/main/data/lwm2m-registry/10269.xml
new file mode 100644
index 0000000000..e4156e055e
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10269.xml
@@ -0,0 +1,246 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10270.xml b/application/src/main/data/lwm2m-registry/10270.xml
new file mode 100644
index 0000000000..b7d171920c
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10270.xml
@@ -0,0 +1,246 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10271.xml b/application/src/main/data/lwm2m-registry/10271.xml
new file mode 100644
index 0000000000..b4f8dfa6d5
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10271.xml
@@ -0,0 +1,246 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10272.xml b/application/src/main/data/lwm2m-registry/10272.xml
new file mode 100644
index 0000000000..0464c98a73
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10272.xml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10273.xml b/application/src/main/data/lwm2m-registry/10273.xml
new file mode 100644
index 0000000000..823adae0ed
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10273.xml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10274.xml b/application/src/main/data/lwm2m-registry/10274.xml
new file mode 100644
index 0000000000..f61554ca88
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10274.xml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10275.xml b/application/src/main/data/lwm2m-registry/10275.xml
new file mode 100644
index 0000000000..10248ea511
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10275.xml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10276.xml b/application/src/main/data/lwm2m-registry/10276.xml
new file mode 100644
index 0000000000..5863f0326b
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10276.xml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10277.xml b/application/src/main/data/lwm2m-registry/10277.xml
new file mode 100644
index 0000000000..83c6d4f0d5
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10277.xml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10278.xml b/application/src/main/data/lwm2m-registry/10278.xml
new file mode 100644
index 0000000000..d004f89de6
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10278.xml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10279.xml b/application/src/main/data/lwm2m-registry/10279.xml
new file mode 100644
index 0000000000..38838e7c94
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10279.xml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10280.xml b/application/src/main/data/lwm2m-registry/10280.xml
new file mode 100644
index 0000000000..de2b7b6732
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10280.xml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10281.xml b/application/src/main/data/lwm2m-registry/10281.xml
new file mode 100644
index 0000000000..99eab667f2
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10281.xml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10282.xml b/application/src/main/data/lwm2m-registry/10282.xml
new file mode 100644
index 0000000000..e603de36b3
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10282.xml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10283.xml b/application/src/main/data/lwm2m-registry/10283.xml
new file mode 100644
index 0000000000..fd130fa713
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10283.xml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10284.xml b/application/src/main/data/lwm2m-registry/10284.xml
new file mode 100644
index 0000000000..1e889da48a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10284.xml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10286.xml b/application/src/main/data/lwm2m-registry/10286.xml
new file mode 100644
index 0000000000..39e0809788
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10286.xml
@@ -0,0 +1,48 @@
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10290.xml b/application/src/main/data/lwm2m-registry/10290.xml
new file mode 100644
index 0000000000..7b89cc06c0
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10290.xml
@@ -0,0 +1,198 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10291.xml b/application/src/main/data/lwm2m-registry/10291.xml
new file mode 100644
index 0000000000..a232ed02f9
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10291.xml
@@ -0,0 +1,208 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10292.xml b/application/src/main/data/lwm2m-registry/10292.xml
new file mode 100644
index 0000000000..00fe33e320
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10292.xml
@@ -0,0 +1,208 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10299.xml b/application/src/main/data/lwm2m-registry/10299.xml
new file mode 100644
index 0000000000..a16a7c4fc2
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10299.xml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10300.xml b/application/src/main/data/lwm2m-registry/10300.xml
new file mode 100644
index 0000000000..234f0301a4
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10300.xml
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10308.xml b/application/src/main/data/lwm2m-registry/10308.xml
new file mode 100644
index 0000000000..0ed1a6ea30
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10308.xml
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10309.xml b/application/src/main/data/lwm2m-registry/10309.xml
new file mode 100644
index 0000000000..b37b435724
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10309.xml
@@ -0,0 +1,115 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10311.xml b/application/src/main/data/lwm2m-registry/10311.xml
new file mode 100644
index 0000000000..6b6a78d21d
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10311.xml
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10313.xml b/application/src/main/data/lwm2m-registry/10313.xml
new file mode 100644
index 0000000000..4875e5d6b0
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10313.xml
@@ -0,0 +1,253 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10314.xml b/application/src/main/data/lwm2m-registry/10314.xml
new file mode 100644
index 0000000000..f003fa03c2
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10314.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10315.xml b/application/src/main/data/lwm2m-registry/10315.xml
new file mode 100644
index 0000000000..da7ff8c67e
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10315.xml
@@ -0,0 +1,129 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10316.xml b/application/src/main/data/lwm2m-registry/10316.xml
new file mode 100644
index 0000000000..d10dabf99b
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10316.xml
@@ -0,0 +1,233 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10318.xml b/application/src/main/data/lwm2m-registry/10318.xml
new file mode 100644
index 0000000000..989db0de7e
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10318.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10319.xml b/application/src/main/data/lwm2m-registry/10319.xml
new file mode 100644
index 0000000000..b8ebcef6fb
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10319.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10320.xml b/application/src/main/data/lwm2m-registry/10320.xml
new file mode 100644
index 0000000000..fb48c814f0
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10320.xml
@@ -0,0 +1,141 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10322.xml b/application/src/main/data/lwm2m-registry/10322.xml
new file mode 100644
index 0000000000..98822e0efe
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10322.xml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10323.xml b/application/src/main/data/lwm2m-registry/10323.xml
new file mode 100644
index 0000000000..dcbdaeb91d
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10323.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10324.xml b/application/src/main/data/lwm2m-registry/10324.xml
new file mode 100644
index 0000000000..b3db2b4f06
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10324.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10326.xml b/application/src/main/data/lwm2m-registry/10326.xml
new file mode 100644
index 0000000000..cdc01a0e61
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10326.xml
@@ -0,0 +1,722 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10327.xml b/application/src/main/data/lwm2m-registry/10327.xml
new file mode 100644
index 0000000000..5f0e232a22
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10327.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10328.xml b/application/src/main/data/lwm2m-registry/10328.xml
new file mode 100644
index 0000000000..fdf39d5dc1
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10328.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10329.xml b/application/src/main/data/lwm2m-registry/10329.xml
new file mode 100644
index 0000000000..7470bec6e1
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10329.xml
@@ -0,0 +1,433 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10330.xml b/application/src/main/data/lwm2m-registry/10330.xml
new file mode 100644
index 0000000000..75c5a2ecdd
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10330.xml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10331.xml b/application/src/main/data/lwm2m-registry/10331.xml
new file mode 100644
index 0000000000..ca555cc0d9
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10331.xml
@@ -0,0 +1,221 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10332.xml b/application/src/main/data/lwm2m-registry/10332.xml
new file mode 100644
index 0000000000..f582373043
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10332.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10333.xml b/application/src/main/data/lwm2m-registry/10333.xml
new file mode 100644
index 0000000000..aaeb25487d
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10333.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10334.xml b/application/src/main/data/lwm2m-registry/10334.xml
new file mode 100644
index 0000000000..479406630c
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10334.xml
@@ -0,0 +1,129 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10335.xml b/application/src/main/data/lwm2m-registry/10335.xml
new file mode 100644
index 0000000000..91fe8cd9c5
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10335.xml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10336.xml b/application/src/main/data/lwm2m-registry/10336.xml
new file mode 100644
index 0000000000..5016e6ee13
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10336.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10337.xml b/application/src/main/data/lwm2m-registry/10337.xml
new file mode 100644
index 0000000000..6204020400
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10337.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10338.xml b/application/src/main/data/lwm2m-registry/10338.xml
new file mode 100644
index 0000000000..c66df8addf
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10338.xml
@@ -0,0 +1,124 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10339.xml b/application/src/main/data/lwm2m-registry/10339.xml
new file mode 100644
index 0000000000..5376de4a6c
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10339.xml
@@ -0,0 +1,114 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10340.xml b/application/src/main/data/lwm2m-registry/10340.xml
new file mode 100644
index 0000000000..679b46f150
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10340.xml
@@ -0,0 +1,218 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10341.xml b/application/src/main/data/lwm2m-registry/10341.xml
new file mode 100644
index 0000000000..c632fd5c57
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10341.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10342.xml b/application/src/main/data/lwm2m-registry/10342.xml
new file mode 100644
index 0000000000..644bcf9071
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10342.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10343.xml b/application/src/main/data/lwm2m-registry/10343.xml
new file mode 100644
index 0000000000..a34c80f3a6
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10343.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10344.xml b/application/src/main/data/lwm2m-registry/10344.xml
new file mode 100644
index 0000000000..d28d343ae3
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10344.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10345.xml b/application/src/main/data/lwm2m-registry/10345.xml
new file mode 100644
index 0000000000..ad489a7faa
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10345.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10346.xml b/application/src/main/data/lwm2m-registry/10346.xml
new file mode 100644
index 0000000000..4ee595e16a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10346.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10347.xml b/application/src/main/data/lwm2m-registry/10347.xml
new file mode 100644
index 0000000000..6c0808ed61
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10347.xml
@@ -0,0 +1,154 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10348.xml b/application/src/main/data/lwm2m-registry/10348.xml
new file mode 100644
index 0000000000..76451605f0
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10348.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10349.xml b/application/src/main/data/lwm2m-registry/10349.xml
new file mode 100644
index 0000000000..a4212558c4
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10349.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10350.xml b/application/src/main/data/lwm2m-registry/10350.xml
new file mode 100644
index 0000000000..1942cabe0c
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10350.xml
@@ -0,0 +1,116 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10351.xml b/application/src/main/data/lwm2m-registry/10351.xml
new file mode 100644
index 0000000000..473b440b02
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10351.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10352.xml b/application/src/main/data/lwm2m-registry/10352.xml
new file mode 100644
index 0000000000..1e7503ef42
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10352.xml
@@ -0,0 +1,114 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10353.xml b/application/src/main/data/lwm2m-registry/10353.xml
new file mode 100644
index 0000000000..0b6bb56cc4
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10353.xml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10354.xml b/application/src/main/data/lwm2m-registry/10354.xml
new file mode 100644
index 0000000000..60d5fc0050
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10354.xml
@@ -0,0 +1,217 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10355.xml b/application/src/main/data/lwm2m-registry/10355.xml
new file mode 100644
index 0000000000..fb38ee2767
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10355.xml
@@ -0,0 +1,95 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10356.xml b/application/src/main/data/lwm2m-registry/10356.xml
new file mode 100644
index 0000000000..2e362ce052
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10356.xml
@@ -0,0 +1,203 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10357.xml b/application/src/main/data/lwm2m-registry/10357.xml
new file mode 100644
index 0000000000..9487b2813d
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10357.xml
@@ -0,0 +1,95 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10358.xml b/application/src/main/data/lwm2m-registry/10358.xml
new file mode 100644
index 0000000000..27a3684b22
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10358.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10359.xml b/application/src/main/data/lwm2m-registry/10359.xml
new file mode 100644
index 0000000000..2fc8af9677
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10359.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10360.xml b/application/src/main/data/lwm2m-registry/10360.xml
new file mode 100644
index 0000000000..935fea848c
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10360.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10361.xml b/application/src/main/data/lwm2m-registry/10361.xml
new file mode 100644
index 0000000000..3525647afd
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10361.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10362.xml b/application/src/main/data/lwm2m-registry/10362.xml
new file mode 100644
index 0000000000..8a928e4425
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10362.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10363.xml b/application/src/main/data/lwm2m-registry/10363.xml
new file mode 100644
index 0000000000..ca9d064f2f
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10363.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10364.xml b/application/src/main/data/lwm2m-registry/10364.xml
new file mode 100644
index 0000000000..3519665e62
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10364.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10365.xml b/application/src/main/data/lwm2m-registry/10365.xml
new file mode 100644
index 0000000000..792844271e
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10365.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10366.xml b/application/src/main/data/lwm2m-registry/10366.xml
new file mode 100644
index 0000000000..44e94ae51d
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10366.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10368.xml b/application/src/main/data/lwm2m-registry/10368.xml
new file mode 100644
index 0000000000..68d3dd3ee3
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10368.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10369.xml b/application/src/main/data/lwm2m-registry/10369.xml
new file mode 100644
index 0000000000..dcf8190bda
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10369.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10371.xml b/application/src/main/data/lwm2m-registry/10371.xml
new file mode 100644
index 0000000000..73a4476865
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10371.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10374.xml b/application/src/main/data/lwm2m-registry/10374.xml
new file mode 100644
index 0000000000..a51646b95f
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10374.xml
@@ -0,0 +1,161 @@
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10375.xml b/application/src/main/data/lwm2m-registry/10375.xml
new file mode 100644
index 0000000000..5818491c51
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10375.xml
@@ -0,0 +1,148 @@
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10376.xml b/application/src/main/data/lwm2m-registry/10376.xml
new file mode 100644
index 0000000000..faec95398e
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10376.xml
@@ -0,0 +1,211 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10377.xml b/application/src/main/data/lwm2m-registry/10377.xml
new file mode 100644
index 0000000000..f46ace72aa
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10377.xml
@@ -0,0 +1,251 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/10378.xml b/application/src/main/data/lwm2m-registry/10378.xml
new file mode 100644
index 0000000000..bbd45811d0
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/10378.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/11.xml b/application/src/main/data/lwm2m-registry/11.xml
new file mode 100644
index 0000000000..438b2c3df2
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/11.xml
@@ -0,0 +1,408 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/12.xml b/application/src/main/data/lwm2m-registry/12.xml
new file mode 100644
index 0000000000..d4aad43dca
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/12.xml
@@ -0,0 +1,551 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/13.xml b/application/src/main/data/lwm2m-registry/13.xml
new file mode 100644
index 0000000000..9b30ecd225
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/13.xml
@@ -0,0 +1,209 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/14.xml b/application/src/main/data/lwm2m-registry/14.xml
new file mode 100644
index 0000000000..a1d7f7239f
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/14.xml
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/15.xml b/application/src/main/data/lwm2m-registry/15.xml
new file mode 100644
index 0000000000..f4363e3f33
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/15.xml
@@ -0,0 +1,175 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/16.xml b/application/src/main/data/lwm2m-registry/16.xml
new file mode 100644
index 0000000000..f8f2dc40df
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/16.xml
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/18830.xml b/application/src/main/data/lwm2m-registry/18830.xml
new file mode 100644
index 0000000000..87e0747d5c
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/18830.xml
@@ -0,0 +1,173 @@
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/18831.xml b/application/src/main/data/lwm2m-registry/18831.xml
new file mode 100644
index 0000000000..68dc873ed0
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/18831.xml
@@ -0,0 +1,120 @@
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/19.xml b/application/src/main/data/lwm2m-registry/19.xml
new file mode 100644
index 0000000000..c4f5cbe6ab
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/19.xml
@@ -0,0 +1,144 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/2.xml b/application/src/main/data/lwm2m-registry/2.xml
new file mode 100644
index 0000000000..4ea5805b36
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/2.xml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/20.xml b/application/src/main/data/lwm2m-registry/20.xml
new file mode 100644
index 0000000000..68aa96048c
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/20.xml
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/2048.xml b/application/src/main/data/lwm2m-registry/2048.xml
new file mode 100644
index 0000000000..0bc99cffd1
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/2048.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/2049.xml b/application/src/main/data/lwm2m-registry/2049.xml
new file mode 100644
index 0000000000..7509b22160
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/2049.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/2050.xml b/application/src/main/data/lwm2m-registry/2050.xml
new file mode 100644
index 0000000000..da27a90de0
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/2050.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/2051.xml b/application/src/main/data/lwm2m-registry/2051.xml
new file mode 100644
index 0000000000..1f4ecda5d1
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/2051.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/2052.xml b/application/src/main/data/lwm2m-registry/2052.xml
new file mode 100644
index 0000000000..abaae1b54e
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/2052.xml
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/2053.xml b/application/src/main/data/lwm2m-registry/2053.xml
new file mode 100644
index 0000000000..a765a7894a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/2053.xml
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/2054.xml b/application/src/main/data/lwm2m-registry/2054.xml
new file mode 100644
index 0000000000..3935fb7253
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/2054.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/2055.xml b/application/src/main/data/lwm2m-registry/2055.xml
new file mode 100644
index 0000000000..cbd0dc5796
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/2055.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/2056.xml b/application/src/main/data/lwm2m-registry/2056.xml
new file mode 100644
index 0000000000..ddd50e269a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/2056.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/2057.xml b/application/src/main/data/lwm2m-registry/2057.xml
new file mode 100644
index 0000000000..68d4bcd135
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/2057.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/21.xml b/application/src/main/data/lwm2m-registry/21.xml
new file mode 100644
index 0000000000..5685648bad
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/21.xml
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/22.xml b/application/src/main/data/lwm2m-registry/22.xml
new file mode 100644
index 0000000000..02fed409ae
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/22.xml
@@ -0,0 +1,157 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/23.xml b/application/src/main/data/lwm2m-registry/23.xml
new file mode 100644
index 0000000000..55db80cc77
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/23.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/24.xml b/application/src/main/data/lwm2m-registry/24.xml
new file mode 100644
index 0000000000..7dd6942646
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/24.xml
@@ -0,0 +1,163 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/25.xml b/application/src/main/data/lwm2m-registry/25.xml
new file mode 100644
index 0000000000..5ab62b3c36
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/25.xml
@@ -0,0 +1,104 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/26.xml b/application/src/main/data/lwm2m-registry/26.xml
new file mode 100644
index 0000000000..0f77aad973
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/26.xml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/27.xml b/application/src/main/data/lwm2m-registry/27.xml
new file mode 100644
index 0000000000..9f30947997
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/27.xml
@@ -0,0 +1,322 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/28.xml b/application/src/main/data/lwm2m-registry/28.xml
new file mode 100644
index 0000000000..8edd51bcc5
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/28.xml
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3.xml b/application/src/main/data/lwm2m-registry/3.xml
new file mode 100644
index 0000000000..cb360acd10
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3.xml
@@ -0,0 +1,331 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3200.xml b/application/src/main/data/lwm2m-registry/3200.xml
new file mode 100644
index 0000000000..6a6310b5bb
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3200.xml
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3201.xml b/application/src/main/data/lwm2m-registry/3201.xml
new file mode 100644
index 0000000000..b950077387
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3201.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3202.xml b/application/src/main/data/lwm2m-registry/3202.xml
new file mode 100644
index 0000000000..7814f6ccf9
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3202.xml
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3203.xml b/application/src/main/data/lwm2m-registry/3203.xml
new file mode 100644
index 0000000000..0316e920a5
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3203.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3300.xml b/application/src/main/data/lwm2m-registry/3300.xml
new file mode 100644
index 0000000000..f7a5b8a3e2
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3300.xml
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3301.xml b/application/src/main/data/lwm2m-registry/3301.xml
new file mode 100644
index 0000000000..6604464f20
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3301.xml
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3302.xml b/application/src/main/data/lwm2m-registry/3302.xml
new file mode 100644
index 0000000000..fe9e886408
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3302.xml
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3303.xml b/application/src/main/data/lwm2m-registry/3303.xml
new file mode 100644
index 0000000000..2db5da74c8
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3303.xml
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3304.xml b/application/src/main/data/lwm2m-registry/3304.xml
new file mode 100644
index 0000000000..7331c5f83a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3304.xml
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3305.xml b/application/src/main/data/lwm2m-registry/3305.xml
new file mode 100644
index 0000000000..f927a44a59
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3305.xml
@@ -0,0 +1,278 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3306.xml b/application/src/main/data/lwm2m-registry/3306.xml
new file mode 100644
index 0000000000..748bc0f005
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3306.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3308.xml b/application/src/main/data/lwm2m-registry/3308.xml
new file mode 100644
index 0000000000..f1d5c18f12
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3308.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3310.xml b/application/src/main/data/lwm2m-registry/3310.xml
new file mode 100644
index 0000000000..6b4e04d6b6
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3310.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3311.xml b/application/src/main/data/lwm2m-registry/3311.xml
new file mode 100644
index 0000000000..4c98be2522
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3311.xml
@@ -0,0 +1,127 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3312.xml b/application/src/main/data/lwm2m-registry/3312.xml
new file mode 100644
index 0000000000..17bd0312db
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3312.xml
@@ -0,0 +1,127 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3313.xml b/application/src/main/data/lwm2m-registry/3313.xml
new file mode 100644
index 0000000000..43d0d63b88
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3313.xml
@@ -0,0 +1,157 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3314.xml b/application/src/main/data/lwm2m-registry/3314.xml
new file mode 100644
index 0000000000..96ebc5a537
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3314.xml
@@ -0,0 +1,147 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3315.xml b/application/src/main/data/lwm2m-registry/3315.xml
new file mode 100644
index 0000000000..d54a09edd8
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3315.xml
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3316.xml b/application/src/main/data/lwm2m-registry/3316.xml
new file mode 100644
index 0000000000..16a20a219a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3316.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3317.xml b/application/src/main/data/lwm2m-registry/3317.xml
new file mode 100644
index 0000000000..9d41da70de
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3317.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3318.xml b/application/src/main/data/lwm2m-registry/3318.xml
new file mode 100644
index 0000000000..957b593c89
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3318.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3319.xml b/application/src/main/data/lwm2m-registry/3319.xml
new file mode 100644
index 0000000000..e3a13ea3ef
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3319.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3320.xml b/application/src/main/data/lwm2m-registry/3320.xml
new file mode 100644
index 0000000000..9ec16fb671
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3320.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3321.xml b/application/src/main/data/lwm2m-registry/3321.xml
new file mode 100644
index 0000000000..e4a1f80bf1
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3321.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3322.xml b/application/src/main/data/lwm2m-registry/3322.xml
new file mode 100644
index 0000000000..838ed44995
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3322.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3323.xml b/application/src/main/data/lwm2m-registry/3323.xml
new file mode 100644
index 0000000000..e65c11c123
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3323.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3324.xml b/application/src/main/data/lwm2m-registry/3324.xml
new file mode 100644
index 0000000000..b80d427b91
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3324.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3325.xml b/application/src/main/data/lwm2m-registry/3325.xml
new file mode 100644
index 0000000000..c0577d64cd
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3325.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3326.xml b/application/src/main/data/lwm2m-registry/3326.xml
new file mode 100644
index 0000000000..e247637bcf
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3326.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3327.xml b/application/src/main/data/lwm2m-registry/3327.xml
new file mode 100644
index 0000000000..9cc689459f
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3327.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3328.xml b/application/src/main/data/lwm2m-registry/3328.xml
new file mode 100644
index 0000000000..752ebe0c53
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3328.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3329.xml b/application/src/main/data/lwm2m-registry/3329.xml
new file mode 100644
index 0000000000..fe221a1564
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3329.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3330.xml b/application/src/main/data/lwm2m-registry/3330.xml
new file mode 100644
index 0000000000..baec2dab14
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3330.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3331.xml b/application/src/main/data/lwm2m-registry/3331.xml
new file mode 100644
index 0000000000..1094d5bf31
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3331.xml
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3332.xml b/application/src/main/data/lwm2m-registry/3332.xml
new file mode 100644
index 0000000000..d4026e9558
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3332.xml
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3333.xml b/application/src/main/data/lwm2m-registry/3333.xml
new file mode 100644
index 0000000000..1fc7117482
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3333.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3334.xml b/application/src/main/data/lwm2m-registry/3334.xml
new file mode 100644
index 0000000000..4ff3e6c6a6
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3334.xml
@@ -0,0 +1,229 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3335.xml b/application/src/main/data/lwm2m-registry/3335.xml
new file mode 100644
index 0000000000..9f608653f5
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3335.xml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3336.xml b/application/src/main/data/lwm2m-registry/3336.xml
new file mode 100644
index 0000000000..68fea3b90e
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3336.xml
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3337.xml b/application/src/main/data/lwm2m-registry/3337.xml
new file mode 100644
index 0000000000..fa4eefbe81
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3337.xml
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3338.xml b/application/src/main/data/lwm2m-registry/3338.xml
new file mode 100644
index 0000000000..fd1fba3332
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3338.xml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3339.xml b/application/src/main/data/lwm2m-registry/3339.xml
new file mode 100644
index 0000000000..194685474e
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3339.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3340.xml b/application/src/main/data/lwm2m-registry/3340.xml
new file mode 100644
index 0000000000..35de944f3a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3340.xml
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3341.xml b/application/src/main/data/lwm2m-registry/3341.xml
new file mode 100644
index 0000000000..38e8fb8d9d
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3341.xml
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3342.xml b/application/src/main/data/lwm2m-registry/3342.xml
new file mode 100644
index 0000000000..cd27717ed4
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3342.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3343.xml b/application/src/main/data/lwm2m-registry/3343.xml
new file mode 100644
index 0000000000..3aa109923a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3343.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3344.xml b/application/src/main/data/lwm2m-registry/3344.xml
new file mode 100644
index 0000000000..2dd8648a19
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3344.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3345.xml b/application/src/main/data/lwm2m-registry/3345.xml
new file mode 100644
index 0000000000..bb234638a9
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3345.xml
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3346.xml b/application/src/main/data/lwm2m-registry/3346.xml
new file mode 100644
index 0000000000..d49322f32a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3346.xml
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3347.xml b/application/src/main/data/lwm2m-registry/3347.xml
new file mode 100644
index 0000000000..136e3445b9
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3347.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3348.xml b/application/src/main/data/lwm2m-registry/3348.xml
new file mode 100644
index 0000000000..9309b4b3dd
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3348.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3349.xml b/application/src/main/data/lwm2m-registry/3349.xml
new file mode 100644
index 0000000000..28029e7a6a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3349.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3350.xml b/application/src/main/data/lwm2m-registry/3350.xml
new file mode 100644
index 0000000000..f4f8477fdc
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3350.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3351.xml b/application/src/main/data/lwm2m-registry/3351.xml
new file mode 100644
index 0000000000..9219a765df
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3351.xml
@@ -0,0 +1,131 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3352.xml b/application/src/main/data/lwm2m-registry/3352.xml
new file mode 100644
index 0000000000..0e519c4951
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3352.xml
@@ -0,0 +1,124 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3353.xml b/application/src/main/data/lwm2m-registry/3353.xml
new file mode 100644
index 0000000000..1881a3b3bb
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3353.xml
@@ -0,0 +1,123 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3354.xml b/application/src/main/data/lwm2m-registry/3354.xml
new file mode 100644
index 0000000000..702be4cabc
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3354.xml
@@ -0,0 +1,134 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3355.xml b/application/src/main/data/lwm2m-registry/3355.xml
new file mode 100644
index 0000000000..fb56230fdf
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3355.xml
@@ -0,0 +1,156 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3356.xml b/application/src/main/data/lwm2m-registry/3356.xml
new file mode 100644
index 0000000000..95c7fc7835
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3356.xml
@@ -0,0 +1,120 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3357.xml b/application/src/main/data/lwm2m-registry/3357.xml
new file mode 100644
index 0000000000..935b7c4ba7
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3357.xml
@@ -0,0 +1,131 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3358.xml b/application/src/main/data/lwm2m-registry/3358.xml
new file mode 100644
index 0000000000..2784d6a990
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3358.xml
@@ -0,0 +1,106 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3359.xml b/application/src/main/data/lwm2m-registry/3359.xml
new file mode 100644
index 0000000000..91cb845e7a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3359.xml
@@ -0,0 +1,105 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3360.xml b/application/src/main/data/lwm2m-registry/3360.xml
new file mode 100644
index 0000000000..12b2302854
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3360.xml
@@ -0,0 +1,128 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3361.xml b/application/src/main/data/lwm2m-registry/3361.xml
new file mode 100644
index 0000000000..3d354bffac
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3361.xml
@@ -0,0 +1,130 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3362.xml b/application/src/main/data/lwm2m-registry/3362.xml
new file mode 100644
index 0000000000..3cb73eb79a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3362.xml
@@ -0,0 +1,97 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3363.xml b/application/src/main/data/lwm2m-registry/3363.xml
new file mode 100644
index 0000000000..3b44b91f26
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3363.xml
@@ -0,0 +1,100 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3364.xml b/application/src/main/data/lwm2m-registry/3364.xml
new file mode 100644
index 0000000000..0a042d12d2
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3364.xml
@@ -0,0 +1,97 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3365.xml b/application/src/main/data/lwm2m-registry/3365.xml
new file mode 100644
index 0000000000..83dbf0c807
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3365.xml
@@ -0,0 +1,123 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3366.xml b/application/src/main/data/lwm2m-registry/3366.xml
new file mode 100644
index 0000000000..5498af2a3d
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3366.xml
@@ -0,0 +1,176 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3367.xml b/application/src/main/data/lwm2m-registry/3367.xml
new file mode 100644
index 0000000000..1f35cf4cc5
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3367.xml
@@ -0,0 +1,138 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3368.xml b/application/src/main/data/lwm2m-registry/3368.xml
new file mode 100644
index 0000000000..40c17bfd37
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3368.xml
@@ -0,0 +1,100 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3369.xml b/application/src/main/data/lwm2m-registry/3369.xml
new file mode 100644
index 0000000000..b93bb86644
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3369.xml
@@ -0,0 +1,105 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3370.xml b/application/src/main/data/lwm2m-registry/3370.xml
new file mode 100644
index 0000000000..667ab0a281
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3370.xml
@@ -0,0 +1,141 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3371.xml b/application/src/main/data/lwm2m-registry/3371.xml
new file mode 100644
index 0000000000..fbc0860870
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3371.xml
@@ -0,0 +1,141 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3372.xml b/application/src/main/data/lwm2m-registry/3372.xml
new file mode 100644
index 0000000000..49d585a973
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3372.xml
@@ -0,0 +1,115 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3373.xml b/application/src/main/data/lwm2m-registry/3373.xml
new file mode 100644
index 0000000000..fb2af328b3
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3373.xml
@@ -0,0 +1,115 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3374.xml b/application/src/main/data/lwm2m-registry/3374.xml
new file mode 100644
index 0000000000..be7ecdc34e
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3374.xml
@@ -0,0 +1,132 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3375.xml b/application/src/main/data/lwm2m-registry/3375.xml
new file mode 100644
index 0000000000..75fb2a5a42
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3375.xml
@@ -0,0 +1,150 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3376.xml b/application/src/main/data/lwm2m-registry/3376.xml
new file mode 100644
index 0000000000..542f816da0
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3376.xml
@@ -0,0 +1,97 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3377.xml b/application/src/main/data/lwm2m-registry/3377.xml
new file mode 100644
index 0000000000..515eb36439
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3377.xml
@@ -0,0 +1,168 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3378.xml b/application/src/main/data/lwm2m-registry/3378.xml
new file mode 100644
index 0000000000..970d245a90
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3378.xml
@@ -0,0 +1,115 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3379.xml b/application/src/main/data/lwm2m-registry/3379.xml
new file mode 100644
index 0000000000..87137da530
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3379.xml
@@ -0,0 +1,123 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3380.xml b/application/src/main/data/lwm2m-registry/3380.xml
new file mode 100644
index 0000000000..8290651569
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3380.xml
@@ -0,0 +1,204 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3381.xml b/application/src/main/data/lwm2m-registry/3381.xml
new file mode 100644
index 0000000000..db2e4d52e8
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3381.xml
@@ -0,0 +1,110 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3382.xml b/application/src/main/data/lwm2m-registry/3382.xml
new file mode 100644
index 0000000000..8f066837c8
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3382.xml
@@ -0,0 +1,108 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3383.xml b/application/src/main/data/lwm2m-registry/3383.xml
new file mode 100644
index 0000000000..663ac45faa
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3383.xml
@@ -0,0 +1,99 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3384.xml b/application/src/main/data/lwm2m-registry/3384.xml
new file mode 100644
index 0000000000..a18c6b910b
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3384.xml
@@ -0,0 +1,112 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3385.xml b/application/src/main/data/lwm2m-registry/3385.xml
new file mode 100644
index 0000000000..cc0a99182a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3385.xml
@@ -0,0 +1,110 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3386.xml b/application/src/main/data/lwm2m-registry/3386.xml
new file mode 100644
index 0000000000..43937efadb
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3386.xml
@@ -0,0 +1,105 @@
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3387.xml b/application/src/main/data/lwm2m-registry/3387.xml
new file mode 100644
index 0000000000..c50ecb47bc
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3387.xml
@@ -0,0 +1,224 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3388.xml b/application/src/main/data/lwm2m-registry/3388.xml
new file mode 100644
index 0000000000..c1b5e54206
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3388.xml
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3389.xml b/application/src/main/data/lwm2m-registry/3389.xml
new file mode 100644
index 0000000000..7e3d0c7cdb
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3389.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3390.xml b/application/src/main/data/lwm2m-registry/3390.xml
new file mode 100644
index 0000000000..0e56d0a19a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3390.xml
@@ -0,0 +1,298 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3391.xml b/application/src/main/data/lwm2m-registry/3391.xml
new file mode 100644
index 0000000000..0b4ce12cd0
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3391.xml
@@ -0,0 +1,258 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3392.xml b/application/src/main/data/lwm2m-registry/3392.xml
new file mode 100644
index 0000000000..07b1b154e4
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3392.xml
@@ -0,0 +1,198 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3393.xml b/application/src/main/data/lwm2m-registry/3393.xml
new file mode 100644
index 0000000000..e3c883ac70
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3393.xml
@@ -0,0 +1,268 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3394.xml b/application/src/main/data/lwm2m-registry/3394.xml
new file mode 100644
index 0000000000..767100e145
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3394.xml
@@ -0,0 +1,177 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3395.xml b/application/src/main/data/lwm2m-registry/3395.xml
new file mode 100644
index 0000000000..29b4ce6b67
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3395.xml
@@ -0,0 +1,182 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3396.xml b/application/src/main/data/lwm2m-registry/3396.xml
new file mode 100644
index 0000000000..c3183f8e29
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3396.xml
@@ -0,0 +1,270 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3397.xml b/application/src/main/data/lwm2m-registry/3397.xml
new file mode 100644
index 0000000000..b8a3bed8d0
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3397.xml
@@ -0,0 +1,302 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3398.xml b/application/src/main/data/lwm2m-registry/3398.xml
new file mode 100644
index 0000000000..de3e24113d
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3398.xml
@@ -0,0 +1,107 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3399.xml b/application/src/main/data/lwm2m-registry/3399.xml
new file mode 100644
index 0000000000..8c29c4af82
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3399.xml
@@ -0,0 +1,238 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3400.xml b/application/src/main/data/lwm2m-registry/3400.xml
new file mode 100644
index 0000000000..aea142daba
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3400.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3401.xml b/application/src/main/data/lwm2m-registry/3401.xml
new file mode 100644
index 0000000000..1b088f1737
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3401.xml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3402.xml b/application/src/main/data/lwm2m-registry/3402.xml
new file mode 100644
index 0000000000..5cf405a66a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3402.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3403.xml b/application/src/main/data/lwm2m-registry/3403.xml
new file mode 100644
index 0000000000..f3690e5df5
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3403.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3404.xml b/application/src/main/data/lwm2m-registry/3404.xml
new file mode 100644
index 0000000000..24250ab608
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3404.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3405.xml b/application/src/main/data/lwm2m-registry/3405.xml
new file mode 100644
index 0000000000..f6939917ae
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3405.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3406.xml b/application/src/main/data/lwm2m-registry/3406.xml
new file mode 100644
index 0000000000..ccc7601eeb
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3406.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3407.xml b/application/src/main/data/lwm2m-registry/3407.xml
new file mode 100644
index 0000000000..7f29324566
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3407.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3408.xml b/application/src/main/data/lwm2m-registry/3408.xml
new file mode 100644
index 0000000000..4d5ff67eef
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3408.xml
@@ -0,0 +1,79 @@
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3410.xml b/application/src/main/data/lwm2m-registry/3410.xml
new file mode 100644
index 0000000000..63449e590b
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3410.xml
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3411.xml b/application/src/main/data/lwm2m-registry/3411.xml
new file mode 100644
index 0000000000..6339b847bd
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3411.xml
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3412.xml b/application/src/main/data/lwm2m-registry/3412.xml
new file mode 100644
index 0000000000..c3675639da
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3412.xml
@@ -0,0 +1,288 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3413.xml b/application/src/main/data/lwm2m-registry/3413.xml
new file mode 100644
index 0000000000..e699cb67dd
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3413.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3414.xml b/application/src/main/data/lwm2m-registry/3414.xml
new file mode 100644
index 0000000000..b3f7ad4567
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3414.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3415.xml b/application/src/main/data/lwm2m-registry/3415.xml
new file mode 100644
index 0000000000..5a76ca93a3
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3415.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3416.xml b/application/src/main/data/lwm2m-registry/3416.xml
new file mode 100644
index 0000000000..bf9f23b6ac
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3416.xml
@@ -0,0 +1,518 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3417.xml b/application/src/main/data/lwm2m-registry/3417.xml
new file mode 100644
index 0000000000..d22a318d84
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3417.xml
@@ -0,0 +1,188 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3418.xml b/application/src/main/data/lwm2m-registry/3418.xml
new file mode 100644
index 0000000000..6ff07dfa94
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3418.xml
@@ -0,0 +1,348 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3419.xml b/application/src/main/data/lwm2m-registry/3419.xml
new file mode 100644
index 0000000000..88b66119a7
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3419.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3420.xml b/application/src/main/data/lwm2m-registry/3420.xml
new file mode 100644
index 0000000000..835f4ec8a5
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3420.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3421.xml b/application/src/main/data/lwm2m-registry/3421.xml
new file mode 100644
index 0000000000..dadf8d8f47
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3421.xml
@@ -0,0 +1,618 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3423.xml b/application/src/main/data/lwm2m-registry/3423.xml
new file mode 100644
index 0000000000..6e0aaf3b21
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3423.xml
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3424.xml b/application/src/main/data/lwm2m-registry/3424.xml
new file mode 100644
index 0000000000..01df43cbaf
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3424.xml
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3425.xml b/application/src/main/data/lwm2m-registry/3425.xml
new file mode 100644
index 0000000000..dd94f14dfd
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3425.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3426.xml b/application/src/main/data/lwm2m-registry/3426.xml
new file mode 100644
index 0000000000..7f33c1bc1a
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3426.xml
@@ -0,0 +1,188 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3427.xml b/application/src/main/data/lwm2m-registry/3427.xml
new file mode 100644
index 0000000000..4ffe60c2bc
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3427.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3428.xml b/application/src/main/data/lwm2m-registry/3428.xml
new file mode 100644
index 0000000000..68dab08269
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3428.xml
@@ -0,0 +1,268 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3429.xml b/application/src/main/data/lwm2m-registry/3429.xml
new file mode 100644
index 0000000000..e5f6f46b75
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3429.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3430.xml b/application/src/main/data/lwm2m-registry/3430.xml
new file mode 100644
index 0000000000..76a14c0656
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3430.xml
@@ -0,0 +1,371 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3431.xml b/application/src/main/data/lwm2m-registry/3431.xml
new file mode 100644
index 0000000000..8f0ec363d1
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3431.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3432.xml b/application/src/main/data/lwm2m-registry/3432.xml
new file mode 100644
index 0000000000..4816248320
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3432.xml
@@ -0,0 +1,218 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3433.xml b/application/src/main/data/lwm2m-registry/3433.xml
new file mode 100644
index 0000000000..018f2af954
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3433.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3434.xml b/application/src/main/data/lwm2m-registry/3434.xml
new file mode 100644
index 0000000000..b06852cbe1
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3434.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3435.xml b/application/src/main/data/lwm2m-registry/3435.xml
new file mode 100644
index 0000000000..1694afc32b
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3435.xml
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3436.xml b/application/src/main/data/lwm2m-registry/3436.xml
new file mode 100644
index 0000000000..a450edf280
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3436.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3437.xml b/application/src/main/data/lwm2m-registry/3437.xml
new file mode 100644
index 0000000000..284911326e
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3437.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3438.xml b/application/src/main/data/lwm2m-registry/3438.xml
new file mode 100644
index 0000000000..2e2405c161
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3438.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3439.xml b/application/src/main/data/lwm2m-registry/3439.xml
new file mode 100644
index 0000000000..b702866ce0
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3439.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3441.xml b/application/src/main/data/lwm2m-registry/3441.xml
new file mode 100644
index 0000000000..bdd5efca6c
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3441.xml
@@ -0,0 +1,262 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/3442.xml b/application/src/main/data/lwm2m-registry/3442.xml
new file mode 100644
index 0000000000..b4607cead7
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/3442.xml
@@ -0,0 +1,332 @@
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/4.xml b/application/src/main/data/lwm2m-registry/4.xml
new file mode 100644
index 0000000000..dc3f100cc4
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/4.xml
@@ -0,0 +1,258 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/5.xml b/application/src/main/data/lwm2m-registry/5.xml
new file mode 100644
index 0000000000..02188533b2
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/5.xml
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/500.xml b/application/src/main/data/lwm2m-registry/500.xml
new file mode 100644
index 0000000000..df21a5aa80
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/500.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/501.xml b/application/src/main/data/lwm2m-registry/501.xml
new file mode 100644
index 0000000000..402cfc0b78
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/501.xml
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/502.xml b/application/src/main/data/lwm2m-registry/502.xml
new file mode 100644
index 0000000000..5b85aa4515
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/502.xml
@@ -0,0 +1,214 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/503.xml b/application/src/main/data/lwm2m-registry/503.xml
new file mode 100644
index 0000000000..6c6e627a94
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/503.xml
@@ -0,0 +1,434 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/504.xml b/application/src/main/data/lwm2m-registry/504.xml
new file mode 100644
index 0000000000..25c033f59d
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/504.xml
@@ -0,0 +1,341 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/505.xml b/application/src/main/data/lwm2m-registry/505.xml
new file mode 100644
index 0000000000..a89c59ca41
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/505.xml
@@ -0,0 +1,262 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/507.xml b/application/src/main/data/lwm2m-registry/507.xml
new file mode 100644
index 0000000000..650a0e8904
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/507.xml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/508.xml b/application/src/main/data/lwm2m-registry/508.xml
new file mode 100644
index 0000000000..7f45059dd5
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/508.xml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/509.xml b/application/src/main/data/lwm2m-registry/509.xml
new file mode 100644
index 0000000000..17ff50250f
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/509.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/510.xml b/application/src/main/data/lwm2m-registry/510.xml
new file mode 100644
index 0000000000..302377bbff
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/510.xml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/6.xml b/application/src/main/data/lwm2m-registry/6.xml
new file mode 100644
index 0000000000..8b1b3da625
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/6.xml
@@ -0,0 +1,143 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/7.xml b/application/src/main/data/lwm2m-registry/7.xml
new file mode 100644
index 0000000000..451780da1f
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/7.xml
@@ -0,0 +1,164 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/8.xml b/application/src/main/data/lwm2m-registry/8.xml
new file mode 100644
index 0000000000..a9296999f5
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/8.xml
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/lwm2m-registry/9.xml b/application/src/main/data/lwm2m-registry/9.xml
new file mode 100644
index 0000000000..4186b1248d
--- /dev/null
+++ b/application/src/main/data/lwm2m-registry/9.xml
@@ -0,0 +1,321 @@
+
+
+
+
+
+
+
diff --git a/application/src/main/data/upgrade/3.4.4/schema_update.sql b/application/src/main/data/upgrade/3.4.4/schema_update.sql
index 91c49bf905..6ac33b255e 100644
--- a/application/src/main/data/upgrade/3.4.4/schema_update.sql
+++ b/application/src/main/data/upgrade/3.4.4/schema_update.sql
@@ -72,8 +72,6 @@ WHERE cleared = FALSE
ORDER BY l.created_time DESC, l.id
LIMIT 1);
-VACUUM FULL ANALYZE alarm;
-
-- ALARM STATUS REFACTORING END
-- ALARM COMMENTS START
diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
index 532fc5090c..9ec8020544 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
@@ -95,7 +95,7 @@ import org.thingsboard.server.service.executors.ExternalCallExecutorService;
import org.thingsboard.server.service.executors.NotificationExecutorService;
import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
import org.thingsboard.server.service.mail.MailExecutorService;
-import org.thingsboard.server.service.notification.rule.NotificationRuleProcessingService;
+import org.thingsboard.server.dao.notification.NotificationRuleProcessingService;
import org.thingsboard.server.service.profile.TbAssetProfileCache;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java
index f23acbc0db..d330dfceec 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java
@@ -16,12 +16,15 @@
package org.thingsboard.server.actors.ruleChain;
import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.TbRuleNodeUpdateException;
import org.thingsboard.server.actors.service.ComponentActor;
import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.common.msg.TbActorStopReason;
+import org.thingsboard.server.dao.notification.trigger.RuleEngineComponentLifecycleEventTrigger;
public abstract class RuleEngineComponentActor> extends ComponentActor {
@@ -32,8 +35,29 @@ public abstract class RuleEngineComponentActor getAlarmComments(
- @ApiParam(value = ALARM_ID_PARAM_DESCRIPTION)
+ @ApiParam(value = ALARM_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(ALARM_ID) String strAlarmId,
@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@@ -117,10 +117,7 @@ public class AlarmCommentController extends BaseController {
) throws Exception {
checkParameter(ALARM_ID, strAlarmId);
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
- Alarm alarm = alarmService.findAlarmByIdAsync(getCurrentUser().getTenantId(), alarmId).get();
- checkNotNull(alarm, "Alarm with id [" + alarmId + "] is not found");
- checkEntityId(alarm.getOriginator(), Operation.READ);
-
+ Alarm alarm = checkAlarmId(alarmId, Operation.READ);
PageLink pageLink = createPageLink(pageSize, page, null, sortProperty, sortOrder);
return checkNotNull(alarmCommentService.findAlarmComments(alarm.getTenantId(), alarmId, pageLink));
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
index 53734b8633..a060335713 100644
--- a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
+++ b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
@@ -104,7 +104,7 @@ public class ControllerConstants {
protected static final String ASSET_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, description, isDefault";
protected static final String ASSET_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, label, customerTitle";
protected static final String ALARM_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, startTs, endTs, type, ackTs, clearTs, severity, status";
- protected static final String ALARM_COMMENT_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime";
+ protected static final String ALARM_COMMENT_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, id";
protected static final String EVENT_SORT_PROPERTY_ALLOWABLE_VALUES = "ts, id";
protected static final String EDGE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, label, customerTitle";
protected static final String RULE_CHAIN_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, root";
diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java
index e4e37a7788..24daab29f0 100644
--- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java
@@ -31,6 +31,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.rule.engine.api.NotificationCenter;
import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.NotificationId;
import org.thingsboard.server.common.data.id.NotificationRequestId;
@@ -38,7 +39,6 @@ import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.Notification;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
-import org.thingsboard.server.common.data.notification.NotificationProcessingContext;
import org.thingsboard.server.common.data.notification.NotificationRequest;
import org.thingsboard.server.common.data.notification.NotificationRequestInfo;
import org.thingsboard.server.common.data.notification.NotificationRequestPreview;
@@ -56,14 +56,18 @@ import org.thingsboard.server.dao.notification.NotificationSettingsService;
import org.thingsboard.server.dao.notification.NotificationTargetService;
import org.thingsboard.server.dao.notification.NotificationTemplateService;
import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.notification.NotificationProcessingContext;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
+import org.thingsboard.server.service.security.permission.Resource;
import javax.validation.Valid;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -196,6 +200,7 @@ public class NotificationController extends BaseController {
@PostMapping("/notification/request/preview")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public NotificationRequestPreview getNotificationRequestPreview(@RequestBody @Valid NotificationRequest request,
+ @RequestParam(defaultValue = "20") int recipientsPreviewSize,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
NotificationRequestPreview preview = new NotificationRequestPreview();
@@ -229,18 +234,29 @@ public class NotificationController extends BaseController {
preview.setProcessedTemplates(processedTemplates);
// generic permission
+ Set recipientsPreview = new LinkedHashSet<>();
Map recipientsCountByTarget = new HashMap<>();
List targets = notificationTargetService.findNotificationTargetsByTenantIdAndIds(user.getTenantId(),
request.getTargets().stream().map(NotificationTargetId::new).collect(Collectors.toList()));
for (NotificationTarget target : targets) {
int recipientsCount;
if (target.getConfiguration().getType() == NotificationTargetType.PLATFORM_USERS) {
- recipientsCount = notificationTargetService.countRecipientsForNotificationTargetConfig(user.getTenantId(), target.getConfiguration());
+ PageData recipients = notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), null,
+ target.getConfiguration(), new PageLink(recipientsPreviewSize));
+ recipientsCount = (int) recipients.getTotalElements();
+ for (User recipient : recipients.getData()) {
+ if (recipientsPreview.size() < recipientsPreviewSize) {
+ recipientsPreview.add(recipient);
+ } else {
+ break;
+ }
+ }
} else {
recipientsCount = 1;
}
recipientsCountByTarget.put(target.getName(), recipientsCount);
}
+ preview.setRecipientsPreview(recipientsPreview);
preview.setRecipientsCountByTarget(recipientsCountByTarget);
preview.setTotalRecipientsCount(recipientsCountByTarget.values().stream().mapToInt(Integer::intValue).sum());
@@ -279,8 +295,8 @@ public class NotificationController extends BaseController {
@PostMapping("/notification/settings")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public NotificationSettings saveNotificationSettings(@RequestBody @Valid NotificationSettings notificationSettings,
- @AuthenticationPrincipal SecurityUser user) {
- // generic permission
+ @AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
+ accessControlService.checkPermission(user, Resource.ADMIN_SETTINGS, Operation.WRITE);
TenantId tenantId = user.isSystemAdmin() ? TenantId.SYS_TENANT_ID : user.getTenantId();
notificationSettingsService.saveNotificationSettings(tenantId, notificationSettings);
return notificationSettings;
@@ -288,10 +304,17 @@ public class NotificationController extends BaseController {
@GetMapping("/notification/settings")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
- public NotificationSettings getNotificationSettings(@AuthenticationPrincipal SecurityUser user) {
- // generic permission
+ public NotificationSettings getNotificationSettings(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
+ accessControlService.checkPermission(user, Resource.ADMIN_SETTINGS, Operation.READ);
TenantId tenantId = user.isSystemAdmin() ? TenantId.SYS_TENANT_ID : user.getTenantId();
return notificationSettingsService.findNotificationSettings(tenantId);
}
+ @GetMapping("/notification/deliveryMethods")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
+ public Set getAvailableDeliveryMethods(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
+ accessControlService.checkPermission(user, Resource.ADMIN_SETTINGS, Operation.READ);
+ return notificationCenter.getAvailableDeliveryMethods(user.getTenantId());
+ }
+
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java
index 4f4380c2df..7f7cc5afd1 100644
--- a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java
@@ -18,6 +18,8 @@ package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -34,14 +36,15 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.id.UserId;
+import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetType;
import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter;
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
+import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter;
import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter;
import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter;
-import org.thingsboard.server.common.data.notification.targets.platform.UsersFilterType;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.notification.NotificationTargetService;
@@ -141,6 +144,19 @@ public class NotificationTargetController extends BaseController {
return notificationTargetService.findNotificationTargetsByTenantId(user.getTenantId(), pageLink);
}
+ @GetMapping(value = "/targets", params = "notificationType")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
+ public PageData getNotificationTargetsBySupportedNotificationType(@RequestParam int pageSize,
+ @RequestParam int page,
+ @RequestParam(required = false) String textSearch,
+ @RequestParam(required = false) String sortProperty,
+ @RequestParam(required = false) String sortOrder,
+ @RequestParam(required = false) NotificationType notificationType,
+ @AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
+ PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
+ return notificationTargetService.findNotificationTargetsByTenantIdAndSupportedNotificationType(user.getTenantId(), notificationType, pageLink);
+ }
+
@ApiOperation(value = "Delete notification target by id (deleteNotificationTargetById)",
notes = "Delete notification target by its id.\n\n" +
"This target cannot be referenced by existing scheduled notification requests or any notification rules." +
@@ -159,13 +175,22 @@ public class NotificationTargetController extends BaseController {
}
// generic permission for users
UsersFilter usersFilter = ((PlatformUsersNotificationTargetConfig) targetConfig).getUsersFilter();
- if (usersFilter.getType() == UsersFilterType.USER_LIST) {
- for (UUID recipientId : ((UserListFilter) usersFilter).getUsersIds()) {
- checkUserId(new UserId(recipientId), Operation.READ);
- }
- } else if (usersFilter.getType() == UsersFilterType.CUSTOMER_USERS) {
- CustomerId customerId = new CustomerId(((CustomerUsersFilter) usersFilter).getCustomerId());
- checkEntityId(customerId, Operation.READ);
+ switch (usersFilter.getType()) {
+ case USER_LIST:
+ for (UUID recipientId : ((UserListFilter) usersFilter).getUsersIds()) {
+ checkUserId(new UserId(recipientId), Operation.READ);
+ }
+ break;
+ case CUSTOMER_USERS:
+ CustomerId customerId = new CustomerId(((CustomerUsersFilter) usersFilter).getCustomerId());
+ checkEntityId(customerId, Operation.READ);
+ break;
+ case TENANT_ADMINISTRATORS:
+ if (CollectionUtils.isNotEmpty(((TenantAdministratorsFilter) usersFilter).getTenantsIds()) ||
+ CollectionUtils.isNotEmpty(((TenantAdministratorsFilter) usersFilter).getTenantProfilesIds())) {
+ throw new AccessDeniedException("");
+ }
+ break;
}
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java
index 6c46fc0fdf..a28842a6ee 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java
@@ -21,6 +21,8 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -32,14 +34,19 @@ import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.EntityInfo;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.tenant.profile.TbTenantProfileService;
+import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
+import java.util.List;
+import java.util.UUID;
+
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_END;
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_START;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
@@ -268,4 +275,12 @@ public class TenantProfileController extends BaseController {
throw handleException(e);
}
}
+
+ @GetMapping(value = "/tenantProfiles", params = {"ids"})
+ @PreAuthorize("hasAuthority('SYS_ADMIN')")
+ public List getTenantProfilesByIds(@RequestParam("ids") UUID[] ids) {
+ return tenantProfileService.findTenantProfilesByIds(TenantId.SYS_TENANT_ID, ids);
+ }
+
+
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java
index e57bb73262..06963e466f 100644
--- a/application/src/main/java/org/thingsboard/server/controller/UserController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java
@@ -19,7 +19,6 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
-import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -58,9 +57,9 @@ import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.common.data.security.UserSettings;
import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent;
+import org.thingsboard.server.common.data.security.model.JwtPair;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.user.TbUserService;
-import org.thingsboard.server.common.data.security.model.JwtPair;
import org.thingsboard.server.service.query.EntityQueryService;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.UserPrincipal;
@@ -70,7 +69,6 @@ import org.thingsboard.server.service.security.permission.Resource;
import org.thingsboard.server.service.security.system.SystemSecurityService;
import javax.servlet.http.HttpServletRequest;
-
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -109,7 +107,6 @@ public class UserController extends BaseController {
public static final String ACTIVATE_URL_PATTERN = "%s/api/noauth/activate?activateToken=%s";
@Value("${security.user_token_access_enabled}")
- @Getter
private boolean userTokenAccessEnabled;
private final MailService mailService;
diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
index a22b8854bb..0531f3e7e9 100644
--- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
+++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
@@ -26,6 +26,7 @@ import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.install.DatabaseEntitiesUpgradeService;
import org.thingsboard.server.service.install.DatabaseTsUpgradeService;
import org.thingsboard.server.service.install.EntityDatabaseSchemaService;
+import org.thingsboard.server.service.install.InstallScripts;
import org.thingsboard.server.service.install.NoSqlKeyspaceService;
import org.thingsboard.server.service.install.SystemDataLoaderService;
import org.thingsboard.server.service.install.TsDatabaseSchemaService;
@@ -34,6 +35,9 @@ import org.thingsboard.server.service.install.migrate.EntitiesMigrateService;
import org.thingsboard.server.service.install.migrate.TsLatestMigrateService;
import org.thingsboard.server.service.install.update.CacheCleanupService;
import org.thingsboard.server.service.install.update.DataUpdateService;
+import org.thingsboard.server.service.install.update.DefaultDataUpdateService;
+
+import static org.thingsboard.server.service.install.update.DefaultDataUpdateService.getEnv;
@Service
@Profile("install")
@@ -88,6 +92,9 @@ public class ThingsboardInstallService {
@Autowired(required = false)
private TsLatestMigrateService latestMigrateService;
+ @Autowired
+ private InstallScripts installScripts;
+
public void performInstall() {
try {
if (isUpgrade) {
@@ -242,7 +249,12 @@ public class ThingsboardInstallService {
databaseEntitiesUpgradeService.upgradeDatabase("3.4.4");
log.info("Updating system data...");
systemDataLoaderService.updateSystemWidgets();
- systemDataLoaderService.createDefaultNotificationConfigs();
+ if (!getEnv("SKIP_DEFAULT_NOTIFICATION_CONFIGS_CREATION", false)) {
+ systemDataLoaderService.createDefaultNotificationConfigs();
+ } else {
+ log.info("Skipping default notification configs creation");
+ }
+ installScripts.loadSystemLwm2mResources();
break;
//TODO update CacheCleanupService on the next version upgrade
default:
@@ -285,6 +297,7 @@ public class ThingsboardInstallService {
systemDataLoaderService.createQueues();
// systemDataLoaderService.loadSystemPlugins();
// systemDataLoaderService.loadSystemRules();
+ installScripts.loadSystemLwm2mResources();
if (loadDemo) {
log.info("Loading demo data...");
@@ -303,3 +316,4 @@ public class ThingsboardInstallService {
}
}
+
diff --git a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java
index 00bbf3b9e5..2eedd18d00 100644
--- a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java
+++ b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java
@@ -187,6 +187,7 @@ public class EntityActionService {
entityNode.put("configuration", "");
}
metaData.putValue("entityName", entity.getName());
+ metaData.putValue("entityType", entityId.getEntityType().toString());
} else {
entityNode = json.createObjectNode();
if (actionType == ActionType.ATTRIBUTES_UPDATED) {
diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
index 5ba092937f..f845634aa0 100644
--- a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
+++ b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.apiusage;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.ListenableFuture;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
@@ -52,6 +53,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.common.msg.tools.SchedulerUtils;
+import org.thingsboard.server.dao.notification.NotificationRuleProcessingService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
@@ -86,6 +88,7 @@ import java.util.stream.Collectors;
@Slf4j
@Service
+@RequiredArgsConstructor
public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService implements TbApiUsageStateService {
public static final String HOURLY = "Hourly";
@@ -105,6 +108,7 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
private final ApiUsageStateService apiUsageStateService;
private final TbTenantProfileCache tenantProfileCache;
private final MailService mailService;
+ private final NotificationRuleProcessingService notificationRuleProcessingService;
private final DbCallbackExecutorService dbExecutor;
@Lazy
@@ -126,26 +130,7 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
private final Lock updateLock = new ReentrantLock();
- private final ExecutorService mailExecutor;
-
- public DefaultTbApiUsageStateService(TbClusterService clusterService,
- PartitionService partitionService,
- TenantService tenantService,
- TimeseriesService tsService,
- ApiUsageStateService apiUsageStateService,
- TbTenantProfileCache tenantProfileCache,
- MailService mailService,
- DbCallbackExecutorService dbExecutor) {
- this.clusterService = clusterService;
- this.partitionService = partitionService;
- this.tenantService = tenantService;
- this.tsService = tsService;
- this.apiUsageStateService = apiUsageStateService;
- this.tenantProfileCache = tenantProfileCache;
- this.mailService = mailService;
- this.mailExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("api-usage-svc-mail"));
- this.dbExecutor = dbExecutor;
- }
+ private final ExecutorService mailExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("api-usage-svc-mail"));
@PostConstruct
public void init() {
@@ -355,6 +340,7 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK);
if (state.getEntityType() == EntityType.TENANT && !state.getEntityId().equals(TenantId.SYS_TENANT_ID)) {
+
String email = tenantService.findTenantById(state.getTenantId()).getEmail();
if (StringUtils.isNotEmpty(email)) {
result.forEach((apiFeature, stateValue) -> {
diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultRateLimitService.java b/application/src/main/java/org/thingsboard/server/service/apiusage/limits/DefaultRateLimitService.java
similarity index 66%
rename from application/src/main/java/org/thingsboard/server/service/apiusage/DefaultRateLimitService.java
rename to application/src/main/java/org/thingsboard/server/service/apiusage/limits/DefaultRateLimitService.java
index dd3f4c5150..b75fb99c02 100644
--- a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultRateLimitService.java
+++ b/application/src/main/java/org/thingsboard/server/service/apiusage/limits/DefaultRateLimitService.java
@@ -13,19 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.service.apiusage;
+package org.thingsboard.server.service.apiusage.limits;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.msg.tools.TbRateLimits;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
@Service
@RequiredArgsConstructor
@@ -33,28 +31,19 @@ public class DefaultRateLimitService implements RateLimitService {
private final TbTenantProfileCache tenantProfileCache;
- private final Map> rateLimits = new ConcurrentHashMap<>();
+ private final Map> rateLimits = new ConcurrentHashMap<>();
@Override
- public boolean checkEntityExportLimit(TenantId tenantId) {
- return checkLimit(tenantId, "entityExport", DefaultTenantProfileConfiguration::getTenantEntityExportRateLimit);
- }
-
- @Override
- public boolean checkEntityImportLimit(TenantId tenantId) {
- return checkLimit(tenantId, "entityImport", DefaultTenantProfileConfiguration::getTenantEntityImportRateLimit);
- }
-
- private boolean checkLimit(TenantId tenantId, String rateLimitsKey, Function rateLimitConfigExtractor) {
+ public boolean checkRateLimit(TenantId tenantId, LimitedApi api) {
String rateLimitConfig = tenantProfileCache.get(tenantId).getProfileConfiguration()
- .map(rateLimitConfigExtractor).orElse(null);
+ .map(api::getLimitConfig).orElse(null);
- Map rateLimits = this.rateLimits.get(rateLimitsKey);
+ Map rateLimits = this.rateLimits.get(api);
if (StringUtils.isEmpty(rateLimitConfig)) {
if (rateLimits != null) {
rateLimits.remove(tenantId);
if (rateLimits.isEmpty()) {
- this.rateLimits.remove(rateLimitsKey);
+ this.rateLimits.remove(api);
}
}
return true;
@@ -62,7 +51,7 @@ public class DefaultRateLimitService implements RateLimitService {
if (rateLimits == null) {
rateLimits = new ConcurrentHashMap<>();
- this.rateLimits.put(rateLimitsKey, rateLimits);
+ this.rateLimits.put(api, rateLimits);
}
TbRateLimits rateLimit = rateLimits.get(tenantId);
if (rateLimit == null || !rateLimit.getConfiguration().equals(rateLimitConfig)) {
diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/limits/LimitedApi.java b/application/src/main/java/org/thingsboard/server/service/apiusage/limits/LimitedApi.java
new file mode 100644
index 0000000000..f69ece6661
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/apiusage/limits/LimitedApi.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright © 2016-2023 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.apiusage.limits;
+
+import lombok.RequiredArgsConstructor;
+import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
+
+import java.util.function.Function;
+
+@RequiredArgsConstructor
+public enum LimitedApi {
+
+ ENTITY_EXPORT(DefaultTenantProfileConfiguration::getTenantEntityExportRateLimit),
+ ENTITY_IMPORT(DefaultTenantProfileConfiguration::getTenantEntityImportRateLimit),
+ NOTIFICATION_REQUEST(DefaultTenantProfileConfiguration::getTenantNotificationRequestsRateLimit);
+
+ private final Function configExtractor;
+
+ public String getLimitConfig(DefaultTenantProfileConfiguration profileConfiguration) {
+ return configExtractor.apply(profileConfiguration);
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/RateLimitService.java b/application/src/main/java/org/thingsboard/server/service/apiusage/limits/RateLimitService.java
similarity index 81%
rename from application/src/main/java/org/thingsboard/server/service/apiusage/RateLimitService.java
rename to application/src/main/java/org/thingsboard/server/service/apiusage/limits/RateLimitService.java
index af5debac2e..c984ec8fa5 100644
--- a/application/src/main/java/org/thingsboard/server/service/apiusage/RateLimitService.java
+++ b/application/src/main/java/org/thingsboard/server/service/apiusage/limits/RateLimitService.java
@@ -13,14 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.service.apiusage;
+package org.thingsboard.server.service.apiusage.limits;
import org.thingsboard.server.common.data.id.TenantId;
public interface RateLimitService {
- boolean checkEntityExportLimit(TenantId tenantId);
-
- boolean checkEntityImportLimit(TenantId tenantId);
+ boolean checkRateLimit(TenantId tenantId, LimitedApi api);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java
index 26c38188fd..6f86632ffd 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java
@@ -18,11 +18,14 @@ package org.thingsboard.server.service.entitiy.alarm;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmComment;
+import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.audit.ActionType;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.dao.alarm.AlarmCommentService;
@@ -52,8 +55,17 @@ public class DefaultTbAlarmCommentService extends AbstractTbEntityService implem
}
@Override
- public void deleteAlarmComment(Alarm alarm, AlarmComment alarmComment, User user) {
- alarmCommentService.deleteAlarmComment(alarm.getTenantId(), alarmComment.getId());
- notificationEntityService.notifyAlarmComment(alarm, alarmComment, ActionType.DELETED_COMMENT, user);
+ public void deleteAlarmComment(Alarm alarm, AlarmComment alarmComment, User user) throws ThingsboardException {
+ if (alarmComment.getType() == AlarmCommentType.OTHER) {
+ alarmComment.setType(AlarmCommentType.SYSTEM);
+ alarmComment.setUserId(null);
+ alarmComment.setComment(JacksonUtil.newObjectNode().put("text",
+ String.format("User %s deleted his comment",
+ (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())));
+ AlarmComment savedAlarmComment = checkNotNull(alarmCommentService.saveAlarmComment(alarm.getTenantId(), alarmComment));
+ notificationEntityService.notifyAlarmComment(alarm, savedAlarmComment, ActionType.DELETED_COMMENT, user);
+ } else {
+ throw new ThingsboardException("System comment could not be deleted", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
+ }
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmCommentService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmCommentService.java
index 8c6aa3366a..a2bca133cb 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmCommentService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmCommentService.java
@@ -23,5 +23,5 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
public interface TbAlarmCommentService {
AlarmComment saveAlarmComment(Alarm alarm, AlarmComment alarmComment, User user) throws ThingsboardException;
- void deleteAlarmComment(Alarm alarm, AlarmComment alarmComment, User user);
+ void deleteAlarmComment(Alarm alarm, AlarmComment alarmComment, User user) throws ThingsboardException;
}
diff --git a/application/src/main/java/org/thingsboard/server/service/executors/NotificationExecutorService.java b/application/src/main/java/org/thingsboard/server/service/executors/NotificationExecutorService.java
index 53d55fdad6..81ad36a4a1 100644
--- a/application/src/main/java/org/thingsboard/server/service/executors/NotificationExecutorService.java
+++ b/application/src/main/java/org/thingsboard/server/service/executors/NotificationExecutorService.java
@@ -22,7 +22,7 @@ import org.thingsboard.common.util.AbstractListeningExecutor;
@Component
public class NotificationExecutorService extends AbstractListeningExecutor {
- @Value("${notification_system.thread_pool_size:30}")
+ @Value("${notification_system.thread_pool_size:10}")
private int threadPoolSize;
@Override
diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
index bdbf584a3e..a182a4d83f 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
@@ -22,6 +22,7 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
@@ -678,13 +679,27 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
@Override
public void createDefaultNotificationConfigs() {
- notificationSettingsService.createDefaultNotificationConfigs(TenantId.SYS_TENANT_ID);
+ try {
+ log.info("Creating default notification configs for system admin");
+ notificationSettingsService.createDefaultNotificationConfigs(TenantId.SYS_TENANT_ID);
+ } catch (Exception e) {
+ if (StringUtils.contains(e.getMessage(), "already exists")) {
+ log.info("Default notification configs are already present for system admin, skipping");
+ } else {
+ throw e;
+ }
+ }
PageDataIterable tenants = new PageDataIterable<>(tenantService::findTenantsIds, 500);
+ log.info("Creating default notification configs for all tenants");
for (TenantId tenantId : tenants) {
try {
notificationSettingsService.createDefaultNotificationConfigs(tenantId);
} catch (Exception e) {
- log.warn("Failed to create default notification configs for tenant {}: {}", tenantId, e.getMessage());
+ if (StringUtils.contains(e.getMessage(), "already exists")) {
+ log.info("Default notification configs are already present for tenant {}, skipping", tenantId);
+ } else {
+ throw e;
+ }
}
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
index 7f63ed5310..0ccb201cb9 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
@@ -21,7 +21,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Dashboard;
+import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.StringUtils;
+import org.thingsboard.server.common.data.TbResource;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate;
@@ -30,6 +33,7 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.dashboard.DashboardService;
+import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.rule.RuleChainService;
@@ -41,9 +45,11 @@ import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Base64;
import java.util.Optional;
import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper;
+import static org.thingsboard.server.utils.LwM2mObjectModelUtils.toLwm2mResource;
/**
* Created by ashvayka on 18.04.18.
@@ -65,7 +71,7 @@ public class InstallScripts {
public static final String WIDGET_BUNDLES_DIR = "widget_bundles";
public static final String OAUTH2_CONFIG_TEMPLATES_DIR = "oauth2_config_templates";
public static final String DASHBOARDS_DIR = "dashboards";
- public static final String MODELS_DIR = "models";
+ public static final String MODELS_LWM2M_DIR = "lwm2m-registry";
public static final String CREDENTIALS_DIR = "credentials";
public static final String EDGE_MANAGEMENT = "edge_management";
@@ -260,4 +266,45 @@ public class InstallScripts {
);
}
}
+
+ public void loadSystemLwm2mResources() {
+ Path resourceLwm2mPath = Paths.get(dataDir, MODELS_LWM2M_DIR);
+ try (DirectoryStream dirStream = Files.newDirectoryStream(resourceLwm2mPath, path -> path.toString().endsWith(InstallScripts.XML_EXT))) {
+ dirStream.forEach(
+ path -> {
+ try {
+ String data = Base64.getEncoder().encodeToString(Files.readAllBytes(path));
+ TbResource tbResource = new TbResource();
+ tbResource.setTenantId(TenantId.SYS_TENANT_ID);
+ tbResource.setData(data);
+ tbResource.setResourceType(ResourceType.LWM2M_MODEL);
+ tbResource.setFileName(path.toFile().getName());
+ doSaveLwm2mResource(tbResource);
+ } catch (Exception e) {
+ log.error("Unable to load resource lwm2m object model from file: [{}]", path.toString());
+ throw new RuntimeException("resource lwm2m object model from file", e);
+ }
+ }
+ );
+ } catch (Exception e) {
+ log.error("Unable to load resources lwm2m object model from file: [{}]", resourceLwm2mPath.toString());
+ throw new RuntimeException("resource lwm2m object model from file", e);
+ }
+ }
+
+ private void doSaveLwm2mResource(TbResource resource) throws ThingsboardException {
+ try {
+ log.trace("Executing saveResource [{}]", resource);
+ if (StringUtils.isEmpty(resource.getData())) {
+ throw new DataValidationException("Resource data should be specified!");
+ }
+ toLwm2mResource(resource);
+ resourceService.saveResource(resource);
+ } catch (DataValidationException e) {
+ log.debug("[{}] {}", resource.getFileName(), e.getMessage());
+ } catch (Exception ex) {
+ throw ex;
+ }
+ }
}
+
diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
index b9ff5383b5..f5f212beb2 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
@@ -684,6 +684,11 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.4.4", SCHEMA_UPDATE_SQL);
loadSql(schemaUpdateFile, conn);
+ try {
+ conn.createStatement().execute("VACUUM FULL ANALYZE alarm;"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
+ } catch (Exception e) {
+ }
+
try {
conn.createStatement().execute("ALTER TABLE asset_profile ADD COLUMN default_edge_rule_chain_id uuid"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
} catch (Exception e) {
diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
index 43d6a4df0a..13943d5698 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
@@ -672,7 +672,7 @@ public class DefaultDataUpdateService implements DataUpdateService {
return mainQueueConfiguration;
}
- private boolean getEnv(String name, boolean defaultValue) {
+ public static boolean getEnv(String name, boolean defaultValue) {
String env = System.getenv(name);
if (env == null) {
return defaultValue;
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java
index 81c0fbf6a5..619616bc8b 100644
--- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java
@@ -22,7 +22,10 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.DonAsynchron;
+import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.rule.engine.api.NotificationCenter;
+import org.thingsboard.rule.engine.api.SmsService;
+import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.NotificationId;
import org.thingsboard.server.common.data.id.NotificationRequestId;
@@ -32,16 +35,18 @@ import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.AlreadySentException;
import org.thingsboard.server.common.data.notification.Notification;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
-import org.thingsboard.server.common.data.notification.NotificationProcessingContext;
import org.thingsboard.server.common.data.notification.NotificationRequest;
import org.thingsboard.server.common.data.notification.NotificationRequestConfig;
import org.thingsboard.server.common.data.notification.NotificationRequestStats;
import org.thingsboard.server.common.data.notification.NotificationRequestStatus;
import org.thingsboard.server.common.data.notification.NotificationStatus;
import org.thingsboard.server.common.data.notification.NotificationType;
+import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
+import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
+import org.thingsboard.server.common.data.notification.targets.platform.UsersFilterType;
import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
@@ -51,15 +56,19 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
+import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
import org.thingsboard.server.dao.notification.NotificationRequestService;
import org.thingsboard.server.dao.notification.NotificationService;
import org.thingsboard.server.dao.notification.NotificationSettingsService;
import org.thingsboard.server.dao.notification.NotificationTargetService;
import org.thingsboard.server.dao.notification.NotificationTemplateService;
+import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.discovery.NotificationsTopicService;
import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
+import org.thingsboard.server.service.apiusage.limits.LimitedApi;
+import org.thingsboard.server.service.apiusage.limits.RateLimitService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.executors.NotificationExecutorService;
import org.thingsboard.server.service.notification.channels.NotificationChannel;
@@ -75,7 +84,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@Service
@@ -89,16 +97,23 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
private final NotificationService notificationService;
private final NotificationTemplateService notificationTemplateService;
private final NotificationSettingsService notificationSettingsService;
+ private final UserService userService;
private final NotificationExecutorService notificationExecutor;
private final DbCallbackExecutorService dbCallbackExecutorService;
private final NotificationsTopicService notificationsTopicService;
private final TbQueueProducerProvider producerProvider;
+ private final RateLimitService rateLimitService;
+ private final MailService mailService;
+ private final SmsService smsService;
private Map channels;
@Override
public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) {
+ if (!rateLimitService.checkRateLimit(tenantId, LimitedApi.NOTIFICATION_REQUEST)) {
+ throw new TbRateLimitsException(EntityType.TENANT);
+ }
NotificationSettings settings = notificationSettingsService.findNotificationSettings(tenantId);
NotificationTemplate notificationTemplate;
if (notificationRequest.getTemplateId() != null) {
@@ -110,13 +125,12 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
List targets = notificationTargetService.findNotificationTargetsByTenantIdAndIds(tenantId,
notificationRequest.getTargets().stream().map(NotificationTargetId::new).collect(Collectors.toList()));
+ Set availableDeliveryMethods = getAvailableDeliveryMethods(tenantId);
notificationTemplate.getConfiguration().getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> {
if (!template.isEnabled()) return;
- if (deliveryMethod == NotificationDeliveryMethod.SLACK) {
- if (!settings.getDeliveryMethodsConfigs().containsKey(deliveryMethod)) {
- throw new IllegalArgumentException("Slack must be configured in the settings");
- }
+ if (!availableDeliveryMethods.contains(deliveryMethod)) {
+ throw new IllegalArgumentException("Settings for " + deliveryMethod.getName() + " are missing");
}
if (notificationRequest.getRuleId() == null) {
if (targets.stream().noneMatch(target -> target.getConfiguration().getType().getSupportedDeliveryMethods().contains(deliveryMethod))) {
@@ -163,19 +177,6 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
} catch (Exception e) {
log.error("[{}] Failed to update stats for notification request", requestId, e);
}
-
- UserId senderId = notificationRequest.getSenderId();
- if (senderId != null) {
- if (stats.getErrors().isEmpty()) {
- int sent = stats.getSent().values().stream().mapToInt(AtomicInteger::get).sum();
- sendBasicNotification(tenantId, senderId, "Notifications sent",
- "All notifications were successfully sent (" + sent + ")");
- } else {
- int failures = stats.getErrors().values().stream().mapToInt(Map::size).sum();
- sendBasicNotification(tenantId, senderId, "Notification failure",
- "Some notifications were not sent (" + failures + ")"); // TODO: 'Go to' button
- }
- }
}, dbCallbackExecutorService);
});
@@ -186,9 +187,21 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
Iterable extends NotificationRecipient> recipients;
switch (target.getConfiguration().getType()) {
case PLATFORM_USERS: {
- recipients = new PageDataIterable<>(pageLink -> {
- return notificationTargetService.findRecipientsForNotificationTargetConfig(ctx.getTenantId(), ctx.getCustomerId(), target.getConfiguration(), pageLink);
- }, 200);
+ PlatformUsersNotificationTargetConfig platformUsersTargetConfig = (PlatformUsersNotificationTargetConfig) target.getConfiguration();
+ if (platformUsersTargetConfig.getUsersFilter().getType() == UsersFilterType.AFFECTED_USER) {
+ if (ctx.getRequest().getInfo() instanceof RuleOriginatedNotificationInfo) {
+ UserId targetUserId = ((RuleOriginatedNotificationInfo) ctx.getRequest().getInfo()).getTargetUserId();
+ if (targetUserId != null) {
+ recipients = List.of(userService.findUserById(ctx.getTenantId(), targetUserId));
+ break;
+ }
+ }
+ recipients = Collections.emptyList();
+ } else {
+ recipients = new PageDataIterable<>(pageLink -> {
+ return notificationTargetService.findRecipientsForNotificationTargetConfig(ctx.getTenantId(), ctx.getCustomerId(), platformUsersTargetConfig, pageLink);
+ }, 500);
+ }
break;
}
case SLACK: {
@@ -330,6 +343,24 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
}
}
+ @Override
+ public Set getAvailableDeliveryMethods(TenantId tenantId) {
+ Set deliveryMethods = new HashSet<>();
+ deliveryMethods.add(NotificationDeliveryMethod.WEB);
+ NotificationSettings notificationSettings = notificationSettingsService.findNotificationSettings(tenantId);
+ if (notificationSettings.getDeliveryMethodsConfigs().containsKey(NotificationDeliveryMethod.SLACK)) {
+ deliveryMethods.add(NotificationDeliveryMethod.SLACK);
+ }
+ try {
+ mailService.testConnection(tenantId);
+ deliveryMethods.add(NotificationDeliveryMethod.EMAIL);
+ } catch (Exception e) {}
+ if (smsService.isConfigured(tenantId)) {
+ deliveryMethods.add(NotificationDeliveryMethod.SMS);
+ }
+ return deliveryMethods;
+ }
+
@Override
public void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId) {
log.debug("Deleting notification request {}", notificationRequestId);
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationProcessingContext.java b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java
similarity index 79%
rename from common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationProcessingContext.java
rename to application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java
index a22f6a69d2..5492650d48 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationProcessingContext.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.common.data.notification;
+package org.thingsboard.server.service.notification;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -25,6 +25,9 @@ import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
+import org.thingsboard.server.common.data.notification.NotificationRequest;
+import org.thingsboard.server.common.data.notification.NotificationRequestStats;
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
import org.thingsboard.server.common.data.notification.settings.NotificationDeliveryMethodConfig;
@@ -35,12 +38,12 @@ import org.thingsboard.server.common.data.notification.template.NotificationTemp
import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig;
import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate;
-import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.regex.Pattern;
@SuppressWarnings("unchecked")
public class NotificationProcessingContext {
@@ -59,6 +62,8 @@ public class NotificationProcessingContext {
@Getter
private final NotificationRequestStats stats;
+ private static final Pattern TEMPLATE_PARAM_PATTERN = Pattern.compile("\\$\\{([a-zA-Z]+)(:[a-zA-Z]+)?}");
+
@Builder
public NotificationProcessingContext(TenantId tenantId, NotificationRequest request, NotificationSettings settings,
NotificationTemplate template) {
@@ -74,18 +79,9 @@ public class NotificationProcessingContext {
private void init() {
NotificationTemplateConfig templateConfig = notificationTemplate.getConfiguration();
templateConfig.getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> {
- if (!template.isEnabled()) return;
-
- template = template.copy();
- if (StringUtils.isEmpty(template.getBody())) {
- template.setBody(templateConfig.getDefaultTextTemplate());
+ if (template.isEnabled()) {
+ templates.put(deliveryMethod, template);
}
- if (template instanceof HasSubject) {
- if (StringUtils.isEmpty(((HasSubject) template).getSubject())) {
- ((HasSubject) template).setSubject(templateConfig.getNotificationSubject());
- }
- }
- templates.put(deliveryMethod, template);
});
deliveryMethods = templates.keySet();
}
@@ -114,9 +110,14 @@ public class NotificationProcessingContext {
.map(config -> config.get("actionButtonConfig")).filter(JsonNode::isObject)
.map(config -> (ObjectNode) config);
if (buttonConfig.isPresent()) {
+ JsonNode text = buttonConfig.get().get("text");
+ if (text != null && text.isTextual()) {
+ text = new TextNode(processTemplate(text.asText(), templateContext));
+ buttonConfig.get().set("text", text);
+ }
JsonNode link = buttonConfig.get().get("link");
if (link != null && link.isTextual()) {
- link = new TextNode(processTemplate(link.asText(), templateContext, info != null ? info.getTemplateData() : Collections.emptyMap()));
+ link = new TextNode(processTemplate(link.asText(), templateContext));
buttonConfig.get().set("link", link);
}
}
@@ -124,22 +125,23 @@ public class NotificationProcessingContext {
return template;
}
- private static String processTemplate(String template, Map... contexts) {
- if (template == null) return null;
- String result = template;
- for (Map context : contexts) {
- for (Map.Entry kv : context.entrySet()) {
- String value = Strings.nullToEmpty(kv.getValue());
- result = result.replace("${" + kv.getKey() + '}', value);
+ private static String processTemplate(String template, Map context) {
+ return TEMPLATE_PARAM_PATTERN.matcher(template).replaceAll(matchResult -> {
+ String key = matchResult.group(1);
+ String value = Strings.nullToEmpty(context.get(key));
+ String function = matchResult.group(2);
+ if (function != null) {
+ switch (function) {
+ case ":upperCase":
+ return value.toUpperCase();
+ case ":lowerCase":
+ return value.toLowerCase();
+ case ":capitalize":
+ return StringUtils.capitalize(value.toLowerCase());
+ }
}
- }
- return result;
- }
-
- public static String processTemplate(String template, NotificationInfo notificationInfo) {
- if (notificationInfo == null) return template;
- Map templateContext = notificationInfo.getTemplateData();
- return processTemplate(template, templateContext);
+ return value;
+ });
}
public Map createTemplateContext(User recipient) {
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/channels/EmailNotificationChannel.java b/application/src/main/java/org/thingsboard/server/service/notification/channels/EmailNotificationChannel.java
index be80d9d75c..ce9c6e3b98 100644
--- a/application/src/main/java/org/thingsboard/server/service/notification/channels/EmailNotificationChannel.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/channels/EmailNotificationChannel.java
@@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate;
import org.thingsboard.server.service.mail.MailExecutorService;
-import org.thingsboard.server.common.data.notification.NotificationProcessingContext;
+import org.thingsboard.server.service.notification.NotificationProcessingContext;
@Component
@RequiredArgsConstructor
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/channels/NotificationChannel.java b/application/src/main/java/org/thingsboard/server/service/notification/channels/NotificationChannel.java
index 0357cccf3a..7e82d31c27 100644
--- a/application/src/main/java/org/thingsboard/server/service/notification/channels/NotificationChannel.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/channels/NotificationChannel.java
@@ -17,7 +17,7 @@ package org.thingsboard.server.service.notification.channels;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
-import org.thingsboard.server.common.data.notification.NotificationProcessingContext;
+import org.thingsboard.server.service.notification.NotificationProcessingContext;
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/channels/SlackNotificationChannel.java b/application/src/main/java/org/thingsboard/server/service/notification/channels/SlackNotificationChannel.java
index 2c9d948fb8..67108c2a37 100644
--- a/application/src/main/java/org/thingsboard/server/service/notification/channels/SlackNotificationChannel.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/channels/SlackNotificationChannel.java
@@ -20,7 +20,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.slack.SlackService;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
-import org.thingsboard.server.common.data.notification.NotificationProcessingContext;
+import org.thingsboard.server.service.notification.NotificationProcessingContext;
import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
import org.thingsboard.server.common.data.notification.template.SlackDeliveryMethodNotificationTemplate;
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java b/application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java
index 5d97d73a90..b49f1a9b73 100644
--- a/application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java
@@ -24,7 +24,7 @@ import org.thingsboard.rule.engine.api.SmsService;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate;
-import org.thingsboard.server.common.data.notification.NotificationProcessingContext;
+import org.thingsboard.server.service.notification.NotificationProcessingContext;
import org.thingsboard.server.service.sms.SmsExecutorService;
@Component
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java
index 5d89983b04..682c97ede2 100644
--- a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java
@@ -23,30 +23,27 @@ import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.NotificationCenter;
import org.thingsboard.server.common.data.EntityType;
-import org.thingsboard.server.common.data.UpdateMessage;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.NotificationRequestId;
import org.thingsboard.server.common.data.id.NotificationRuleId;
-import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.NotificationRequest;
import org.thingsboard.server.common.data.notification.NotificationRequestConfig;
import org.thingsboard.server.common.data.notification.NotificationRequestStatus;
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
import org.thingsboard.server.common.data.notification.rule.NotificationRule;
+import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
-import org.thingsboard.server.common.msg.queue.ServiceType;
-import org.thingsboard.server.dao.alarm.AlarmApiCallResult;
import org.thingsboard.server.dao.notification.NotificationRequestService;
+import org.thingsboard.server.dao.notification.NotificationRuleProcessingService;
import org.thingsboard.server.dao.notification.NotificationRuleService;
+import org.thingsboard.server.dao.notification.trigger.RuleEngineMsgTrigger;
import org.thingsboard.server.service.executors.NotificationExecutorService;
-import org.thingsboard.server.service.notification.rule.trigger.EntitiesLimitTriggerProcessor.EntitiesLimitTriggerObject;
import org.thingsboard.server.service.notification.rule.trigger.NotificationRuleTriggerProcessor;
-import org.thingsboard.server.service.notification.rule.trigger.RuleEngineComponentLifecycleEventTriggerProcessor.RuleEngineComponentLifecycleEventTriggerObject;
import org.thingsboard.server.service.notification.rule.trigger.RuleEngineMsgNotificationRuleTriggerProcessor;
import java.util.Collection;
@@ -61,7 +58,7 @@ import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
@Slf4j
-@SuppressWarnings("rawtypes")
+@SuppressWarnings({"rawtypes", "unchecked"})
public class DefaultNotificationRuleProcessingService implements NotificationRuleProcessingService {
private final NotificationRuleService notificationRuleService;
@@ -75,80 +72,37 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul
private final Map ruleEngineMsgTypeToTriggerType = new HashMap<>();
@Override
- public void process(TenantId tenantId, TbMsg ruleEngineMsg) {
- String msgType = ruleEngineMsg.getType();
- NotificationRuleTriggerType triggerType = ruleEngineMsgTypeToTriggerType.get(msgType);
- if (triggerType == null) {
- return;
- }
- processTrigger(tenantId, triggerType, ruleEngineMsg.getOriginator(), ruleEngineMsg);
- }
-
- @Override
- public void process(TenantId tenantId, AlarmApiCallResult alarmUpdate) {
- processTrigger(tenantId, NotificationRuleTriggerType.ALARM, alarmUpdate.getAlarm().getId(), alarmUpdate);
- }
-
- @Override
- public void process(TenantId tenantId, RuleChainId ruleChainId, String ruleChainName, EntityId componentId, String componentName, ComponentLifecycleEvent eventType, Exception error) {
- RuleEngineComponentLifecycleEventTriggerObject triggerObject = RuleEngineComponentLifecycleEventTriggerObject.builder()
- .ruleChainId(ruleChainId)
- .ruleChainName(ruleChainName)
- .componentId(componentId)
- .componentName(componentName)
- .eventType(eventType)
- .error(error)
- .build();
- processTrigger(tenantId, NotificationRuleTriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, componentId, triggerObject);
- }
-
- @Override
- public void process(UpdateMessage platformUpdateMessage) {
-// if (!partitionService.resolve(ServiceType.TB_CORE, TenantId.SYS_TENANT_ID, TenantId.SYS_TENANT_ID).isMyPartition()) {
-// return;
-// }
-// // todo: don't send repetitive notification after platform restart?
-//
-// processTrigger(TenantId.SYS_TENANT_ID, NotificationRuleTriggerType.NEW_PLATFORM_VERSION, TenantId.SYS_TENANT_ID, platformUpdateMessage);
- }
-
- @Override
- public void process(TenantId tenantId, EntityType entityType, long limit, long currentCount) {
-// EntitiesLimitTriggerObject triggerObject = EntitiesLimitTriggerObject.builder()
-// .entityType(entityType)
-// .limit(limit)
-// .currentCount(currentCount)
-// .build();
- }
-
- @Override
- public void process(ComponentLifecycleMsg componentLifecycleMsg) {
-// EntityId entityId = componentLifecycleMsg.getEntityId();
-// switch (entityId.getEntityType()) {
-// case TENANT:
-//
-// }
- }
-
- private void processTrigger(TenantId tenantId, NotificationRuleTriggerType triggerType, EntityId originatorEntityId, Object triggerObject) {
- List rules = notificationRuleService.findNotificationRulesByTenantIdAndTriggerType(tenantId, triggerType);
+ public void process(TenantId tenantId, NotificationRuleTrigger trigger) {
+ List rules = notificationRuleService.findNotificationRulesByTenantIdAndTriggerType(tenantId, trigger.getType());
for (NotificationRule rule : rules) {
notificationExecutor.submit(() -> {
try {
- processNotificationRule(rule, originatorEntityId, triggerObject);
+ processNotificationRule(rule, trigger);
} catch (Throwable e) {
- log.error("Failed to process notification rule {} for trigger type {} with trigger object {}", rule.getId(), rule.getTriggerType(), triggerObject, e);
+ log.error("Failed to process notification rule {} for trigger type {} with trigger object {}", rule.getId(), rule.getTriggerType(), trigger, e);
}
});
}
}
- private void processNotificationRule(NotificationRule rule, EntityId originatorEntityId, Object triggerObject) {
+ @Override
+ public void process(TenantId tenantId, TbMsg ruleEngineMsg) {
+ NotificationRuleTriggerType triggerType = ruleEngineMsgTypeToTriggerType.get(ruleEngineMsg.getType());
+ if (triggerType == null) {
+ return;
+ }
+ process(tenantId, RuleEngineMsgTrigger.builder()
+ .msg(ruleEngineMsg)
+ .triggerType(triggerType)
+ .build());
+ }
+
+ private void processNotificationRule(NotificationRule rule, NotificationRuleTrigger trigger) {
NotificationRuleTriggerConfig triggerConfig = rule.getTriggerConfig();
log.debug("Processing notification rule '{}' for trigger type {}", rule.getName(), rule.getTriggerType());
- if (matchesClearRule(triggerObject, triggerConfig)) {
- List notificationRequests = notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityId(rule.getTenantId(), rule.getId(), originatorEntityId);
+ if (matchesClearRule(trigger, triggerConfig)) {
+ List notificationRequests = notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityId(rule.getTenantId(), rule.getId(), trigger.getOriginatorEntityId());
if (notificationRequests.isEmpty()) {
return;
}
@@ -157,8 +111,8 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul
.filter(NotificationRequest::isSent)
.flatMap(notificationRequest -> notificationRequest.getTargets().stream())
.distinct().collect(Collectors.toList());
- NotificationInfo notificationInfo = constructNotificationInfo(triggerObject, triggerConfig);
- submitNotificationRequest(targets, rule, originatorEntityId, notificationInfo, 0);
+ NotificationInfo notificationInfo = constructNotificationInfo(trigger, triggerConfig);
+ submitNotificationRequest(targets, rule, trigger.getOriginatorEntityId(), notificationInfo, 0);
notificationRequests.forEach(notificationRequest -> {
if (notificationRequest.isScheduled()) {
@@ -168,24 +122,24 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul
return;
}
- if (matchesFilter(triggerObject, triggerConfig)) {
- NotificationInfo notificationInfo = constructNotificationInfo(triggerObject, triggerConfig);
+ if (matchesFilter(trigger, triggerConfig)) {
+ NotificationInfo notificationInfo = constructNotificationInfo(trigger, triggerConfig);
rule.getRecipientsConfig().getTargetsTable().forEach((delay, targets) -> {
- submitNotificationRequest(targets, rule, originatorEntityId, notificationInfo, delay);
+ submitNotificationRequest(targets, rule, trigger.getOriginatorEntityId(), notificationInfo, delay);
});
}
}
- private boolean matchesFilter(Object triggerObject, NotificationRuleTriggerConfig triggerConfig) {
- return triggerProcessors.get(triggerConfig.getTriggerType()).matchesFilter(triggerObject, triggerConfig);
+ private boolean matchesFilter(NotificationRuleTrigger trigger, NotificationRuleTriggerConfig triggerConfig) {
+ return triggerProcessors.get(triggerConfig.getTriggerType()).matchesFilter(trigger, triggerConfig);
}
- private boolean matchesClearRule(Object triggerObject, NotificationRuleTriggerConfig triggerConfig) {
- return triggerProcessors.get(triggerConfig.getTriggerType()).matchesClearRule(triggerObject, triggerConfig);
+ private boolean matchesClearRule(NotificationRuleTrigger trigger, NotificationRuleTriggerConfig triggerConfig) {
+ return triggerProcessors.get(triggerConfig.getTriggerType()).matchesClearRule(trigger, triggerConfig);
}
- private NotificationInfo constructNotificationInfo(Object triggerObject, NotificationRuleTriggerConfig triggerConfig) {
- return triggerProcessors.get(triggerConfig.getTriggerType()).constructNotificationInfo(triggerObject, triggerConfig);
+ private NotificationInfo constructNotificationInfo(NotificationRuleTrigger trigger, NotificationRuleTriggerConfig triggerConfig) {
+ return triggerProcessors.get(triggerConfig.getTriggerType()).constructNotificationInfo(trigger, triggerConfig);
}
private void submitNotificationRequest(List targets, NotificationRule rule,
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/NotificationRuleProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/NotificationRuleProcessingService.java
deleted file mode 100644
index 7d9db425f1..0000000000
--- a/application/src/main/java/org/thingsboard/server/service/notification/rule/NotificationRuleProcessingService.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * Copyright © 2016-2023 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.service.notification.rule;
-
-import org.thingsboard.server.common.data.EntityType;
-import org.thingsboard.server.common.data.UpdateMessage;
-import org.thingsboard.server.common.data.id.EntityId;
-import org.thingsboard.server.common.data.id.RuleChainId;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
-import org.thingsboard.server.common.msg.TbMsg;
-import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
-import org.thingsboard.server.dao.alarm.AlarmApiCallResult;
-
-public interface NotificationRuleProcessingService {
-
- void process(TenantId tenantId, TbMsg ruleEngineMsg);
-
- // for handling internal component lifecycle events that are not getting to rule chain
- void process(ComponentLifecycleMsg componentLifecycleMsg);
-
- void process(TenantId tenantId, AlarmApiCallResult alarmUpdate);
-
- void process(TenantId tenantId, RuleChainId ruleChainId, String ruleChainName,
- EntityId componentId, String componentName, ComponentLifecycleEvent eventType, Exception error);
-
- void process(UpdateMessage platformUpdateMessage);
-
- void process(TenantId tenantId, EntityType entityType, long limit, long currentCount);
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java
index 6ccff5686f..9fd0c06118 100644
--- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java
@@ -25,8 +25,9 @@ import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
import org.thingsboard.server.common.data.notification.info.AlarmAssignmentNotificationInfo;
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentNotificationRuleTriggerConfig;
+import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentNotificationRuleTriggerConfig.Action;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
-import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.dao.notification.trigger.RuleEngineMsgTrigger;
import java.util.Set;
@@ -36,26 +37,28 @@ import static org.apache.commons.collections.CollectionUtils.isEmpty;
public class AlarmAssignmentTriggerProcessor implements RuleEngineMsgNotificationRuleTriggerProcessor {
@Override
- public boolean matchesFilter(TbMsg ruleEngineMsg, AlarmAssignmentNotificationRuleTriggerConfig triggerConfig) {
- if (ruleEngineMsg.getType().equals(DataConstants.ALARM_UNASSIGN) && !triggerConfig.isNotifyOnUnassign()) {
+ public boolean matchesFilter(RuleEngineMsgTrigger trigger, AlarmAssignmentNotificationRuleTriggerConfig triggerConfig) {
+ Action action = trigger.getMsg().getType().equals(DataConstants.ALARM_ASSIGN) ? Action.ASSIGNED : Action.UNASSIGNED;
+ if (!triggerConfig.getNotifyOn().contains(action)) {
return false;
}
- Alarm alarm = JacksonUtil.fromString(ruleEngineMsg.getData(), Alarm.class);
+ Alarm alarm = JacksonUtil.fromString(trigger.getMsg().getData(), Alarm.class);
return (isEmpty(triggerConfig.getAlarmTypes()) || triggerConfig.getAlarmTypes().contains(alarm.getType())) &&
(isEmpty(triggerConfig.getAlarmSeverities()) || triggerConfig.getAlarmSeverities().contains(alarm.getSeverity())) &&
(isEmpty(triggerConfig.getAlarmStatuses()) || AlarmStatusFilter.from(triggerConfig.getAlarmStatuses()).matches(alarm));
}
@Override
- public NotificationInfo constructNotificationInfo(TbMsg ruleEngineMsg, AlarmAssignmentNotificationRuleTriggerConfig triggerConfig) {
- // TODO: readable action
- AlarmInfo alarmInfo = JacksonUtil.fromString(ruleEngineMsg.getData(), AlarmInfo.class);
+ public NotificationInfo constructNotificationInfo(RuleEngineMsgTrigger trigger, AlarmAssignmentNotificationRuleTriggerConfig triggerConfig) {
+ AlarmInfo alarmInfo = JacksonUtil.fromString(trigger.getMsg().getData(), AlarmInfo.class);
AlarmAssignee assignee = alarmInfo.getAssignee();
return AlarmAssignmentNotificationInfo.builder()
+ .action(trigger.getMsg().getType().equals(DataConstants.ALARM_ASSIGN) ? "assigned" : "unassigned")
.assigneeFirstName(assignee != null ? assignee.getFirstName() : null)
.assigneeLastName(assignee != null ? assignee.getLastName() : null)
.assigneeEmail(assignee != null ? assignee.getEmail() : null)
- .userName(ruleEngineMsg.getMetaData().getValue("userName"))
+ .assigneeId(assignee != null ? assignee.getId() : null)
+ .userName(trigger.getMsg().getMetaData().getValue("userName"))
.alarmId(alarmInfo.getUuidId())
.alarmType(alarmInfo.getType())
.alarmOriginator(alarmInfo.getOriginator())
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java
index 8be6e1622b..0250f0929f 100644
--- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java
@@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.notification.info.NotificationInfo;
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmCommentNotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.dao.notification.trigger.RuleEngineMsgTrigger;
import java.util.Set;
@@ -37,30 +38,35 @@ import static org.apache.commons.collections.CollectionUtils.isEmpty;
public class AlarmCommentTriggerProcessor implements RuleEngineMsgNotificationRuleTriggerProcessor {
@Override
- public boolean matchesFilter(TbMsg ruleEngineMsg, AlarmCommentNotificationRuleTriggerConfig triggerConfig) {
- if (ruleEngineMsg.getMetaData().getValue("comment") == null) {
+ public boolean matchesFilter(RuleEngineMsgTrigger trigger, AlarmCommentNotificationRuleTriggerConfig triggerConfig) {
+ TbMsg msg = trigger.getMsg();
+ if (msg.getMetaData().getValue("comment") == null) {
+ return false;
+ }
+ if (msg.getType().equals(DataConstants.COMMENT_UPDATED) && !triggerConfig.isNotifyOnCommentUpdate()) {
return false;
}
if (triggerConfig.isOnlyUserComments()) {
- AlarmComment comment = JacksonUtil.fromString(ruleEngineMsg.getMetaData().getValue("comment"), AlarmComment.class);
+ AlarmComment comment = JacksonUtil.fromString(msg.getMetaData().getValue("comment"), AlarmComment.class);
if (comment.getType() == AlarmCommentType.SYSTEM) {
return false;
}
}
- Alarm alarm = JacksonUtil.fromString(ruleEngineMsg.getData(), Alarm.class);
+ Alarm alarm = JacksonUtil.fromString(msg.getData(), Alarm.class);
return (isEmpty(triggerConfig.getAlarmTypes()) || triggerConfig.getAlarmTypes().contains(alarm.getType())) &&
(isEmpty(triggerConfig.getAlarmSeverities()) || triggerConfig.getAlarmSeverities().contains(alarm.getSeverity())) &&
(isEmpty(triggerConfig.getAlarmStatuses()) || AlarmStatusFilter.from(triggerConfig.getAlarmStatuses()).matches(alarm));
}
@Override
- public NotificationInfo constructNotificationInfo(TbMsg ruleEngineMsg, AlarmCommentNotificationRuleTriggerConfig triggerConfig) {
- // TODO: readable action
- AlarmComment comment = JacksonUtil.fromString(ruleEngineMsg.getMetaData().getValue("comment"), AlarmComment.class);
- AlarmInfo alarmInfo = JacksonUtil.fromString(ruleEngineMsg.getData(), AlarmInfo.class);
+ public NotificationInfo constructNotificationInfo(RuleEngineMsgTrigger trigger, AlarmCommentNotificationRuleTriggerConfig triggerConfig) {
+ TbMsg msg = trigger.getMsg();
+ AlarmComment comment = JacksonUtil.fromString(msg.getMetaData().getValue("comment"), AlarmComment.class);
+ AlarmInfo alarmInfo = JacksonUtil.fromString(msg.getData(), AlarmInfo.class);
return AlarmCommentNotificationInfo.builder()
.comment(comment.getComment().get("text").asText())
- .userName(ruleEngineMsg.getMetaData().getValue("userName"))
+ .action(msg.getType().equals(DataConstants.COMMENT_CREATED) ? "added" : "updated")
+ .userName(msg.getMetaData().getValue("userName"))
.alarmId(alarmInfo.getUuidId())
.alarmType(alarmInfo.getType())
.alarmOriginator(alarmInfo.getOriginator())
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java
index babe177eea..085fb02bbc 100644
--- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java
@@ -26,15 +26,17 @@ import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotific
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig.ClearRule;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
import org.thingsboard.server.dao.alarm.AlarmApiCallResult;
+import org.thingsboard.server.dao.notification.trigger.AlarmTrigger;
import static org.apache.commons.collections.CollectionUtils.isEmpty;
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
@Service
-public class AlarmTriggerProcessor implements NotificationRuleTriggerProcessor {
+public class AlarmTriggerProcessor implements NotificationRuleTriggerProcessor {
@Override
- public boolean matchesFilter(AlarmApiCallResult alarmUpdate, AlarmNotificationRuleTriggerConfig triggerConfig) {
+ public boolean matchesFilter(AlarmTrigger trigger, AlarmNotificationRuleTriggerConfig triggerConfig) {
+ AlarmApiCallResult alarmUpdate = trigger.getAlarmUpdate();
Alarm alarm = alarmUpdate.getAlarm();
if (!typeMatches(alarm, triggerConfig)) {
return false;
@@ -64,7 +66,8 @@ public class AlarmTriggerProcessor implements NotificationRuleTriggerProcessor {
+public class EntitiesLimitTriggerProcessor implements NotificationRuleTriggerProcessor {
@Override
- public boolean matchesFilter(EntitiesLimitTriggerObject triggerObject, EntitiesLimitNotificationRuleTriggerConfig triggerConfig) {
- if (isNotEmpty(triggerConfig.getEntityTypes()) && !triggerConfig.getEntityTypes().contains(triggerObject.getEntityType())) {
+ public boolean matchesFilter(EntitiesLimitTrigger trigger, EntitiesLimitNotificationRuleTriggerConfig triggerConfig) {
+ if (isNotEmpty(triggerConfig.getEntityTypes()) && !triggerConfig.getEntityTypes().contains(trigger.getEntityType())) {
return false;
}
- return ((float) triggerObject.getCurrentCount() / triggerObject.getLimit()) >= triggerConfig.getThreshold();
+ return (int) (trigger.getLimit() * triggerConfig.getThreshold()) == trigger.getCurrentCount(); // strict comparing not to send notification on each new entity
}
@Override
- public NotificationInfo constructNotificationInfo(EntitiesLimitTriggerObject triggerObject, EntitiesLimitNotificationRuleTriggerConfig triggerConfig) {
+ public NotificationInfo constructNotificationInfo(EntitiesLimitTrigger trigger, EntitiesLimitNotificationRuleTriggerConfig triggerConfig) {
return EntitiesLimitNotificationInfo.builder()
- .entityType(triggerObject.getEntityType())
- .threshold((int) (triggerConfig.getThreshold() * 100))
+ .entityType(trigger.getEntityType())
+ .currentCount(trigger.getCurrentCount())
+ .limit(trigger.getLimit())
+ .percents((int) (((float)trigger.getCurrentCount() / trigger.getLimit()) * 100))
.build();
}
@@ -51,12 +50,4 @@ public class EntitiesLimitTriggerProcessor implements NotificationRuleTriggerPro
return NotificationRuleTriggerType.ENTITIES_LIMIT;
}
- @Data
- @Builder
- public static class EntitiesLimitTriggerObject {
- private final EntityType entityType;
- private final long limit;
- private final long currentCount;
- }
-
}
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java
index 60044dd96a..bcace461f5 100644
--- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java
@@ -17,14 +17,16 @@ package org.thingsboard.server.service.notification.rule.trigger;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.DataConstants;
+import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType;
-import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.notification.info.EntityActionNotificationInfo;
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionNotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.dao.notification.trigger.RuleEngineMsgTrigger;
+import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@@ -32,8 +34,8 @@ import java.util.UUID;
public class EntityActionTriggerProcessor implements RuleEngineMsgNotificationRuleTriggerProcessor {
@Override
- public boolean matchesFilter(TbMsg ruleEngineMsg, EntityActionNotificationRuleTriggerConfig triggerConfig) {
- String msgType = ruleEngineMsg.getType();
+ public boolean matchesFilter(RuleEngineMsgTrigger trigger, EntityActionNotificationRuleTriggerConfig triggerConfig) {
+ String msgType = trigger.getMsg().getType();
if (msgType.equals(DataConstants.ENTITY_CREATED)) {
if (!triggerConfig.isCreated()) {
return false;
@@ -49,26 +51,31 @@ public class EntityActionTriggerProcessor implements RuleEngineMsgNotificationRu
} else {
return false;
}
- return triggerConfig.getEntityType() == null || ruleEngineMsg.getOriginator().getEntityType() == triggerConfig.getEntityType();
+ return triggerConfig.getEntityType() == null || getEntityType(trigger.getMsg()) == triggerConfig.getEntityType();
}
@Override
- public NotificationInfo constructNotificationInfo(TbMsg ruleEngineMsg, EntityActionNotificationRuleTriggerConfig triggerConfig) {
- EntityId entityId = ruleEngineMsg.getOriginator();
- String msgType = ruleEngineMsg.getType();
+ public NotificationInfo constructNotificationInfo(RuleEngineMsgTrigger trigger, EntityActionNotificationRuleTriggerConfig triggerConfig) {
+ TbMsg msg = trigger.getMsg();
+ String msgType = msg.getType();
ActionType actionType = msgType.equals(DataConstants.ENTITY_CREATED) ? ActionType.ADDED :
msgType.equals(DataConstants.ENTITY_UPDATED) ? ActionType.UPDATED :
msgType.equals(DataConstants.ENTITY_DELETED) ? ActionType.DELETED : null;
return EntityActionNotificationInfo.builder()
- .entityId(entityId)
- .entityName(ruleEngineMsg.getMetaData().getValue("entityName"))
+ .entityId(actionType != ActionType.DELETED ? msg.getOriginator() : null)
+ .entityName(msg.getMetaData().getValue("entityName"))
.actionType(actionType)
- .originatorUserId(UUID.fromString(ruleEngineMsg.getMetaData().getValue("userId")))
- .originatorUserName(ruleEngineMsg.getMetaData().getValue("userName"))
- .entityCustomerId(ruleEngineMsg.getCustomerId())
+ .originatorUserId(UUID.fromString(msg.getMetaData().getValue("userId")))
+ .originatorUserName(msg.getMetaData().getValue("userName"))
+ .entityCustomerId(msg.getCustomerId())
.build();
}
+ private static EntityType getEntityType(TbMsg msg) {
+ return Optional.ofNullable(msg.getMetaData().getValue("entityType"))
+ .map(EntityType::valueOf).orElse(null);
+ }
+
@Override
public NotificationRuleTriggerType getTriggerType() {
return NotificationRuleTriggerType.ENTITY_ACTION;
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java
index e26328404b..ba346caab8 100644
--- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java
@@ -15,25 +15,36 @@
*/
package org.thingsboard.server.service.notification.rule.trigger;
+import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
-import org.thingsboard.server.common.data.UpdateMessage;
+import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.info.NewPlatformVersionNotificationInfo;
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionNotificationRuleTriggerConfig;
+import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionTrigger;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
+import org.thingsboard.server.common.msg.queue.ServiceType;
+import org.thingsboard.server.queue.discovery.PartitionService;
@Service
-public class NewPlatformVersionTriggerProcessor implements NotificationRuleTriggerProcessor {
+@RequiredArgsConstructor
+public class NewPlatformVersionTriggerProcessor implements NotificationRuleTriggerProcessor {
+
+ private final PartitionService partitionService;
@Override
- public boolean matchesFilter(UpdateMessage triggerObject, NewPlatformVersionNotificationRuleTriggerConfig triggerConfig) {
- return triggerObject.isUpdateAvailable();
+ public boolean matchesFilter(NewPlatformVersionTrigger trigger, NewPlatformVersionNotificationRuleTriggerConfig triggerConfig) {
+ // todo: don't send repetitive notification after platform restart?
+ if (!partitionService.resolve(ServiceType.TB_CORE, TenantId.SYS_TENANT_ID, TenantId.SYS_TENANT_ID).isMyPartition()) {
+ return false;
+ }
+ return trigger.getMessage().isUpdateAvailable();
}
@Override
- public NotificationInfo constructNotificationInfo(UpdateMessage updateMessage, NewPlatformVersionNotificationRuleTriggerConfig triggerConfig) {
+ public NotificationInfo constructNotificationInfo(NewPlatformVersionTrigger trigger, NewPlatformVersionNotificationRuleTriggerConfig triggerConfig) {
return NewPlatformVersionNotificationInfo.builder()
- .message(updateMessage.getMessage())
+ .message(trigger.getMessage().getMessage())
.build();
}
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NotificationRuleTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NotificationRuleTriggerProcessor.java
index 4980906f7a..bb24f66d87 100644
--- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NotificationRuleTriggerProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NotificationRuleTriggerProcessor.java
@@ -16,18 +16,19 @@
package org.thingsboard.server.service.notification.rule.trigger;
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
+import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
-public interface NotificationRuleTriggerProcessor {
+public interface NotificationRuleTriggerProcessor {
- boolean matchesFilter(T triggerObject, C triggerConfig);
+ boolean matchesFilter(T trigger, C triggerConfig);
- default boolean matchesClearRule(T triggerObject, C triggerConfig) {
+ default boolean matchesClearRule(T trigger, C triggerConfig) {
return false;
}
- NotificationInfo constructNotificationInfo(T triggerObject, C triggerConfig);
+ NotificationInfo constructNotificationInfo(T trigger, C triggerConfig);
NotificationRuleTriggerType getTriggerType();
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java
index 53b53ab0aa..afda3cc001 100644
--- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java
@@ -15,36 +15,34 @@
*/
package org.thingsboard.server.service.notification.rule.trigger;
-import com.google.common.base.Strings;
-import lombok.Builder;
-import lombok.Data;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.EntityType;
-import org.thingsboard.server.common.data.id.EntityId;
-import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
import org.thingsboard.server.common.data.notification.info.RuleEngineComponentLifecycleEventNotificationInfo;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
-import org.thingsboard.server.service.notification.rule.trigger.RuleEngineComponentLifecycleEventTriggerProcessor.RuleEngineComponentLifecycleEventTriggerObject;
+import org.thingsboard.server.dao.notification.trigger.RuleEngineComponentLifecycleEventTrigger;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.Set;
@Service
-public class RuleEngineComponentLifecycleEventTriggerProcessor implements NotificationRuleTriggerProcessor {
+public class RuleEngineComponentLifecycleEventTriggerProcessor implements NotificationRuleTriggerProcessor {
@Override
- public boolean matchesFilter(RuleEngineComponentLifecycleEventTriggerObject triggerObject, RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig triggerConfig) {
+ public boolean matchesFilter(RuleEngineComponentLifecycleEventTrigger trigger, RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig triggerConfig) {
if (CollectionUtils.isNotEmpty(triggerConfig.getRuleChains())) {
- if (!triggerConfig.getRuleChains().contains(triggerObject.getRuleChainId().getId())) {
+ if (!triggerConfig.getRuleChains().contains(trigger.getRuleChainId().getId())) {
return false;
}
}
- EntityType componentType = triggerObject.getComponentId().getEntityType();
+ EntityType componentType = trigger.getComponentId().getEntityType();
Set trackedEvents;
boolean onlyFailures;
if (componentType == EntityType.RULE_CHAIN) {
@@ -60,35 +58,36 @@ public class RuleEngineComponentLifecycleEventTriggerProcessor implements Notifi
trackedEvents = Set.of(ComponentLifecycleEvent.STARTED, ComponentLifecycleEvent.UPDATED, ComponentLifecycleEvent.STOPPED);
}
- if (!trackedEvents.contains(triggerObject.getEventType())) {
+ if (!trackedEvents.contains(trigger.getEventType())) {
return false;
}
if (onlyFailures) {
- return triggerObject.getError() != null;
+ return trigger.getError() != null;
}
return true;
}
@Override
- public NotificationInfo constructNotificationInfo(RuleEngineComponentLifecycleEventTriggerObject triggerObject, RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig triggerConfig) {
+ public NotificationInfo constructNotificationInfo(RuleEngineComponentLifecycleEventTrigger trigger, RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig triggerConfig) {
return RuleEngineComponentLifecycleEventNotificationInfo.builder()
- .ruleChainId(triggerObject.getRuleChainId())
- .ruleChainName(triggerObject.getRuleChainName())
- .componentId(triggerObject.getComponentId())
- .componentName(triggerObject.getComponentName())
- .eventType(triggerObject.getEventType())
- .error(getErrorMsg(triggerObject.getError()))
+ .ruleChainId(trigger.getRuleChainId())
+ .ruleChainName(trigger.getRuleChainName())
+ .componentId(trigger.getComponentId())
+ .componentName(trigger.getComponentName())
+ .action(trigger.getEventType() == ComponentLifecycleEvent.STARTED ? "start" :
+ trigger.getEventType() == ComponentLifecycleEvent.UPDATED ? "update" :
+ trigger.getEventType() == ComponentLifecycleEvent.STOPPED ? "stop" : null)
+ .eventType(trigger.getEventType())
+ .error(getErrorMsg(trigger.getError()))
.build();
}
- private String getErrorMsg(Exception error) {
- String errorMsg = error != null ? error.getMessage() : null;
- errorMsg = Strings.nullToEmpty(errorMsg);
- int lengthLimit = 150;
- if (errorMsg.length() > lengthLimit) {
- errorMsg = StringUtils.substring(errorMsg, 0, lengthLimit + 1).trim() + "[...]";
- }
- return errorMsg;
+ private String getErrorMsg(Throwable error) {
+ if (error == null) return null;
+
+ StringWriter sw = new StringWriter();
+ error.printStackTrace(new PrintWriter(sw));
+ return StringUtils.abbreviate(ExceptionUtils.getStackTrace(error), 200);
}
@Override
@@ -96,15 +95,4 @@ public class RuleEngineComponentLifecycleEventTriggerProcessor implements Notifi
return NotificationRuleTriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT;
}
- @Data
- @Builder
- public static class RuleEngineComponentLifecycleEventTriggerObject {
- private final RuleChainId ruleChainId;
- private final String ruleChainName;
- private final EntityId componentId;
- private final String componentName;
- private final ComponentLifecycleEvent eventType;
- private final Exception error;
- }
-
}
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineMsgNotificationRuleTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineMsgNotificationRuleTriggerProcessor.java
index 8d200c4ae3..4436b4737f 100644
--- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineMsgNotificationRuleTriggerProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineMsgNotificationRuleTriggerProcessor.java
@@ -16,11 +16,11 @@
package org.thingsboard.server.service.notification.rule.trigger;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig;
-import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.dao.notification.trigger.RuleEngineMsgTrigger;
import java.util.Set;
-public interface RuleEngineMsgNotificationRuleTriggerProcessor extends NotificationRuleTriggerProcessor {
+public interface RuleEngineMsgNotificationRuleTriggerProcessor extends NotificationRuleTriggerProcessor {
Set getSupportedMsgTypes();
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
index 2e736fefe1..571d4d6e96 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
@@ -69,7 +69,6 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.edge.EdgeNotificationService;
import org.thingsboard.server.service.notification.NotificationSchedulerService;
-import org.thingsboard.server.service.notification.rule.NotificationRuleProcessingService;
import org.thingsboard.server.service.ota.OtaPackageStateService;
import org.thingsboard.server.service.profile.TbAssetProfileCache;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
@@ -592,7 +591,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService objectModels =
- ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText());
- if (objectModels.size() == 0) {
- return null;
- } else {
- ObjectModel obj = objectModels.get(0);
- LwM2mObject lwM2mObject = new LwM2mObject();
- lwM2mObject.setId(obj.id);
- lwM2mObject.setKeyId(resource.getResourceKey());
- lwM2mObject.setName(obj.name);
- lwM2mObject.setMultiple(obj.multiple);
- lwM2mObject.setMandatory(obj.mandatory);
- LwM2mInstance instance = new LwM2mInstance();
- instance.setId(0);
- List resources = new ArrayList<>();
- obj.resources.forEach((k, v) -> {
- if (isSave) {
- LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false);
- resources.add(lwM2MResourceObserve);
- } else if (v.operations.isReadable()) {
- LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false);
- resources.add(lwM2MResourceObserve);
- }
- });
- if (isSave || resources.size() > 0) {
- instance.setResources(resources.toArray(LwM2mResourceObserve[]::new));
- lwM2mObject.setInstances(new LwM2mInstance[]{instance});
- return lwM2mObject;
- } else {
- return null;
- }
- }
- } catch (IOException | InvalidDDFFileException e) {
- log.error("Could not parse the XML of objectModel with name [{}]", resource.getSearchText(), e);
- return null;
- }
- }
-
@Override
public TbResource save(TbResource tbResource, User user) throws ThingsboardException {
ActionType actionType = tbResource.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
@@ -215,35 +165,11 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements
throw new DataValidationException("Resource data should be specified!");
}
if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) {
- try {
- List objectModels =
- ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText());
- if (!objectModels.isEmpty()) {
- ObjectModel objectModel = objectModels.get(0);
-
- String resourceKey = objectModel.id + LWM2M_SEPARATOR_KEY + objectModel.version;
- String name = objectModel.name;
- resource.setResourceKey(resourceKey);
- if (resource.getId() == null) {
- resource.setTitle(name + " id=" + objectModel.id + " v" + objectModel.version);
- }
- resource.setSearchText(resourceKey + LWM2M_SEPARATOR_SEARCH_TEXT + name);
- } else {
- throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", resource.getSearchText()));
- }
- } catch (InvalidDDFFileException e) {
- log.error("Failed to parse file {}", resource.getFileName(), e);
- throw new DataValidationException("Failed to parse file " + resource.getFileName());
- } catch (IOException e) {
- throw new ThingsboardException(e, ThingsboardErrorCode.GENERAL);
- }
- if (resource.getResourceType().equals(ResourceType.LWM2M_MODEL) && toLwM2mObject(resource, true) == null) {
- throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", resource.getSearchText()));
- }
+ toLwm2mResource(resource);
} else {
resource.setResourceKey(resource.getFileName());
}
-
return resourceService.saveResource(resource);
}
}
+
diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsValidator.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsValidator.java
index 274630cc39..dddb011fd5 100644
--- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsValidator.java
+++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsValidator.java
@@ -36,10 +36,10 @@ public class DefaultJwtSettingsValidator implements JwtSettingsValidator {
if (StringUtils.isEmpty(jwtSettings.getTokenIssuer())) {
throw new DataValidationException("JWT token issuer should be specified!");
}
- if (Optional.ofNullable(jwtSettings.getRefreshTokenExpTime()).orElse(0) <= TimeUnit.MINUTES.toSeconds(15)) {
+ if (Optional.ofNullable(jwtSettings.getRefreshTokenExpTime()).orElse(0) < TimeUnit.MINUTES.toSeconds(15)) {
throw new DataValidationException("JWT refresh token expiration time should be at least 15 minutes!");
}
- if (Optional.ofNullable(jwtSettings.getTokenExpirationTime()).orElse(0) <= TimeUnit.MINUTES.toSeconds(1)) {
+ if (Optional.ofNullable(jwtSettings.getTokenExpirationTime()).orElse(0) < TimeUnit.MINUTES.toSeconds(1)) {
throw new DataValidationException("JWT token expiration time should be at least 1 minute!");
}
if (jwtSettings.getTokenExpirationTime() >= jwtSettings.getRefreshTokenExpTime()) {
diff --git a/application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java b/application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java
index 7bd5fb7817..f5bdcb7373 100644
--- a/application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java
+++ b/application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java
@@ -30,12 +30,12 @@ import com.slack.api.model.ConversationType;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
-import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
import org.thingsboard.rule.engine.api.slack.SlackService;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig;
+import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversationType;
import org.thingsboard.server.common.data.util.ThrowingBiFunction;
import org.thingsboard.server.dao.notification.NotificationSettingsService;
@@ -140,10 +140,9 @@ public class DefaultSlackService implements SlackService {
String error = response.getError();
if (error == null) {
error = "unknown error";
- }
- if (error.contains("missing_scope")) {
+ } else if (error.contains("missing_scope")) {
String neededScope = response.getNeeded();
- throw new RuntimeException("Bot token scope '" + neededScope + "' is needed");
+ error = "bot token scope '" + neededScope + "' is needed";
}
throw new RuntimeException("Failed to send message via Slack: " + error);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java
index 77d8c916e1..b646cb7ef6 100644
--- a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java
+++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java
@@ -32,7 +32,8 @@ import org.thingsboard.server.common.data.sync.ie.EntityImportResult;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.queue.util.TbCoreComponent;
-import org.thingsboard.server.service.apiusage.RateLimitService;
+import org.thingsboard.server.service.apiusage.limits.LimitedApi;
+import org.thingsboard.server.service.apiusage.limits.RateLimitService;
import org.thingsboard.server.service.entitiy.TbNotificationEntityService;
import org.thingsboard.server.service.sync.ie.exporting.EntityExportService;
import org.thingsboard.server.service.sync.ie.exporting.impl.BaseEntityExportService;
@@ -72,7 +73,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS
@Override
public , I extends EntityId> EntityExportData exportEntity(EntitiesExportCtx> ctx, I entityId) throws ThingsboardException {
- if (!rateLimitService.checkEntityExportLimit(ctx.getTenantId())) {
+ if (!rateLimitService.checkRateLimit(ctx.getTenantId(), LimitedApi.ENTITY_EXPORT)) {
throw new ThingsboardException("Rate limit for entities export is exceeded", ThingsboardErrorCode.TOO_MANY_REQUESTS);
}
@@ -84,7 +85,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS
@Override
public , I extends EntityId> EntityImportResult importEntity(EntitiesImportCtx ctx, EntityExportData exportData) throws ThingsboardException {
- if (!rateLimitService.checkEntityImportLimit(ctx.getTenantId())) {
+ if (!rateLimitService.checkRateLimit(ctx.getTenantId(), LimitedApi.ENTITY_IMPORT)) {
throw new ThingsboardException("Rate limit for entities import is exceeded", ThingsboardErrorCode.TOO_MANY_REQUESTS);
}
if (exportData.getEntity() == null || exportData.getEntity().getId() == null) {
diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java
index 6adefb709e..5956d2b2dd 100644
--- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java
+++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java
@@ -50,9 +50,10 @@ import org.thingsboard.server.common.stats.TbApiUsageReportClient;
import org.thingsboard.server.dao.alarm.AlarmApiCallResult;
import org.thingsboard.server.dao.alarm.AlarmOperationResult;
import org.thingsboard.server.dao.alarm.AlarmService;
+import org.thingsboard.server.dao.notification.NotificationRuleProcessingService;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.entitiy.alarm.TbAlarmCommentService;
-import org.thingsboard.server.service.notification.rule.NotificationRuleProcessingService;
+import org.thingsboard.server.dao.notification.trigger.AlarmTrigger;
import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
import java.util.Collection;
@@ -234,7 +235,9 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
return TbSubscriptionUtils.toAlarmUpdateProto(tenantId, entityId, alarm);
});
}
- notificationRuleProcessingService.process(tenantId, result);
+ notificationRuleProcessingService.process(tenantId, AlarmTrigger.builder()
+ .alarmUpdate(result)
+ .build());
});
}
@@ -249,7 +252,9 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
return TbSubscriptionUtils.toAlarmDeletedProto(tenantId, entityId, alarm);
});
}
- notificationRuleProcessingService.process(tenantId, result);
+ notificationRuleProcessingService.process(tenantId, AlarmTrigger.builder()
+ .alarmUpdate(result)
+ .build());
});
}
diff --git a/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java b/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java
index 1c7ed35fc9..d9f49ae4d3 100644
--- a/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java
+++ b/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java
@@ -25,8 +25,10 @@ import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.UpdateMessage;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionTrigger;
+import org.thingsboard.server.dao.notification.NotificationRuleProcessingService;
import org.thingsboard.server.queue.util.TbCoreComponent;
-import org.thingsboard.server.service.notification.rule.NotificationRuleProcessingService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@@ -134,7 +136,9 @@ public class DefaultUpdateService implements UpdateService {
version
);
if (updateMessage.isUpdateAvailable() && !updateMessage.equals(prevUpdateMessage)) {
- notificationRuleProcessingService.process(updateMessage);
+ notificationRuleProcessingService.process(TenantId.SYS_TENANT_ID, NewPlatformVersionTrigger.builder()
+ .message(updateMessage)
+ .build());
}
} catch (Exception e) {
log.trace(e.getMessage());
diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationUpdate.java
index beeb46b1ac..d9d8ea5b85 100644
--- a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationUpdate.java
+++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationUpdate.java
@@ -15,6 +15,7 @@
*/
package org.thingsboard.server.service.ws.notification.sub;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@@ -42,6 +43,7 @@ public class NotificationUpdate {
private boolean deleted;
+ @JsonIgnore
public UUID getNotificationId() {
return notificationId != null ? notificationId.getId() :
notification != null ? notification.getUuidId() : null;
diff --git a/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java b/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java
new file mode 100644
index 0000000000..bb2e49bd62
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java
@@ -0,0 +1,114 @@
+/**
+ * Copyright © 2016-2023 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.leshan.core.model.DDFFileParser;
+import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
+import org.eclipse.leshan.core.model.InvalidDDFFileException;
+import org.eclipse.leshan.core.model.ObjectModel;
+import org.thingsboard.server.common.data.ResourceType;
+import org.thingsboard.server.common.data.TbResource;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.lwm2m.LwM2mInstance;
+import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
+import org.thingsboard.server.common.data.lwm2m.LwM2mResourceObserve;
+import org.thingsboard.server.dao.exception.DataValidationException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+
+import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
+import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_SEARCH_TEXT;
+
+@Slf4j
+public class LwM2mObjectModelUtils {
+
+ private static final DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator());
+
+ public static void toLwm2mResource (TbResource resource) throws ThingsboardException {
+ try {
+ List objectModels =
+ ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText());
+ if (!objectModels.isEmpty()) {
+ ObjectModel objectModel = objectModels.get(0);
+
+ String resourceKey = objectModel.id + LWM2M_SEPARATOR_KEY + objectModel.version;
+ String name = objectModel.name;
+ resource.setResourceKey(resourceKey);
+ if (resource.getId() == null) {
+ resource.setTitle(name + " id=" + objectModel.id + " v" + objectModel.version);
+ }
+ resource.setSearchText(resourceKey + LWM2M_SEPARATOR_SEARCH_TEXT + name);
+ } else {
+ throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", resource.getSearchText()));
+ }
+ } catch (InvalidDDFFileException e) {
+ log.error("Failed to parse file {}", resource.getFileName(), e);
+ throw new DataValidationException("Failed to parse file " + resource.getFileName());
+ } catch (IOException e) {
+ throw new ThingsboardException(e, ThingsboardErrorCode.GENERAL);
+ }
+ if (resource.getResourceType().equals(ResourceType.LWM2M_MODEL) && toLwM2mObject(resource, true) == null) {
+ throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", resource.getSearchText()));
+ }
+ }
+
+ public static LwM2mObject toLwM2mObject(TbResource resource, boolean isSave) {
+ try {
+ List objectModels =
+ ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText());
+ if (objectModels.size() == 0) {
+ return null;
+ } else {
+ ObjectModel obj = objectModels.get(0);
+ LwM2mObject lwM2mObject = new LwM2mObject();
+ lwM2mObject.setId(obj.id);
+ lwM2mObject.setKeyId(resource.getResourceKey());
+ lwM2mObject.setName(obj.name);
+ lwM2mObject.setMultiple(obj.multiple);
+ lwM2mObject.setMandatory(obj.mandatory);
+ LwM2mInstance instance = new LwM2mInstance();
+ instance.setId(0);
+ List resources = new ArrayList<>();
+ obj.resources.forEach((k, v) -> {
+ if (isSave) {
+ LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false);
+ resources.add(lwM2MResourceObserve);
+ } else if (v.operations.isReadable()) {
+ LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false);
+ resources.add(lwM2MResourceObserve);
+ }
+ });
+ if (isSave || resources.size() > 0) {
+ instance.setResources(resources.toArray(LwM2mResourceObserve[]::new));
+ lwM2mObject.setInstances(new LwM2mInstance[]{instance});
+ return lwM2mObject;
+ } else {
+ return null;
+ }
+ }
+ } catch (IOException | InvalidDDFFileException e) {
+ log.error("Could not parse the XML of objectModel with name [{}]", resource.getSearchText(), e);
+ return null;
+ }
+ }
+
+}
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index db194cb6e0..d96e50ca7f 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -440,6 +440,9 @@ cache:
notificationRules:
timeToLiveInMinutes: "${CACHE_SPECS_NOTIFICATION_RULES_TTL:1440}"
maxSize: "${CACHE_SPECS_NOTIFICATION_RULES_MAX_SIZE:10000}"
+ notificationSettings:
+ timeToLiveInMinutes: "${CACHE_SPECS_NOTIFICATION_SETTINGS_TTL:10}"
+ maxSize: "${CACHE_SPECS_NOTIFICATION_SETTINGS_MAX_SIZE:1000}"
attributes:
timeToLiveInMinutes: "${CACHE_SPECS_ATTRIBUTES_TTL:1440}"
maxSize: "${CACHE_SPECS_ATTRIBUTES_MAX_SIZE:100000}"
@@ -1216,7 +1219,7 @@ vc:
repositories-folder: "${TB_VC_GIT_REPOSITORIES_FOLDER:${java.io.tmpdir}/repositories}"
notification_system:
- thread_pool_size: "${TB_NOTIFICATION_SYSTEM_THREAD_POOL_SIZE:30}"
+ thread_pool_size: "${TB_NOTIFICATION_SYSTEM_THREAD_POOL_SIZE:10}"
management:
endpoints:
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmCommentControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmCommentControllerTest.java
index 8566d9d6a4..b8c091de05 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmCommentControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmCommentControllerTest.java
@@ -202,7 +202,13 @@ public abstract class BaseAlarmCommentControllerTest extends AbstractControllerT
doDelete("/api/alarm/" + alarm.getId() + "/comment/" + alarmComment.getId())
.andExpect(status().isOk());
- testLogEntityAction(alarm, alarm.getId(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.DELETED_COMMENT, 1, alarmComment);
+ AlarmComment expectedAlarmComment = AlarmComment.builder()
+ .alarmId(alarm.getId())
+ .type(AlarmCommentType.SYSTEM)
+ .comment(JacksonUtil.newObjectNode().put("text", String.format("User %s deleted his comment",
+ CUSTOMER_USER_EMAIL)))
+ .build();
+ testLogEntityAction(alarm, alarm.getId(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.DELETED_COMMENT, 1, expectedAlarmComment);
}
@Test
@@ -215,7 +221,13 @@ public abstract class BaseAlarmCommentControllerTest extends AbstractControllerT
doDelete("/api/alarm/" + alarm.getId() + "/comment/" + alarmComment.getId())
.andExpect(status().isOk());
- testLogEntityAction(alarm, alarm.getId(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.DELETED_COMMENT, 1, alarmComment);
+ AlarmComment expectedAlarmComment = AlarmComment.builder()
+ .alarmId(alarm.getId())
+ .type(AlarmCommentType.SYSTEM)
+ .comment(JacksonUtil.newObjectNode().put("text", String.format("User %s deleted his comment",
+ TENANT_ADMIN_EMAIL)))
+ .build();
+ testLogEntityAction(alarm, alarm.getId(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.DELETED_COMMENT, 1, expectedAlarmComment);
}
@Test
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAssetProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAssetProfileControllerTest.java
index caeaf83c4b..2f7c51c3a2 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseAssetProfileControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseAssetProfileControllerTest.java
@@ -425,6 +425,7 @@ public abstract class BaseAssetProfileControllerTest extends AbstractControllerT
Collections.sort(loadedAssetProfileInfos, assetProfileInfoIdComparator);
List assetProfileInfos = assetProfiles.stream().map(assetProfile -> new AssetProfileInfo(assetProfile.getId(),
+ assetProfile.getTenantId(),
assetProfile.getName(), assetProfile.getImage(), assetProfile.getDefaultDashboardId())).collect(Collectors.toList());
Assert.assertEquals(assetProfileInfos, loadedAssetProfileInfos);
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
index 1546e0814e..4d7c614ddb 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
@@ -546,6 +546,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
Collections.sort(loadedDeviceProfileInfos, deviceProfileInfoIdComparator);
List deviceProfileInfos = deviceProfiles.stream().map(deviceProfile -> new DeviceProfileInfo(deviceProfile.getId(),
+ deviceProfile.getTenantId(),
deviceProfile.getName(), deviceProfile.getImage(), deviceProfile.getDefaultDashboardId(),
deviceProfile.getType(), deviceProfile.getTransportType())).collect(Collectors.toList());
diff --git a/application/src/test/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateServiceTest.java b/application/src/test/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateServiceTest.java
index 2729f8ec7a..d650e619a9 100644
--- a/application/src/test/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateServiceTest.java
+++ b/application/src/test/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateServiceTest.java
@@ -18,31 +18,27 @@ package org.thingsboard.server.service.apiusage;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
+import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.msg.queue.ServiceType;
-import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
import org.thingsboard.server.queue.discovery.PartitionService;
-import org.thingsboard.server.queue.scheduler.SchedulerComponent;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import java.util.UUID;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.BDDMockito.willReturn;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
@RunWith(MockitoJUnitRunner.class)
public class DefaultTbApiUsageStateServiceTest {
@@ -68,11 +64,12 @@ public class DefaultTbApiUsageStateServiceTest {
TenantId tenantId = TenantId.fromUUID(UUID.fromString("00797a3b-7aeb-4b5b-b57a-c2a810d0f112"));
+ @Spy
+ @InjectMocks
DefaultTbApiUsageStateService service;
@Before
public void setUp() {
- service = spy(new DefaultTbApiUsageStateService(clusterService, partitionService, tenantService, tsService, apiUsageStateService, tenantProfileCache, mailService, dbExecutor));
}
@Test
diff --git a/application/src/test/java/org/thingsboard/server/service/entitiy/alarmComment/DefaultTbAlarmCommentServiceTest.java b/application/src/test/java/org/thingsboard/server/service/entitiy/alarmComment/DefaultTbAlarmCommentServiceTest.java
index 485049bfd6..1377b1a934 100644
--- a/application/src/test/java/org/thingsboard/server/service/entitiy/alarmComment/DefaultTbAlarmCommentServiceTest.java
+++ b/application/src/test/java/org/thingsboard/server/service/entitiy/alarmComment/DefaultTbAlarmCommentServiceTest.java
@@ -28,9 +28,11 @@ import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmComment;
+import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AlarmCommentId;
import org.thingsboard.server.common.data.id.AlarmId;
+import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.dao.alarm.AlarmCommentService;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.customer.CustomerService;
@@ -41,6 +43,7 @@ import org.thingsboard.server.service.telemetry.AlarmSubscriptionService;
import java.util.UUID;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
@@ -84,13 +87,28 @@ public class DefaultTbAlarmCommentServiceTest {
}
@Test
- public void testDelete() {
+ public void testDelete() throws ThingsboardException {
var alarmId = new AlarmId(UUID.randomUUID());
- var alarmCommentId = new AlarmCommentId(UUID.randomUUID());
+ var alarmComment = new AlarmComment();
+ alarmComment.setAlarmId(alarmId);
+ alarmComment.setUserId(new UserId(UUID.randomUUID()));
+ alarmComment.setType(AlarmCommentType.OTHER);
- doNothing().when(alarmCommentService).deleteAlarmComment(Mockito.any(), eq(alarmCommentId));
- service.deleteAlarmComment(new Alarm(alarmId), new AlarmComment(alarmCommentId), new User());
+ when(alarmCommentService.saveAlarmComment(Mockito.any(), eq(alarmComment))).thenReturn(alarmComment);
+ service.deleteAlarmComment(new Alarm(alarmId), alarmComment, new User());
verify(notificationEntityService, times(1)).notifyAlarmComment(any(), any(), any(), any());
}
+
+ @Test
+ public void testShouldNotDeleteSystemComment() {
+ var alarmId = new AlarmId(UUID.randomUUID());
+ var alarmComment = new AlarmComment();
+ alarmComment.setAlarmId(alarmId);
+ alarmComment.setType(AlarmCommentType.SYSTEM);
+
+ assertThatThrownBy(() -> service.deleteAlarmComment(new Alarm(alarmId), alarmComment, new User()))
+ .isInstanceOf(ThingsboardException.class)
+ .hasMessageContaining("System comment could not be deleted");
+ }
}
\ No newline at end of file
diff --git a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java
index 7988b5ae2e..7bbd88445f 100644
--- a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java
+++ b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java
@@ -132,7 +132,6 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest
notificationTemplate.setName("Notification template: " + text);
notificationTemplate.setNotificationType(notificationType);
NotificationTemplateConfig config = new NotificationTemplateConfig();
- config.setDefaultTextTemplate(text);
config.setDeliveryMethodsTemplates(new HashMap<>());
for (NotificationDeliveryMethod deliveryMethod : deliveryMethods) {
DeliveryMethodNotificationTemplate deliveryMethodNotificationTemplate;
@@ -157,6 +156,7 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest
throw new IllegalArgumentException("Unsupported delivery method " + deliveryMethod);
}
deliveryMethodNotificationTemplate.setEnabled(true);
+ deliveryMethodNotificationTemplate.setBody(text);
config.getDeliveryMethodsTemplates().put(deliveryMethod, deliveryMethodNotificationTemplate);
}
notificationTemplate.setConfiguration(config);
diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java
index f9c9f2836b..7d318f5522 100644
--- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java
+++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java
@@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.rule.engine.api.NotificationCenter;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.NotificationTargetId;
+import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.Notification;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import org.thingsboard.server.common.data.notification.NotificationRequest;
@@ -339,6 +340,8 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
target1Config.setUsersFilter(userListFilter);
target1.setConfiguration(target1Config);
target1 = saveNotificationTarget(target1);
+ List recipients = new ArrayList<>();
+ recipients.add(tenantAdminUserId);
createDifferentCustomer();
loginTenantAdmin();
@@ -350,6 +353,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
customerUser.setCustomerId(differentCustomerId);
customerUser.setEmail("other-customer-" + i + "@thingsboard.org");
customerUser = createUser(customerUser, "12345678");
+ recipients.add(customerUser.getId());
}
NotificationTarget target2 = new NotificationTarget();
target2.setName("Other customer users");
@@ -367,27 +371,25 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
String requestorEmail = TENANT_ADMIN_EMAIL;
NotificationTemplateConfig templateConfig = new NotificationTemplateConfig();
- templateConfig.setDefaultTextTemplate("Default message for SMS and WEB: ${recipientEmail}");
- templateConfig.setNotificationSubject("Default subject for EMAIL: ${recipientEmail}");
HashMap templates = new HashMap<>();
templateConfig.setDeliveryMethodsTemplates(templates);
notificationTemplate.setConfiguration(templateConfig);
WebDeliveryMethodNotificationTemplate webNotificationTemplate = new WebDeliveryMethodNotificationTemplate();
webNotificationTemplate.setEnabled(true);
- // using default message for web
+ webNotificationTemplate.setBody("Message for WEB: ${recipientEmail}");
webNotificationTemplate.setSubject("Subject for WEB: ${recipientEmail}");
templates.put(NotificationDeliveryMethod.WEB, webNotificationTemplate);
SmsDeliveryMethodNotificationTemplate smsNotificationTemplate = new SmsDeliveryMethodNotificationTemplate();
smsNotificationTemplate.setEnabled(true);
- // using default message for sms
+ smsNotificationTemplate.setBody("Message for SMS: ${recipientEmail}");
templates.put(NotificationDeliveryMethod.SMS, smsNotificationTemplate);
EmailDeliveryMethodNotificationTemplate emailNotificationTemplate = new EmailDeliveryMethodNotificationTemplate();
emailNotificationTemplate.setEnabled(true);
+ emailNotificationTemplate.setSubject("Subject for EMAIL: ${recipientEmail}");
emailNotificationTemplate.setBody("Message for EMAIL: ${recipientEmail}");
- // using default subject for email
templates.put(NotificationDeliveryMethod.EMAIL, emailNotificationTemplate);
SlackDeliveryMethodNotificationTemplate slackNotificationTemplate = new SlackDeliveryMethodNotificationTemplate();
@@ -407,12 +409,13 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
assertThat(preview.getRecipientsCountByTarget().get(target1.getName())).isEqualTo(1);
assertThat(preview.getRecipientsCountByTarget().get(target2.getName())).isEqualTo(customerUsersCount);
assertThat(preview.getTotalRecipientsCount()).isEqualTo(1 + customerUsersCount);
+ assertThat(preview.getRecipientsPreview()).extracting(User::getId).containsAll(recipients);
Map processedTemplates = preview.getProcessedTemplates();
assertThat(processedTemplates.get(NotificationDeliveryMethod.WEB)).asInstanceOf(type(WebDeliveryMethodNotificationTemplate.class))
.satisfies(template -> {
assertThat(template.getBody())
- .startsWith("Default message for SMS and WEB")
+ .startsWith("Message for WEB")
.endsWith(requestorEmail);
assertThat(template.getSubject())
.startsWith("Subject for WEB")
@@ -421,7 +424,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
assertThat(processedTemplates.get(NotificationDeliveryMethod.SMS)).asInstanceOf(type(SmsDeliveryMethodNotificationTemplate.class))
.satisfies(template -> {
assertThat(template.getBody())
- .startsWith("Default message for SMS and WEB")
+ .startsWith("Message for SMS")
.endsWith(requestorEmail);
});
assertThat(processedTemplates.get(NotificationDeliveryMethod.EMAIL)).asInstanceOf(type(EmailDeliveryMethodNotificationTemplate.class))
@@ -430,7 +433,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
.startsWith("Message for EMAIL")
.endsWith(requestorEmail);
assertThat(template.getSubject())
- .startsWith("Default subject for EMAIL")
+ .startsWith("Subject for EMAIL")
.endsWith(requestorEmail);
});
assertThat(processedTemplates.get(NotificationDeliveryMethod.SLACK)).asInstanceOf(type(SlackDeliveryMethodNotificationTemplate.class))
@@ -534,9 +537,9 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
notificationTemplate.setName("Slack notification template");
notificationTemplate.setNotificationType(NotificationType.GENERAL);
NotificationTemplateConfig config = new NotificationTemplateConfig();
- config.setDefaultTextTemplate("To Slack :) ${recipientEmail}");
SlackDeliveryMethodNotificationTemplate slackNotificationTemplate = new SlackDeliveryMethodNotificationTemplate();
slackNotificationTemplate.setEnabled(true);
+ slackNotificationTemplate.setBody("To Slack :) ${recipientEmail}");
config.setDeliveryMethodsTemplates(Map.of(
NotificationDeliveryMethod.SLACK, slackNotificationTemplate
));
@@ -556,7 +559,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
NotificationRequest successfulNotificationRequest = submitNotificationRequest(List.of(notificationTarget.getId()), notificationTemplate.getId(), 0);
await().atMost(2, TimeUnit.SECONDS)
.until(() -> findNotificationRequest(successfulNotificationRequest.getId()).isSent());
- verify(slackService).sendMessage(eq(tenantId), eq(slackToken), eq(conversationId), eq(config.getDefaultTextTemplate()));
+ verify(slackService).sendMessage(eq(tenantId), eq(slackToken), eq(conversationId), eq(slackNotificationTemplate.getBody()));
NotificationRequestStats stats = getStats(successfulNotificationRequest.getId());
assertThat(stats.getSent().get(NotificationDeliveryMethod.SLACK)).hasValue(1);
diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java
index 84b22b5d14..f355d9072c 100644
--- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java
+++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java
@@ -75,6 +75,8 @@ import org.thingsboard.server.common.data.script.ScriptLanguage;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.notification.NotificationRequestService;
+import org.thingsboard.server.dao.notification.NotificationRuleService;
+import org.thingsboard.server.dao.notification.NotificationTemplateService;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.service.telemetry.AlarmSubscriptionService;
@@ -103,6 +105,10 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
private AlarmSubscriptionService alarmSubscriptionService;
@Autowired
private NotificationRequestService notificationRequestService;
+ @Autowired
+ private NotificationRuleService notificationRuleService;
+ @Autowired
+ private NotificationTemplateService notificationTemplateService;
@SpyBean
private AlarmService alarmService;
@@ -110,6 +116,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
@Before
public void beforeEach() throws Exception {
loginTenantAdmin();
+ notificationRuleService.deleteNotificationRulesByTenantId(tenantId);
+ notificationTemplateService.deleteNotificationTemplatesByTenantId(tenantId);
}
@Test
@@ -145,7 +153,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
getWsClient().waitForUpdate(true);
Notification notification = getWsClient().getLastDataUpdate().getUpdate();
- assertThat(notification.getSubject()).isEqualTo("added: DEVICE [" + device.getId() + "]");
+ assertThat(notification.getSubject()).isEqualTo("added: Device [" + device.getId() + "]");
assertThat(notification.getText()).isEqualTo("User: " + TENANT_ADMIN_EMAIL);
@@ -155,7 +163,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
getWsClient().waitForUpdate(true);
notification = getWsClient().getLastDataUpdate().getUpdate();
- assertThat(notification.getSubject()).isEqualTo("updated: DEVICE [" + device.getId() + "]");
+ assertThat(notification.getSubject()).isEqualTo("updated: Device [" + device.getId() + "]");
getWsClient().registerWaitForUpdate();
@@ -163,7 +171,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
getWsClient().waitForUpdate(true);
notification = getWsClient().getLastDataUpdate().getUpdate();
- assertThat(notification.getSubject()).isEqualTo("deleted: DEVICE [" + device.getId() + "]");
+ assertThat(notification.getSubject()).isEqualTo("deleted: Device [" + device.getId() + "]");
}
@Test
@@ -225,7 +233,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
assertThat(actualDelay).isCloseTo(expectedDelay, offset(0.5));
AlarmStatus expectedStatus = AlarmStatus.ACTIVE_UNACK;
- AlarmSeverity expectedSeverity = AlarmSeverity.CRITICAL;
+ String expectedSeverity = AlarmSeverity.CRITICAL.toString().toLowerCase();
assertThat(notification.getSubject()).isEqualTo("Alarm type: " + alarmType + ", status: " + expectedStatus + ", " +
"severity: " + expectedSeverity + ", deviceId: " + device.getId());
@@ -303,7 +311,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
getWsClient().waitForUpdate(true);
Notification notification = getWsClient().getLastDataUpdate().getUpdate();
- assertThat(notification.getSubject()).isEqualTo("CRITICAL alarm '" + alarmType + "' is ACTIVE_UNACK");
+ assertThat(notification.getSubject()).isEqualTo("critical alarm '" + alarmType + "' is ACTIVE_UNACK");
assertThat(notification.getInfo()).asInstanceOf(type(AlarmNotificationInfo.class))
.extracting(AlarmNotificationInfo::getAlarmId).isEqualTo(alarm.getUuidId());
@@ -322,48 +330,6 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
assertThat(findNotificationRequests(EntityType.ALARM).getData()).filteredOn(NotificationRequest::isScheduled).isEmpty();
}
- @Test
- public void testNotificationRuleProcessing_ruleEngineComponentLifecycleEvent_ruleNodeStartError() {
- String subject = "Rule Node '${componentName}' in Rule Chain '${ruleChainName}' failed to start";
- String text = "The error: ${error}";
- NotificationTemplate template = createNotificationTemplate(NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, subject, text, NotificationDeliveryMethod.WEB);
-
- NotificationRule rule = new NotificationRule();
- rule.setName("Rule node start-up failures in my rule chain");
- rule.setTemplateId(template.getId());
- rule.setTriggerType(NotificationRuleTriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT);
-
- RuleChain ruleChain = createEmptyRuleChain("My Rule Chain");
- var triggerConfig = new RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig();
- triggerConfig.setRuleChains(Set.of(ruleChain.getUuidId()));
- triggerConfig.setRuleChainEvents(Set.of(ComponentLifecycleEvent.STARTED));
- triggerConfig.setOnlyRuleChainLifecycleFailures(true);
-
- triggerConfig.setTrackRuleNodeEvents(true);
- triggerConfig.setRuleNodeEvents(Set.of(ComponentLifecycleEvent.STARTED));
- triggerConfig.setOnlyRuleNodeLifecycleFailures(true);
- rule.setTriggerConfig(triggerConfig);
-
- NotificationTarget target = createNotificationTarget(tenantAdminUserId);
- DefaultNotificationRuleRecipientsConfig recipientsConfig = new DefaultNotificationRuleRecipientsConfig();
- recipientsConfig.setTriggerType(NotificationRuleTriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT);
- recipientsConfig.setTargets(List.of(target.getUuidId()));
- rule.setRecipientsConfig(recipientsConfig);
- rule = saveNotificationRule(rule);
-
- getWsClient().subscribeForUnreadNotifications(10).waitForReply(true);
- getWsClient().registerWaitForUpdate();
-
- addRuleNodeWithError(ruleChain.getId(), "My generator");
-
- getWsClient().waitForUpdate(10000, true);
- Notification notification = getWsClient().getLastDataUpdate().getUpdate();
-
- assertThat(notification.getType()).isEqualTo(NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT);
- assertThat(notification.getSubject()).isEqualTo("Rule Node 'My generator' in Rule Chain 'My Rule Chain' failed to start");
- assertThat(notification.getText()).startsWith("The error: Can't compile script");
- }
-
@Test
public void testNotificationRuleInfo() throws Exception {
NotificationDeliveryMethod[] deliveryMethods = {NotificationDeliveryMethod.WEB, NotificationDeliveryMethod.EMAIL};
diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationTemplateApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationTemplateApiTest.java
index 67a7fd8bd4..1b843d61b4 100644
--- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationTemplateApiTest.java
+++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationTemplateApiTest.java
@@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.StringUtils;
import org.junit.Before;
import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.ResultMatcher;
import org.thingsboard.server.common.data.id.IdBased;
@@ -29,6 +30,8 @@ import org.thingsboard.server.common.data.notification.template.NotificationTemp
import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.dao.notification.NotificationRuleService;
+import org.thingsboard.server.dao.notification.NotificationTemplateService;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.List;
@@ -40,9 +43,16 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@DaoSqlTest
public class NotificationTemplateApiTest extends AbstractNotificationApiTest {
+ @Autowired
+ private NotificationTemplateService templateService;
+ @Autowired
+ private NotificationRuleService notificationRuleService;
+
@Before
public void beforeEach() throws Exception {
loginTenantAdmin();
+ notificationRuleService.deleteNotificationRulesByTenantId(tenantId);
+ templateService.deleteNotificationTemplatesByTenantId(tenantId);
}
@Test
@@ -61,8 +71,6 @@ public class NotificationTemplateApiTest extends AbstractNotificationApiTest {
NotificationTemplateConfig config = new NotificationTemplateConfig();
notificationTemplate.setConfiguration(config);
- config.setDefaultTextTemplate("Default text");
- config.setNotificationSubject(null);
EmailDeliveryMethodNotificationTemplate emailTemplate = new EmailDeliveryMethodNotificationTemplate();
emailTemplate.setEnabled(true);
emailTemplate.setBody(null);
@@ -74,14 +82,9 @@ public class NotificationTemplateApiTest extends AbstractNotificationApiTest {
validationError = saveAndGetError(notificationTemplate, status().isBadRequest());
assertThat(validationError)
- .contains("notificationSubject must be")
+ .contains("subject must not be")
+ .contains("body must not be")
.contains("name is malformed");
-
- config.setDefaultTextTemplate(null);
-
- validationError = saveAndGetError(notificationTemplate, status().isBadRequest());
- assertThat(validationError)
- .contains("defaultTextTemplate").contains("must be specified");
}
@Test
diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/connection/AbstractMqttV5ClientSparkplugConnectionTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/connection/AbstractMqttV5ClientSparkplugConnectionTest.java
index 5f9b794739..1a5781afd4 100644
--- a/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/connection/AbstractMqttV5ClientSparkplugConnectionTest.java
+++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/connection/AbstractMqttV5ClientSparkplugConnectionTest.java
@@ -137,6 +137,12 @@ public abstract class AbstractMqttV5ClientSparkplugConnectionTest extends Abstra
}
}
+ /**
+ * OFFLINE_All
+ * @param cntDevices
+ * @throws Exception
+ */
+
protected void processConnectClientWithCorrectAccessTokenWithNDEATH_State_ONLINE_All_Then_OFFLINE_All(int cntDevices) throws Exception {
long ts = calendar.getTimeInMillis();
List devices = connectClientWithCorrectAccessTokenWithNDEATHCreatedDevices(cntDevices, ts);
@@ -166,6 +172,7 @@ public abstract class AbstractMqttV5ClientSparkplugConnectionTest extends Abstra
}
}
+
private boolean findEqualsKeyValueInKvEntrys(List finalFuture, TsKvEntry tsKvEntry) {
for (TsKvEntry kvEntry : finalFuture) {
if (kvEntry.getKey().equals(tsKvEntry.getKey()) && kvEntry.getValue().equals(tsKvEntry.getValue())) {
@@ -174,5 +181,5 @@ public abstract class AbstractMqttV5ClientSparkplugConnectionTest extends Abstra
}
return false;
}
-
}
+
diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/DefaultTbActorSystem.java b/common/actor/src/main/java/org/thingsboard/server/actors/DefaultTbActorSystem.java
index e0b63c84ec..26ef4e8822 100644
--- a/common/actor/src/main/java/org/thingsboard/server/actors/DefaultTbActorSystem.java
+++ b/common/actor/src/main/java/org/thingsboard/server/actors/DefaultTbActorSystem.java
@@ -192,7 +192,7 @@ public class DefaultTbActorSystem implements TbActorSystem {
}
TbActorMailbox mailbox = actors.remove(actorId);
if (mailbox != null) {
- mailbox.destroy();
+ mailbox.destroy(null);
}
}
diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActor.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActor.java
index c042e2bec5..3b6456ab18 100644
--- a/common/actor/src/main/java/org/thingsboard/server/actors/TbActor.java
+++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActor.java
@@ -16,6 +16,7 @@
package org.thingsboard.server.actors;
import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.TbActorStopReason;
public interface TbActor {
@@ -26,7 +27,7 @@ public interface TbActor {
default void init(TbActorCtx ctx) throws TbActorException {
}
- default void destroy() throws TbActorException {
+ default void destroy(TbActorStopReason stopReason, Throwable cause) throws TbActorException {
}
default InitFailureStrategy onInitFailure(int attempt, Throwable t) {
diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java
index bc0d8835ba..587da8f3b1 100644
--- a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java
+++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java
@@ -75,7 +75,7 @@ public final class TbActorMailbox implements TbActorCtx {
if (strategy.isStop() || (settings.getMaxActorInitAttempts() > 0 && attemptIdx > settings.getMaxActorInitAttempts())) {
log.info("[{}] Failed to init actor, attempt {}, going to stop attempts.", selfId, attempt, t);
stopReason = TbActorStopReason.INIT_FAILED;
- destroy();
+ destroy(t.getCause());
} else if (strategy.getRetryDelay() > 0) {
log.info("[{}] Failed to init actor, attempt {}, going to retry in attempts in {}ms", selfId, attempt, strategy.getRetryDelay());
log.debug("[{}] Error", selfId, t);
@@ -142,7 +142,7 @@ public final class TbActorMailbox implements TbActorCtx {
actor.process(msg);
} catch (TbRuleNodeUpdateException updateException) {
stopReason = TbActorStopReason.INIT_FAILED;
- destroy();
+ destroy(updateException.getCause());
} catch (Throwable t) {
log.debug("[{}] Failed to process message: {}", selfId, msg, t);
ProcessFailureStrategy strategy = actor.onProcessFailure(t);
@@ -208,7 +208,7 @@ public final class TbActorMailbox implements TbActorCtx {
}
}
- public void destroy() {
+ public void destroy(Throwable cause) {
if (stopReason == null) {
stopReason = TbActorStopReason.STOPPED;
}
@@ -216,7 +216,7 @@ public final class TbActorMailbox implements TbActorCtx {
dispatcher.getExecutor().execute(() -> {
try {
ready.set(NOT_READY);
- actor.destroy();
+ actor.destroy(stopReason, cause);
highPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason));
normalPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason));
} catch (Throwable t) {
diff --git a/common/actor/src/test/java/org/thingsboard/server/actors/TestRootActor.java b/common/actor/src/test/java/org/thingsboard/server/actors/TestRootActor.java
index e91038d41b..7a79fb4872 100644
--- a/common/actor/src/test/java/org/thingsboard/server/actors/TestRootActor.java
+++ b/common/actor/src/test/java/org/thingsboard/server/actors/TestRootActor.java
@@ -18,6 +18,7 @@ package org.thingsboard.server.actors;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.msg.TbActorMsg;
+import org.thingsboard.server.common.msg.TbActorStopReason;
@Slf4j
public class TestRootActor extends AbstractTbActor {
@@ -60,7 +61,7 @@ public class TestRootActor extends AbstractTbActor {
}
@Override
- public void destroy() {
+ public void destroy(TbActorStopReason stopReason, Throwable cause) {
}
diff --git a/common/cluster-api/src/main/proto/queue.proto b/common/cluster-api/src/main/proto/queue.proto
index 0af667356d..4907d0f015 100644
--- a/common/cluster-api/src/main/proto/queue.proto
+++ b/common/cluster-api/src/main/proto/queue.proto
@@ -197,7 +197,6 @@ message GetOrCreateDeviceFromGatewayRequestMsg {
int64 gatewayIdLSB = 2;
string deviceName = 3;
string deviceType = 4;
- bool sparkplug = 5;
}
message GetOrCreateDeviceFromGatewayResponseMsg {
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentService.java
index 81795c092d..90fbc09fcf 100644
--- a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentService.java
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentService.java
@@ -16,6 +16,7 @@
package org.thingsboard.server.dao.alarm;
import com.google.common.util.concurrent.ListenableFuture;
+import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmCommentInfo;
import org.thingsboard.server.common.data.id.AlarmCommentId;
@@ -27,7 +28,7 @@ import org.thingsboard.server.common.data.page.PageLink;
public interface AlarmCommentService {
AlarmComment createOrUpdateAlarmComment(TenantId tenantId, AlarmComment alarmComment);
- void deleteAlarmComment(TenantId tenantId, AlarmCommentId alarmCommentId);
+ AlarmComment saveAlarmComment(TenantId tenantId, AlarmComment alarmComment);
PageData findAlarmComments(TenantId tenantId, AlarmId alarmId, PageLink pageLink);
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleProcessingService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleProcessingService.java
new file mode 100644
index 0000000000..f144db7a62
--- /dev/null
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleProcessingService.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright © 2016-2023 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.dao.notification;
+
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger;
+import org.thingsboard.server.common.msg.TbMsg;
+
+public interface NotificationRuleProcessingService {
+
+ void process(TenantId tenantId, NotificationRuleTrigger trigger);
+
+ void process(TenantId tenantId, TbMsg ruleEngineMsg);
+
+}
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java
index 7852a64f0d..b67edd123c 100644
--- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java
@@ -19,6 +19,7 @@ import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig;
import org.thingsboard.server.common.data.page.PageData;
@@ -34,6 +35,8 @@ public interface NotificationTargetService {
PageData findNotificationTargetsByTenantId(TenantId tenantId, PageLink pageLink);
+ PageData findNotificationTargetsByTenantIdAndSupportedNotificationType(TenantId tenantId, NotificationType notificationType, PageLink pageLink);
+
List findNotificationTargetsByTenantIdAndIds(TenantId tenantId, List ids);
PageData findRecipientsForNotificationTarget(TenantId tenantId, CustomerId customerId, NotificationTargetId targetId, PageLink pageLink);
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/AlarmTrigger.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/AlarmTrigger.java
new file mode 100644
index 0000000000..309e48a3fc
--- /dev/null
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/AlarmTrigger.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright © 2016-2023 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.dao.notification.trigger;
+
+import lombok.Builder;
+import lombok.Data;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger;
+import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
+import org.thingsboard.server.dao.alarm.AlarmApiCallResult;
+
+@Data
+@Builder
+public class AlarmTrigger implements NotificationRuleTrigger {
+
+ private final AlarmApiCallResult alarmUpdate;
+
+ @Override
+ public NotificationRuleTriggerType getType() {
+ return NotificationRuleTriggerType.ALARM;
+ }
+
+ @Override
+ public EntityId getOriginatorEntityId() {
+ return alarmUpdate.getAlarm().getId();
+ }
+
+}
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/EntitiesLimitTrigger.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/EntitiesLimitTrigger.java
new file mode 100644
index 0000000000..01255d2808
--- /dev/null
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/EntitiesLimitTrigger.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright © 2016-2023 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.dao.notification.trigger;
+
+import lombok.Builder;
+import lombok.Data;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger;
+import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
+
+@Data
+@Builder
+public class EntitiesLimitTrigger implements NotificationRuleTrigger {
+
+ private final EntityType entityType;
+ private final long currentCount;
+ private final long limit;
+
+ @Override
+ public NotificationRuleTriggerType getType() {
+ return NotificationRuleTriggerType.ENTITIES_LIMIT;
+ }
+
+ @Override
+ public EntityId getOriginatorEntityId() {
+ return TenantId.SYS_TENANT_ID;
+ }
+
+}
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/RuleEngineComponentLifecycleEventTrigger.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/RuleEngineComponentLifecycleEventTrigger.java
new file mode 100644
index 0000000000..4ba438a385
--- /dev/null
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/RuleEngineComponentLifecycleEventTrigger.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright © 2016-2023 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.dao.notification.trigger;
+
+import lombok.Builder;
+import lombok.Data;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger;
+import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+
+@Data
+@Builder
+public class RuleEngineComponentLifecycleEventTrigger implements NotificationRuleTrigger {
+
+ private final RuleChainId ruleChainId;
+ private final String ruleChainName;
+ private final EntityId componentId;
+ private final String componentName;
+ private final ComponentLifecycleEvent eventType;
+ private final Throwable error;
+
+ @Override
+ public NotificationRuleTriggerType getType() {
+ return NotificationRuleTriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT;
+ }
+
+ @Override
+ public EntityId getOriginatorEntityId() {
+ return componentId;
+ }
+
+}
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/RuleEngineMsgTrigger.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/RuleEngineMsgTrigger.java
new file mode 100644
index 0000000000..6e186c90c3
--- /dev/null
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/RuleEngineMsgTrigger.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright © 2016-2023 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.dao.notification.trigger;
+
+import lombok.Builder;
+import lombok.Data;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger;
+import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
+import org.thingsboard.server.common.msg.TbMsg;
+
+@Data
+@Builder
+public class RuleEngineMsgTrigger implements NotificationRuleTrigger {
+
+ private final TbMsg msg;
+ private final NotificationRuleTriggerType triggerType;
+
+ @Override
+ public NotificationRuleTriggerType getType() {
+ return triggerType;
+ }
+
+ @Override
+ public EntityId getOriginatorEntityId() {
+ return msg.getOriginator();
+ }
+
+}
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileService.java
index 02345e77fd..d1b5bcc1cb 100644
--- a/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileService.java
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileService.java
@@ -23,6 +23,9 @@ import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.entity.EntityDaoService;
+import java.util.List;
+import java.util.UUID;
+
public interface TenantProfileService extends EntityDaoService {
TenantProfile findTenantProfileById(TenantId tenantId, TenantProfileId tenantProfileId);
@@ -47,4 +50,6 @@ public interface TenantProfileService extends EntityDaoService {
void deleteTenantProfiles(TenantId tenantId);
+ List findTenantProfilesByIds(TenantId tenantId, UUID[] ids);
+
}
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java
index 389f16c1ee..cb713bf20a 100644
--- a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java
@@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.id.UserCredentialsId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
@@ -26,6 +27,8 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.entity.EntityDaoService;
+import java.util.List;
+
public interface UserService extends EntityDaoService {
User findUserById(TenantId tenantId, UserId userId);
@@ -60,7 +63,13 @@ public interface UserService extends EntityDaoService {
PageData findTenantAdmins(TenantId tenantId, PageLink pageLink);
- PageData findAllUsers(TenantId tenantId, PageLink pageLink);
+ PageData findAllTenantAdmins(PageLink pageLink);
+
+ PageData findTenantAdminsByTenantsIds(List tenantsIds, PageLink pageLink);
+
+ PageData findTenantAdminsByTenantProfilesIds(List tenantProfilesIds, PageLink pageLink);
+
+ PageData findAllUsers(PageLink pageLink);
void deleteTenantAdmins(TenantId tenantId);
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java
index f59ea8d5d0..eedf925d61 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java
@@ -30,6 +30,7 @@ public class CacheConstants {
public static final String TENANTS_EXIST_CACHE = "tenantsExist";
public static final String DEVICE_PROFILE_CACHE = "deviceProfiles";
public static final String NOTIFICATION_RULES_CACHE = "notificationRules";
+ public static final String NOTIFICATION_SETTINGS_CACHE = "notificationSettings";
public static final String ASSET_PROFILE_CACHE = "assetProfiles";
public static final String ATTRIBUTES_CACHE = "attributes";
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileInfo.java
index fd68bbfae4..295b9f6b99 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileInfo.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileInfo.java
@@ -24,6 +24,7 @@ import lombok.Value;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
+import org.thingsboard.server.common.data.id.TenantId;
import java.util.UUID;
@@ -41,22 +42,28 @@ public class DeviceProfileInfo extends EntityInfo {
@ApiModelProperty(position = 6, value = "Type of the transport used to connect the device. Default transport supports HTTP, CoAP and MQTT.")
private final DeviceTransportType transportType;
+ @ApiModelProperty(position = 7, value = "Tenant id.")
+ private final TenantId tenantId;
+
@JsonCreator
public DeviceProfileInfo(@JsonProperty("id") EntityId id,
+ @JsonProperty("tenantId") TenantId tenantId,
@JsonProperty("name") String name,
@JsonProperty("image") String image,
@JsonProperty("defaultDashboardId") DashboardId defaultDashboardId,
@JsonProperty("type") DeviceProfileType type,
@JsonProperty("transportType") DeviceTransportType transportType) {
super(id, name);
+ this.tenantId = tenantId;
this.image = image;
this.defaultDashboardId = defaultDashboardId;
this.type = type;
this.transportType = transportType;
}
- public DeviceProfileInfo(UUID uuid, String name, String image, UUID defaultDashboardId, DeviceProfileType type, DeviceTransportType transportType) {
+ public DeviceProfileInfo(UUID uuid, UUID tenantId, String name, String image, UUID defaultDashboardId, DeviceProfileType type, DeviceTransportType transportType) {
super(EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE_PROFILE, uuid), name);
+ this.tenantId = new TenantId(tenantId);
this.image = image;
this.defaultDashboardId = defaultDashboardId != null ? new DashboardId(defaultDashboardId) : null;
this.type = type;
@@ -64,7 +71,7 @@ public class DeviceProfileInfo extends EntityInfo {
}
public DeviceProfileInfo(DeviceProfile profile) {
- this(profile.getId(), profile.getName(), profile.getImage(), profile.getDefaultDashboardId(),
+ this(profile.getId(), profile.getTenantId(), profile.getName(), profile.getImage(), profile.getDefaultDashboardId(),
profile.getType(), profile.getTransportType());
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
index 472d73adb6..a4b4c1e5fb 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
@@ -15,6 +15,9 @@
*/
package org.thingsboard.server.common.data;
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
/**
* @author Andrew Shvayka
*/
@@ -44,5 +47,9 @@ public enum EntityType {
NOTIFICATION_TEMPLATE,
NOTIFICATION_REQUEST,
NOTIFICATION,
- NOTIFICATION_RULE
+ NOTIFICATION_RULE;
+
+ @Getter
+ private final String normalName = StringUtils.capitalize(name().toLowerCase().replaceAll("_", " "));
+
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfileInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfileInfo.java
index 0b6fa3a612..e590c4c094 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfileInfo.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfileInfo.java
@@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
+import org.thingsboard.server.common.data.id.TenantId;
import java.util.UUID;
@@ -39,24 +40,30 @@ public class AssetProfileInfo extends EntityInfo {
@ApiModelProperty(position = 4, value = "Reference to the dashboard. Used in the mobile application to open the default dashboard when user navigates to asset details.")
private final DashboardId defaultDashboardId;
+ @ApiModelProperty(position = 5, value = "Tenant id.")
+ private final TenantId tenantId;
+
@JsonCreator
public AssetProfileInfo(@JsonProperty("id") EntityId id,
- @JsonProperty("name") String name,
- @JsonProperty("image") String image,
- @JsonProperty("defaultDashboardId") DashboardId defaultDashboardId) {
+ @JsonProperty("tenantId") TenantId tenantId,
+ @JsonProperty("name") String name,
+ @JsonProperty("image") String image,
+ @JsonProperty("defaultDashboardId") DashboardId defaultDashboardId) {
super(id, name);
+ this.tenantId = tenantId;
this.image = image;
this.defaultDashboardId = defaultDashboardId;
}
- public AssetProfileInfo(UUID uuid, String name, String image, UUID defaultDashboardId) {
+ public AssetProfileInfo(UUID uuid, UUID tenantId, String name, String image, UUID defaultDashboardId) {
super(EntityIdFactory.getByTypeAndUuid(EntityType.ASSET_PROFILE, uuid), name);
+ this.tenantId = new TenantId(tenantId);
this.image = image;
this.defaultDashboardId = defaultDashboardId != null ? new DashboardId(defaultDashboardId) : null;
}
public AssetProfileInfo(AssetProfile profile) {
- this(profile.getId(), profile.getName(), profile.getImage(), profile.getDefaultDashboardId());
+ this(profile.getId(), profile.getTenantId(), profile.getName(), profile.getImage(), profile.getDefaultDashboardId());
}
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java
index 4d7d95a18c..6f5fb09927 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java
@@ -45,9 +45,15 @@ public final class TenantId extends UUIDBased implements EntityId {
super(id);
}
+ @JsonIgnore
+ public boolean isSysTenantId() {
+ return this.equals(SYS_TENANT_ID);
+ }
+
@ApiModelProperty(position = 2, required = true, value = "string", example = "TENANT", allowableValues = "TENANT")
@Override
public EntityType getEntityType() {
return EntityType.TENANT;
}
+
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestPreview.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestPreview.java
index 82319196f5..2e3ff0fd99 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestPreview.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestPreview.java
@@ -16,8 +16,10 @@
package org.thingsboard.server.common.data.notification;
import lombok.Data;
+import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
+import java.util.Collection;
import java.util.Map;
@Data
@@ -26,5 +28,6 @@ public class NotificationRequestPreview {
private Map processedTemplates;
private int totalRecipientsCount;
private Map recipientsCountByTarget;
+ private Collection recipientsPreview;
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java
index 815bf4e3db..46de48840e 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java
@@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.UserId;
import java.util.Map;
import java.util.UUID;
@@ -35,9 +36,11 @@ import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf;
@Builder
public class AlarmAssignmentNotificationInfo implements RuleOriginatedNotificationInfo {
+ private String action;
private String assigneeFirstName;
private String assigneeLastName;
private String assigneeEmail;
+ private UserId assigneeId;
private String userName;
private String alarmType;
@@ -51,15 +54,17 @@ public class AlarmAssignmentNotificationInfo implements RuleOriginatedNotificati
@Override
public Map getTemplateData() {
return mapOf(
+ "action", action,
"assigneeFirstName", assigneeFirstName,
"assigneeLastName", assigneeLastName,
"assigneeEmail", assigneeEmail,
+ "assigneeId", assigneeId != null ? assigneeId.toString() : null,
"userName", userName,
"alarmType", alarmType,
"alarmId", alarmId.toString(),
- "alarmSeverity", alarmSeverity.toString(),
+ "alarmSeverity", alarmSeverity.name().toLowerCase(),
"alarmStatus", alarmStatus.toString(),
- "alarmOriginatorEntityType", alarmOriginator.getEntityType().toString(),
+ "alarmOriginatorEntityType", alarmOriginator.getEntityType().getNormalName(),
"alarmOriginatorId", alarmOriginator.getId().toString(),
"alarmOriginatorName", alarmOriginatorName
);
@@ -70,6 +75,11 @@ public class AlarmAssignmentNotificationInfo implements RuleOriginatedNotificati
return alarmCustomerId;
}
+ @Override
+ public UserId getTargetUserId() {
+ return assigneeId;
+ }
+
@Override
public EntityId getStateEntityId() {
return alarmOriginator;
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java
index 3fce737194..59026a5666 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java
@@ -36,7 +36,9 @@ import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf;
public class AlarmCommentNotificationInfo implements RuleOriginatedNotificationInfo {
private String comment;
+ private String action;
private String userName;
+
private String alarmType;
private UUID alarmId;
private EntityId alarmOriginator;
@@ -49,12 +51,13 @@ public class AlarmCommentNotificationInfo implements RuleOriginatedNotificationI
public Map getTemplateData() {
return mapOf(
"comment", comment,
+ "action", action,
"userName", userName,
"alarmType", alarmType,
"alarmId", alarmId.toString(),
- "alarmSeverity", alarmSeverity.toString(),
+ "alarmSeverity", alarmSeverity.name().toLowerCase(),
"alarmStatus", alarmStatus.toString(),
- "alarmOriginatorEntityType", alarmOriginator.getEntityType().toString(),
+ "alarmOriginatorEntityType", alarmOriginator.getEntityType().getNormalName(),
"alarmOriginatorId", alarmOriginator.getId().toString(),
"alarmOriginatorName", alarmOriginatorName
);
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java
index 502d6069dd..ed179ad4a4 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java
@@ -36,6 +36,7 @@ import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf;
public class AlarmNotificationInfo implements RuleOriginatedNotificationInfo {
private String alarmType;
+ private String action;
private UUID alarmId;
private EntityId alarmOriginator;
private String alarmOriginatorName;
@@ -45,13 +46,13 @@ public class AlarmNotificationInfo implements RuleOriginatedNotificationInfo {
@Override
public Map getTemplateData() {
- // TODO: readable status change
return mapOf(
"alarmType", alarmType,
+ "action", action,
"alarmId", alarmId.toString(),
- "alarmSeverity", alarmSeverity.toString(),
+ "alarmSeverity", alarmSeverity.name().toLowerCase(),
"alarmStatus", alarmStatus.toString(),
- "alarmOriginatorEntityType", alarmOriginator.getEntityType().toString(),
+ "alarmOriginatorEntityType", alarmOriginator.getEntityType().getNormalName(),
"alarmOriginatorName", alarmOriginatorName,
"alarmOriginatorId", alarmOriginator.getId().toString()
);
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/DeviceInactivityNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/DeviceInactivityNotificationInfo.java
index 6a4647ac62..bb77068cdf 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/DeviceInactivityNotificationInfo.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/DeviceInactivityNotificationInfo.java
@@ -15,7 +15,6 @@
*/
package org.thingsboard.server.common.data.notification.info;
-import com.google.common.base.Strings;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java
index add9f9d5d7..2c277492e3 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java
@@ -32,14 +32,18 @@ import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf;
public class EntitiesLimitNotificationInfo implements NotificationInfo {
private EntityType entityType;
- private int threshold;
+ private long currentCount;
+ private long limit;
+ private int percents;
@Override
public Map getTemplateData() {
- // FIXME: readable entity type name, e.g. 'Devices'
return mapOf(
- "entityType", entityType.name(),
- "threshold", String.valueOf(threshold)
+ "entityType", entityType.getNormalName(),
+ "currentCount", String.valueOf(currentCount),
+ "limit", String.valueOf(limit),
+ "percents", String.valueOf(percents)
);
}
+
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java
index b7e49183ea..825148e52c 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java
@@ -49,7 +49,7 @@ public class EntityActionNotificationInfo implements RuleOriginatedNotificationI
@Override
public Map getTemplateData() {
return mapOf(
- "entityType", entityId.getEntityType().name(),
+ "entityType", entityId.getEntityType().getNormalName(),
"entityId", entityId.toString(),
"entityName", entityName,
"actionType", actionType.name().toLowerCase(),
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineComponentLifecycleEventNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineComponentLifecycleEventNotificationInfo.java
index 45c1c689ac..9baac13c79 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineComponentLifecycleEventNotificationInfo.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineComponentLifecycleEventNotificationInfo.java
@@ -37,6 +37,7 @@ public class RuleEngineComponentLifecycleEventNotificationInfo implements Notifi
private String ruleChainName;
private EntityId componentId;
private String componentName;
+ private String action;
private ComponentLifecycleEvent eventType;
private String error;
@@ -46,9 +47,10 @@ public class RuleEngineComponentLifecycleEventNotificationInfo implements Notifi
"ruleChainId", ruleChainId.toString(),
"ruleChainName", ruleChainName,
"componentId", componentId.toString(),
- "componentType", componentId.getEntityType().name(),
+ "componentType", componentId.getEntityType().getNormalName(),
"componentName", componentName,
- "eventType", eventType.name(),
+ "action", action,
+ "eventType", eventType.name().toLowerCase(),
"error", error
);
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineOriginatedNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineOriginatedNotificationInfo.java
index 924028da36..8a40b3c1b0 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineOriginatedNotificationInfo.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineOriginatedNotificationInfo.java
@@ -37,7 +37,7 @@ public class RuleEngineOriginatedNotificationInfo implements NotificationInfo {
@Override
public Map getTemplateData() {
Map templateData = new HashMap<>(msgMetadata);
- templateData.put("originatorType", msgOriginator.getEntityType().toString());
+ templateData.put("originatorType", msgOriginator.getEntityType().getNormalName());
templateData.put("originatorId", msgOriginator.getId().toString());
templateData.put("msgType", msgType);
return templateData;
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleOriginatedNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleOriginatedNotificationInfo.java
index 6ab22dcf05..91320d7571 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleOriginatedNotificationInfo.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleOriginatedNotificationInfo.java
@@ -16,9 +16,14 @@
package org.thingsboard.server.common.data.notification.info;
import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.UserId;
public interface RuleOriginatedNotificationInfo extends NotificationInfo {
CustomerId getOriginatorEntityCustomerId();
+ default UserId getTargetUserId() {
+ return null;
+ }
+
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java
index 949ab40ff7..529bc9a3ab 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java
@@ -27,19 +27,22 @@ import org.thingsboard.server.common.data.id.NotificationTemplateId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
+import org.thingsboard.server.common.data.validation.NoXss;
import javax.validation.Valid;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
+import java.io.Serializable;
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
-public class NotificationRule extends BaseData implements HasTenantId, HasName {
+public class NotificationRule extends BaseData implements HasTenantId, HasName, Serializable {
private TenantId tenantId;
@NotBlank
+ @NoXss
private String name;
@NotNull
private NotificationTemplateId templateId;
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleConfig.java
index 68683afcee..73522c7108 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleConfig.java
@@ -17,8 +17,10 @@ package org.thingsboard.server.common.data.notification.rule;
import lombok.Data;
+import java.io.Serializable;
+
@Data
-public class NotificationRuleConfig {
+public class NotificationRuleConfig implements Serializable {
private String description;
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java
index f2c6ca2863..d31ec098ca 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java
@@ -24,6 +24,7 @@ import lombok.Data;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
import javax.validation.constraints.NotNull;
+import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -34,7 +35,7 @@ import java.util.UUID;
@Type(name = "ALARM", value = EscalatedNotificationRuleRecipientsConfig.class),
})
@Data
-public abstract class NotificationRuleRecipientsConfig {
+public abstract class NotificationRuleRecipientsConfig implements Serializable {
@NotNull
private NotificationRuleTriggerType triggerType;
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentNotificationRuleTriggerConfig.java
index 88e2c5fd76..8bcb6387e2 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentNotificationRuleTriggerConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentNotificationRuleTriggerConfig.java
@@ -19,6 +19,7 @@ import lombok.Data;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
+import javax.validation.constraints.NotEmpty;
import java.util.Set;
@Data
@@ -27,11 +28,16 @@ public class AlarmAssignmentNotificationRuleTriggerConfig implements Notificatio
private Set alarmTypes;
private Set alarmSeverities;
private Set alarmStatuses;
- private boolean notifyOnUnassign;
+ @NotEmpty
+ private Set notifyOn;
@Override
public NotificationRuleTriggerType getTriggerType() {
return NotificationRuleTriggerType.ALARM_ASSIGNMENT;
}
+ public enum Action {
+ ASSIGNED, UNASSIGNED
+ }
+
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmCommentNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmCommentNotificationRuleTriggerConfig.java
index 3cfdd7ae3c..237dce5282 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmCommentNotificationRuleTriggerConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmCommentNotificationRuleTriggerConfig.java
@@ -28,6 +28,7 @@ public class AlarmCommentNotificationRuleTriggerConfig implements NotificationRu
private Set alarmSeverities;
private Set alarmStatuses;
private boolean onlyUserComments;
+ private boolean notifyOnCommentUpdate;
@Override
public NotificationRuleTriggerType getTriggerType() {
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionNotificationRuleTriggerConfig.java
index 230be3b9d5..5473288f94 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionNotificationRuleTriggerConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionNotificationRuleTriggerConfig.java
@@ -20,6 +20,8 @@ import lombok.Data;
@Data
public class NewPlatformVersionNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig {
+ // TODO: don't forget to create default notification configs
+
@Override
public NotificationRuleTriggerType getTriggerType() {
return NotificationRuleTriggerType.NEW_PLATFORM_VERSION;
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionTrigger.java
new file mode 100644
index 0000000000..2959c22b8d
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionTrigger.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright © 2016-2023 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.notification.rule.trigger;
+
+import lombok.Builder;
+import lombok.Data;
+import org.thingsboard.server.common.data.UpdateMessage;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.TenantId;
+
+@Data
+@Builder
+public class NewPlatformVersionTrigger implements NotificationRuleTrigger {
+
+ private final UpdateMessage message;
+
+ @Override
+ public NotificationRuleTriggerType getType() {
+ return NotificationRuleTriggerType.NEW_PLATFORM_VERSION;
+ }
+
+ @Override
+ public EntityId getOriginatorEntityId() {
+ return TenantId.SYS_TENANT_ID;
+ }
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTrigger.java
new file mode 100644
index 0000000000..a2ac4c9d47
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTrigger.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright © 2016-2023 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.notification.rule.trigger;
+
+import org.thingsboard.server.common.data.id.EntityId;
+
+public interface NotificationRuleTrigger {
+
+ NotificationRuleTriggerType getType();
+
+ EntityId getOriginatorEntityId();
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerConfig.java
index c60c72b8de..f67a144bed 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerConfig.java
@@ -20,6 +20,8 @@ import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import java.io.Serializable;
+
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "triggerType")
@JsonSubTypes({
@@ -32,7 +34,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
@Type(value = NewPlatformVersionNotificationRuleTriggerConfig.class, name = "NEW_PLATFORM_VERSION"),
@Type(value = EntitiesLimitNotificationRuleTriggerConfig.class, name = "ENTITIES_LIMIT")
})
-public interface NotificationRuleTriggerConfig {
+public interface NotificationRuleTriggerConfig extends Serializable {
NotificationRuleTriggerType getTriggerType();
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java
index 6f271fbda7..130f6d4f61 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java
@@ -24,6 +24,8 @@ import java.util.UUID;
@Data
public class RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig {
+ private Set ruleChains; // if empty - all rule chains
+
private Set ruleChainEvents; // available options: STARTED, UPDATED, STOPPED. if empty - all events
private boolean onlyRuleChainLifecycleFailures;
@@ -31,8 +33,6 @@ public class RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig impl
private Set ruleNodeEvents; // available options: STARTED, UPDATED, STOPPED. if empty - all events
private boolean onlyRuleNodeLifecycleFailures;
- private Set ruleChains; // if empty - all rule chains
-
@Override
public NotificationRuleTriggerType getTriggerType() {
return NotificationRuleTriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT;
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java
index ea3451338d..962eba6d06 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java
@@ -22,12 +22,14 @@ import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
+import java.io.Serializable;
+
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "method")
@JsonSubTypes({
@Type(name = "SLACK", value = SlackNotificationDeliveryMethodConfig.class)
})
-public interface NotificationDeliveryMethodConfig {
+public interface NotificationDeliveryMethodConfig extends Serializable {
@JsonIgnore
NotificationDeliveryMethod getMethod();
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java
index face4ca017..4ed6cfc30d 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java
@@ -20,10 +20,11 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
+import java.io.Serializable;
import java.util.Map;
@Data
-public class NotificationSettings {
+public class NotificationSettings implements Serializable {
@NotNull
@Valid
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java
index 30bfa67183..2333062f65 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data;
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig;
+import org.thingsboard.server.common.data.validation.NoXss;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@@ -33,6 +34,7 @@ import org.thingsboard.server.common.data.notification.targets.slack.SlackNotifi
@Data
public abstract class NotificationTargetConfig {
+ @NoXss
private String description;
@JsonIgnore
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AffectedUserFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AffectedUserFilter.java
new file mode 100644
index 0000000000..26f0d0295e
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AffectedUserFilter.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright © 2016-2023 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.notification.targets.platform;
+
+import lombok.Data;
+
+@Data
+public class AffectedUserFilter implements UsersFilter {
+
+ @Override
+ public UsersFilterType getType() {
+ return UsersFilterType.AFFECTED_USER;
+ }
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/TenantAdministratorsFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/TenantAdministratorsFilter.java
new file mode 100644
index 0000000000..1176a59fa8
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/TenantAdministratorsFilter.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright © 2016-2023 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.notification.targets.platform;
+
+import lombok.Data;
+
+import java.util.Set;
+import java.util.UUID;
+
+@Data
+public class TenantAdministratorsFilter implements UsersFilter {
+
+ private Set tenantsIds;
+ private Set tenantProfilesIds;
+
+ @Override
+ public UsersFilterType getType() {
+ return UsersFilterType.TENANT_ADMINISTRATORS;
+ }
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilter.java
index 0a49d93d40..62773f6473 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilter.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilter.java
@@ -26,8 +26,10 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonSubTypes({
@Type(value = UserListFilter.class, name = "USER_LIST"),
@Type(value = CustomerUsersFilter.class, name = "CUSTOMER_USERS"),
+ @Type(value = TenantAdministratorsFilter.class, name = "TENANT_ADMINISTRATORS"),
@Type(value = AllUsersFilter.class, name = "ALL_USERS"),
- @Type(value = OriginatorEntityOwnerUsersFilter.class, name = "ORIGINATOR_ENTITY_OWNER_USERS") // for usage in notification rules
+ @Type(value = OriginatorEntityOwnerUsersFilter.class, name = "ORIGINATOR_ENTITY_OWNER_USERS"),
+ @Type(value = AffectedUserFilter.class, name = "AFFECTED_USER")
})
public interface UsersFilter {
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java
index 86e1ec7f87..10a4d3c34f 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java
@@ -15,9 +15,22 @@
*/
package org.thingsboard.server.common.data.notification.targets.platform;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
public enum UsersFilterType {
+
USER_LIST,
CUSTOMER_USERS,
+ TENANT_ADMINISTRATORS,
ALL_USERS,
- ORIGINATOR_ENTITY_OWNER_USERS
+ ORIGINATOR_ENTITY_OWNER_USERS(true),
+ AFFECTED_USER(true);
+
+ private boolean forRules;
+
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java
index ba098304d6..9a3d0775e9 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java
@@ -22,8 +22,12 @@ import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data;
import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
+import javax.validation.constraints.AssertTrue;
+import javax.validation.constraints.NotEmpty;
+
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "method")
@JsonSubTypes({
@@ -37,6 +41,7 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho
public abstract class DeliveryMethodNotificationTemplate {
private boolean enabled;
+ @NotEmpty
private String body;
public DeliveryMethodNotificationTemplate(DeliveryMethodNotificationTemplate other) {
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java
index ab86c82023..70c991481b 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java
@@ -21,12 +21,15 @@ import lombok.NoArgsConstructor;
import lombok.ToString;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
+import javax.validation.constraints.NotEmpty;
+
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class EmailDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject {
+ @NotEmpty
private String subject;
public EmailDeliveryMethodNotificationTemplate(EmailDeliveryMethodNotificationTemplate other) {
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java
index eaebe37c0b..1dcb4aec0c 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java
@@ -15,41 +15,18 @@
*/
package org.thingsboard.server.common.data.notification.template;
-import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
-import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import javax.validation.Valid;
-import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotEmpty;
import java.util.Map;
@Data
public class NotificationTemplateConfig {
- private String notificationSubject;
- private String defaultTextTemplate;
@Valid
@NotEmpty
private Map deliveryMethodsTemplates;
- @JsonIgnore
- @AssertTrue(message = "defaultTextTemplate and notificationSubject must be specified if one absent for delivery method")
- public boolean isValid() {
- if (deliveryMethodsTemplates == null) return true;
- for (DeliveryMethodNotificationTemplate template : deliveryMethodsTemplates.values()) {
- if (StringUtils.isEmpty(template.getBody()) && StringUtils.isEmpty(defaultTextTemplate)) {
- return false;
- }
- if (template instanceof HasSubject) {
- String subject = ((HasSubject) template).getSubject();
- if (StringUtils.isEmpty(subject) && StringUtils.isEmpty(notificationSubject)) {
- return false;
- }
- }
- }
- return true;
- }
-
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java
index 3b9b852eff..8740e3a403 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java
@@ -22,12 +22,15 @@ import lombok.NoArgsConstructor;
import lombok.ToString;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
+import javax.validation.constraints.NotEmpty;
+
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class WebDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject {
+ @NotEmpty
private String subject;
private JsonNode additionalConfig;
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java
index 8790017b7d..030587f50f 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java
@@ -47,6 +47,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
private String tenantEntityExportRateLimit;
private String tenantEntityImportRateLimit;
+ private String tenantNotificationRequestsRateLimit;
private long maxTransportMessages;
private long maxTransportDataPoints;
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
index e58e2c5d89..69f5f78475 100644
--- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
@@ -399,7 +399,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
case NBIRTH:
case NCMD:
case NDATA:
- sparkplugSessionHandler.onAttributesTelemetryProto(msgId, sparkplugBProtoNode, deviceSessionCtx.getDeviceInfo().getDeviceName(), sparkplugTopic);
+ sparkplugSessionHandler.onAttributesTelemetryProto(msgId, sparkplugBProtoNode, sparkplugTopic);
break;
default:
}
@@ -410,7 +410,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
case DBIRTH:
case DCMD:
case DDATA:
- sparkplugSessionHandler.onAttributesTelemetryProto(msgId, sparkplugBProtoDevice, sparkplugTopic.getDeviceId(), sparkplugTopic);
+ sparkplugSessionHandler.onAttributesTelemetryProto(msgId, sparkplugBProtoDevice, sparkplugTopic);
break;
case DDEATH:
sparkplugSessionHandler.onDeviceDisconnect(mqttMsg, sparkplugTopic.getDeviceId());
@@ -1067,8 +1067,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
if (sparkplugTopicNode != null) {
SparkplugBProto.Payload sparkplugBProtoNode = SparkplugBProto.Payload.parseFrom(connectMessage.payload().willMessageInBytes());
sparkplugSessionHandler = new SparkplugNodeSessionHandler(this, deviceSessionCtx, sessionId, sparkplugTopicNode);
- sparkplugSessionHandler.onAttributesTelemetryProto(0, sparkplugBProtoNode,
- deviceSessionCtx.getDeviceInfo().getDeviceName(), sparkplugTopicNode);
+ sparkplugSessionHandler.onAttributesTelemetryProto(0, sparkplugBProtoNode, sparkplugTopicNode);
sessionMetaData.setOverwriteActivityTime(true);
} else {
log.trace("[{}][{}] Failed to fetch sparkplugDevice connect: sparkplugTopicName without SparkplugMessageType.NDEATH.", sessionId, deviceSessionCtx.getDeviceInfo().getDeviceName());
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java
index 2bb59dc9e8..ab0b7be5db 100644
--- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java
@@ -253,7 +253,6 @@ public abstract class AbstractGatewaySessionHandler() {
@Override
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/SparkplugNodeSessionHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/SparkplugNodeSessionHandler.java
index fc480fa882..9b1e3e664e 100644
--- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/SparkplugNodeSessionHandler.java
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/SparkplugNodeSessionHandler.java
@@ -101,7 +101,8 @@ public class SparkplugNodeSessionHandler extends AbstractGatewaySessionHandler contextListenableFuture;
@@ -113,7 +114,7 @@ public class SparkplugNodeSessionHandler extends AbstractGatewaySessionHandler deviceCtx = onDeviceConnectProto(deviceName);
+ ListenableFuture deviceCtx = onDeviceConnectProto(topic);
contextListenableFuture = Futures.transform(deviceCtx, ctx -> {
if (topic.isType(DBIRTH)) {
sendSparkplugStateOnTelemetry(ctx.getSessionInfo(), deviceName, ONLINE,
@@ -218,10 +219,10 @@ public class SparkplugNodeSessionHandler extends AbstractGatewaySessionHandler onDeviceConnectProto(String deviceName) throws ThingsboardException {
+ private ListenableFuture onDeviceConnectProto(SparkplugTopic topic) throws ThingsboardException {
try {
String deviceType = this.gateway.getDeviceType() + "-node";
- return onDeviceConnect(deviceName, deviceType);
+ return onDeviceConnect(topic.getNodeDeviceName(), deviceType);
} catch (RuntimeException e) {
log.error("Failed Sparkplug Device connect proto!", e);
throw new ThingsboardException(e, ThingsboardErrorCode.BAD_REQUEST_PARAMS);
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/sparkplug/SparkplugTopic.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/sparkplug/SparkplugTopic.java
index be721f11bb..2fc49e2b69 100644
--- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/sparkplug/SparkplugTopic.java
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/sparkplug/SparkplugTopic.java
@@ -161,4 +161,9 @@ public class SparkplugTopic {
public boolean isNode() {
return this.deviceId == null;
}
+
+ public String getNodeDeviceName() {
+ return isNode() ? edgeNodeId : deviceId;
+ }
}
+
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java
index aefae8778d..6c6cc18355 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java
@@ -22,12 +22,16 @@ import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import org.thingsboard.common.util.JacksonUtil;
+import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmCommentInfo;
import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.id.AlarmCommentId;
import org.thingsboard.server.common.data.id.AlarmId;
+import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.entity.AbstractEntityService;
@@ -39,7 +43,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
@Service
@Slf4j
-public class BaseAlarmCommentService extends AbstractEntityService implements AlarmCommentService{
+public class BaseAlarmCommentService extends AbstractEntityService implements AlarmCommentService {
@Autowired
private AlarmCommentDao alarmCommentDao;
@@ -58,9 +62,10 @@ public class BaseAlarmCommentService extends AbstractEntityService implements Al
}
@Override
- public void deleteAlarmComment(TenantId tenantId, AlarmCommentId alarmCommentId) {
- log.debug("Deleting Alarm Comment with id: {}", alarmCommentId);
- alarmCommentDao.deleteAlarmComment(tenantId, alarmCommentId);
+ public AlarmComment saveAlarmComment(TenantId tenantId, AlarmComment alarmComment) {
+ log.debug("Deleting Alarm Comment: {}", alarmComment);
+ alarmCommentDataValidator.validate(alarmComment, c -> tenantId);
+ return alarmCommentDao.save(tenantId, alarmComment);
}
@Override
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java
index f34941521a..c464d06857 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java
@@ -293,7 +293,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService deliveryMethods;
- NotificationTemplateConfig templateConfig = fromJson(this.templateConfig, NotificationTemplateConfig.class);
- String templateName = this.templateName;
- if (templateConfig == null && request.getTemplate() != null) {
- templateConfig = request.getTemplate().getConfiguration();
- }
- if (templateConfig != null) {
- deliveryMethods = templateConfig.getDeliveryMethodsTemplates().entrySet().stream()
- .filter(entry -> entry.getValue().isEnabled())
- .map(Map.Entry::getKey).collect(Collectors.toList());
- } else if (request.getStats() != null) {
+ if (request.getStats() != null) {
Set methods = new HashSet<>(request.getStats().getSent().keySet());
methods.addAll(request.getStats().getErrors().keySet());
deliveryMethods = new ArrayList<>(methods);
} else {
- deliveryMethods = Collections.emptyList();
+ NotificationTemplateConfig templateConfig = fromJson(this.templateConfig, NotificationTemplateConfig.class);
+ if (templateConfig == null && request.getTemplate() != null) {
+ templateConfig = request.getTemplate().getConfiguration();
+ }
+ if (templateConfig != null) {
+ deliveryMethods = templateConfig.getDeliveryMethodsTemplates().entrySet().stream()
+ .filter(entry -> entry.getValue().isEnabled())
+ .map(Map.Entry::getKey).collect(Collectors.toList());
+ } else {
+ deliveryMethods = Collections.emptyList();
+ }
}
return new NotificationRequestInfo(request, templateName, deliveryMethods);
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java
index cf34f6fcfb..20a32efbe0 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationService.java
@@ -18,6 +18,9 @@ package org.thingsboard.server.dao.notification;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.NotificationId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
@@ -26,12 +29,15 @@ import org.thingsboard.server.common.data.notification.NotificationStatus;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder;
+import org.thingsboard.server.dao.entity.EntityDaoService;
import org.thingsboard.server.dao.sql.query.EntityKeyMapping;
+import java.util.Optional;
+
@Service
@Slf4j
@RequiredArgsConstructor
-public class DefaultNotificationService implements NotificationService {
+public class DefaultNotificationService implements NotificationService, EntityDaoService {
private final NotificationDao notificationDao;
@@ -81,4 +87,14 @@ public class DefaultNotificationService implements NotificationService {
return notificationDao.deleteByIdAndRecipientId(tenantId, recipientId, notificationId);
}
+ @Override
+ public Optional> findEntity(TenantId tenantId, EntityId entityId) {
+ return Optional.ofNullable(findNotificationById(tenantId, new NotificationId(entityId.getId())));
+ }
+
+ @Override
+ public EntityType getEntityType() {
+ return EntityType.NOTIFICATION;
+ }
+
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java
index 37a225c002..1088347397 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java
@@ -15,21 +15,59 @@
*/
package org.thingsboard.server.dao.notification;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.AdminSettings;
+import org.thingsboard.server.common.data.CacheConstants;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
+import org.thingsboard.server.common.data.id.NotificationTargetId;
+import org.thingsboard.server.common.data.id.NotificationTemplateId;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
+import org.thingsboard.server.common.data.notification.NotificationType;
+import org.thingsboard.server.common.data.notification.rule.DefaultNotificationRuleRecipientsConfig;
+import org.thingsboard.server.common.data.notification.rule.EscalatedNotificationRuleRecipientsConfig;
+import org.thingsboard.server.common.data.notification.rule.NotificationRule;
+import org.thingsboard.server.common.data.notification.rule.NotificationRuleConfig;
+import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentNotificationRuleTriggerConfig;
+import org.thingsboard.server.common.data.notification.rule.trigger.AlarmCommentNotificationRuleTriggerConfig;
+import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig;
+import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig.AlarmAction;
+import org.thingsboard.server.common.data.notification.rule.trigger.DeviceInactivityNotificationRuleTriggerConfig;
+import org.thingsboard.server.common.data.notification.rule.trigger.EntitiesLimitNotificationRuleTriggerConfig;
+import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionNotificationRuleTriggerConfig;
+import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig;
+import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
+import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
+import org.thingsboard.server.common.data.notification.targets.platform.AffectedUserFilter;
import org.thingsboard.server.common.data.notification.targets.platform.AllUsersFilter;
import org.thingsboard.server.common.data.notification.targets.platform.OriginatorEntityOwnerUsersFilter;
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
+import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter;
+import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter;
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
+import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig;
+import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import java.util.Collections;
+import java.util.List;
+import java.util.Map;
import java.util.Optional;
+import java.util.Set;
+
+import static org.thingsboard.common.util.JacksonUtil.newObjectNode;
+import static org.thingsboard.server.dao.DaoUtil.toUUIDs;
@Service
@RequiredArgsConstructor
@@ -42,6 +80,7 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS
private static final String SETTINGS_KEY = "notifications";
+ @CacheEvict(cacheNames = CacheConstants.NOTIFICATION_SETTINGS_CACHE, key = "#tenantId")
@Override
public void saveNotificationSettings(TenantId tenantId, NotificationSettings settings) {
AdminSettings adminSettings = Optional.ofNullable(adminSettingsService.findAdminSettingsByTenantIdAndKey(tenantId, SETTINGS_KEY))
@@ -55,6 +94,7 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS
adminSettingsService.saveAdminSettings(tenantId, adminSettings);
}
+ @Cacheable(cacheNames = CacheConstants.NOTIFICATION_SETTINGS_CACHE, key = "#tenantId")
@Override
public NotificationSettings findNotificationSettings(TenantId tenantId) {
return Optional.ofNullable(adminSettingsService.findAdminSettingsByTenantIdAndKey(tenantId, SETTINGS_KEY))
@@ -66,39 +106,195 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS
});
}
+ @Transactional(propagation = Propagation.NOT_SUPPORTED) // so that parent transaction is not aborted on method failure
@Override
public void createDefaultNotificationConfigs(TenantId tenantId) {
- if (tenantId.equals(TenantId.SYS_TENANT_ID)) {
- // TODO
+ NotificationTarget allUsers = createTarget(tenantId, "All users", new AllUsersFilter(),
+ tenantId.isSysTenantId() ? "All platform users" : "All users in scope of the tenant");
+ NotificationTarget tenantAdmins = createTarget(tenantId, "Tenant administrators", new TenantAdministratorsFilter(),
+ tenantId.isSysTenantId() ? "All tenant administrators" : "Tenant administrators");
+
+ createTemplate(tenantId, "Maintenance work notification", NotificationType.GENERAL,
+ "Infrastructure maintenance",
+ "Maintenance work is scheduled for tomorrow (7:00 a.m. - 9:00 a.m. UTC)");
+
+ if (tenantId.isSysTenantId()) {
+ NotificationTemplate entitiesLimitNotificationTemplate = createTemplate(tenantId, "Entities limit notification", NotificationType.ENTITIES_LIMIT,
+ "${entityType}s limit will be reached soon",
+ "${entityType}s usage: ${currentCount}/${limit} (${percents}%)");
+ EntitiesLimitNotificationRuleTriggerConfig entitiesLimitRuleTriggerConfig = new EntitiesLimitNotificationRuleTriggerConfig();
+ entitiesLimitRuleTriggerConfig.setEntityTypes(null);
+ entitiesLimitRuleTriggerConfig.setThreshold(0.8f);
+ createRule(tenantId, "Entities limit", entitiesLimitNotificationTemplate.getId(), entitiesLimitRuleTriggerConfig,
+ List.of(tenantAdmins.getId()), "Send notification to tenant admins when count of entities of some type reached 80% threshold of the limit");
return;
}
- NotificationTarget allUsersTarget = new NotificationTarget();
- allUsersTarget.setTenantId(tenantId);
- allUsersTarget.setName("All users");
+ NotificationTarget originatorEntityOwnerUsers = createTarget(tenantId, "Users of rule trigger entity's owner", new OriginatorEntityOwnerUsersFilter(),
+ "Customer users in case trigger entity (e.g. alarm) has customer, tenant admins otherwise");
+ NotificationTarget affectedUser = createTarget(tenantId, "Affected user", new AffectedUserFilter(),
+ "If rule trigger is an action that affects some user (e.g. alarm assigned to user) - this user");
+
+ NotificationTemplate alarmNotificationTemplate = createTemplate(tenantId, "Alarm notification", NotificationType.ALARM,
+ "Alarm '${alarmType}' - ${action}",
+ "Severity: ${alarmSeverity}, originator: ${alarmOriginatorEntityType} '${alarmOriginatorName}'",
+ "notifications", null, null);
+ AlarmNotificationRuleTriggerConfig alarmRuleTriggerConfig = new AlarmNotificationRuleTriggerConfig();
+ alarmRuleTriggerConfig.setAlarmTypes(null);
+ alarmRuleTriggerConfig.setAlarmSeverities(null);
+ alarmRuleTriggerConfig.setNotifyOn(Set.of(AlarmAction.CREATED, AlarmAction.SEVERITY_CHANGED, AlarmAction.ACKNOWLEDGED, AlarmAction.CLEARED));
+ createRule(tenantId, "Alarm", alarmNotificationTemplate.getId(), alarmRuleTriggerConfig,
+ List.of(originatorEntityOwnerUsers.getId()), "Send notification to tenant admins or customer users " +
+ "when any alarm is created, updated or cleared");
+
+ NotificationTemplate deviceActionNotificationTemplate = createTemplate(tenantId, "Device action notification", NotificationType.ENTITY_ACTION,
+ "${entityType} was ${actionType}",
+ "${entityType} '${entityName}' was ${actionType} by user ${originatorUserName}",
+ "info", "Go to Device", "/devices/${entityId}");
+ EntityActionNotificationRuleTriggerConfig deviceActionRuleTriggerConfig = new EntityActionNotificationRuleTriggerConfig();
+ deviceActionRuleTriggerConfig.setEntityType(EntityType.DEVICE);
+ deviceActionRuleTriggerConfig.setCreated(true);
+ deviceActionRuleTriggerConfig.setUpdated(false);
+ deviceActionRuleTriggerConfig.setDeleted(true);
+ createRule(tenantId, "Device created or deleted", deviceActionNotificationTemplate.getId(), deviceActionRuleTriggerConfig,
+ List.of(originatorEntityOwnerUsers.getId()), "Send notification to tenant admins or customer users " +
+ "when device is created or deleted");
+
+ NotificationTemplate deviceInactivityNotificationTemplate = createTemplate(tenantId, "Device inactivity notification", NotificationType.DEVICE_INACTIVITY,
+ "Device '${deviceName}' inactive",
+ "Device '${deviceName}' with type '${deviceType}' became inactive",
+ "info", "Go to Device", "/devices/${deviceId}");
+ DeviceInactivityNotificationRuleTriggerConfig deviceInactivityRuleTriggerConfig = new DeviceInactivityNotificationRuleTriggerConfig();
+ deviceInactivityRuleTriggerConfig.setDevices(null);
+ deviceInactivityRuleTriggerConfig.setDeviceProfiles(null);
+ createRule(tenantId, "Device became inactive", deviceInactivityNotificationTemplate.getId(), deviceInactivityRuleTriggerConfig,
+ List.of(originatorEntityOwnerUsers.getId()), "Send notification to tenant admins or customer users " +
+ "when any device became inactive");
+
+ NotificationTemplate alarmCommentNotificationTemplate = createTemplate(tenantId, "Alarm comment notification", NotificationType.ALARM_COMMENT,
+ "Comment on '${alarmType}' alarm",
+ "${userName} ${action} comment: ${comment}",
+ "people", null, null);
+ AlarmCommentNotificationRuleTriggerConfig alarmCommentRuleTriggerConfig = new AlarmCommentNotificationRuleTriggerConfig();
+ alarmCommentRuleTriggerConfig.setAlarmTypes(null);
+ alarmCommentRuleTriggerConfig.setAlarmSeverities(null);
+ alarmCommentRuleTriggerConfig.setAlarmStatuses(Set.of(AlarmSearchStatus.ACTIVE));
+ alarmCommentRuleTriggerConfig.setOnlyUserComments(true);
+ alarmCommentRuleTriggerConfig.setNotifyOnCommentUpdate(false);
+ createRule(tenantId, "Comment on active alarm", alarmCommentNotificationTemplate.getId(), alarmCommentRuleTriggerConfig,
+ List.of(originatorEntityOwnerUsers.getId()), "Send notification to tenant admins or customer users " +
+ "when comment is added by user on active alarm");
+
+ NotificationTemplate alarmAssignedNotificationTemplate = createTemplate(tenantId, "Alarm assigned notification", NotificationType.ALARM_ASSIGNMENT,
+ "Alarm '${alarmType}' (${alarmSeverity}) was assigned to user",
+ "${userName} assigned alarm on ${alarmOriginatorEntityType} '${alarmOriginatorName}' to ${assigneeEmail}",
+ "person", null, null);
+ AlarmAssignmentNotificationRuleTriggerConfig alarmAssignmentRuleTriggerConfig = new AlarmAssignmentNotificationRuleTriggerConfig();
+ alarmAssignmentRuleTriggerConfig.setAlarmTypes(null);
+ alarmAssignmentRuleTriggerConfig.setAlarmSeverities(null);
+ alarmAssignmentRuleTriggerConfig.setAlarmStatuses(null);
+ alarmAssignmentRuleTriggerConfig.setNotifyOn(Set.of(AlarmAssignmentNotificationRuleTriggerConfig.Action.ASSIGNED));
+ createRule(tenantId, "Alarm assignment", alarmAssignedNotificationTemplate.getId(), alarmAssignmentRuleTriggerConfig,
+ List.of(affectedUser.getId()), "Send notification to user when any alarm was assigned to him");
+
+ NotificationTemplate ruleEngineComponentLifecycleFailureNotificationTemplate = createTemplate(tenantId, "Rule chain/node lifecycle failure notification", NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT,
+ "${componentType} '${componentName}' failed to ${action}",
+ "Rule chain '${ruleChainName}' - ${action} failure:
${error}",
+ "warning", "Go to Rule chain", "/features/ruleChains/${ruleChainId}");
+ RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig ruleEngineComponentLifecycleEventRuleTriggerConfig = new RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig();
+ ruleEngineComponentLifecycleEventRuleTriggerConfig.setRuleChains(null);
+ ruleEngineComponentLifecycleEventRuleTriggerConfig.setRuleChainEvents(Set.of(ComponentLifecycleEvent.STARTED, ComponentLifecycleEvent.UPDATED, ComponentLifecycleEvent.STOPPED));
+ ruleEngineComponentLifecycleEventRuleTriggerConfig.setOnlyRuleChainLifecycleFailures(true);
+ ruleEngineComponentLifecycleEventRuleTriggerConfig.setTrackRuleNodeEvents(true);
+ ruleEngineComponentLifecycleEventRuleTriggerConfig.setRuleNodeEvents(Set.of(ComponentLifecycleEvent.STARTED, ComponentLifecycleEvent.UPDATED, ComponentLifecycleEvent.STOPPED));
+ ruleEngineComponentLifecycleEventRuleTriggerConfig.setOnlyRuleNodeLifecycleFailures(true);
+ createRule(tenantId, "Rule node initialization failure", ruleEngineComponentLifecycleFailureNotificationTemplate.getId(),
+ ruleEngineComponentLifecycleEventRuleTriggerConfig, List.of(tenantAdmins.getId()),
+ "Send notification to tenant admins when any Rule chain or Rule node failed to start, update or stop");
+ }
+
+ private NotificationTarget createTarget(TenantId tenantId, String name, UsersFilter filter, String description) {
+ NotificationTarget target = new NotificationTarget();
+ target.setTenantId(tenantId);
+ target.setName(name);
+
PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig();
- targetConfig.setUsersFilter(new AllUsersFilter());
- targetConfig.setDescription("All users in scope of tenant");
- allUsersTarget.setConfiguration(targetConfig);
- allUsersTarget = notificationTargetService.saveNotificationTarget(tenantId, allUsersTarget);
-
- NotificationTarget originatorEntityOwnerUsers = new NotificationTarget();
- originatorEntityOwnerUsers.setTenantId(tenantId);
- originatorEntityOwnerUsers.setName("Users of rule trigger entity's owner");
- targetConfig.setUsersFilter(new OriginatorEntityOwnerUsersFilter());
- targetConfig.setDescription("For usage with notification rules. For example, if alarm trigger type is chosen, " +
- "notifications will be sent to alarm owner's users, e.g. it's customer's users");
- originatorEntityOwnerUsers.setConfiguration(targetConfig);
- originatorEntityOwnerUsers = notificationTargetService.saveNotificationTarget(tenantId, originatorEntityOwnerUsers);
-
- NotificationTemplate alarmNotificationTemplate = new NotificationTemplate();
-
- /*
- * TODO:
- * rule chain start failure
- * alarm
- * */
+ targetConfig.setUsersFilter(filter);
+ targetConfig.setDescription(description);
+ target.setConfiguration(targetConfig);
+ return notificationTargetService.saveNotificationTarget(tenantId, target);
+ }
+
+ private NotificationTemplate createTemplate(TenantId tenantId, String name, NotificationType notificationType,
+ String subjectTemplate, String textTemplate) {
+ return createTemplate(tenantId, name, notificationType, subjectTemplate, textTemplate, null, null, null);
+ }
+
+ private NotificationTemplate createTemplate(TenantId tenantId, String name, NotificationType notificationType,
+ String subjectTemplate, String textTemplate,
+ String icon, String button, String link) {
+ NotificationTemplate template = new NotificationTemplate();
+ template.setTenantId(tenantId);
+ template.setName(name);
+ template.setNotificationType(notificationType);
+ NotificationTemplateConfig templateConfig = new NotificationTemplateConfig();
+ WebDeliveryMethodNotificationTemplate webTemplate = new WebDeliveryMethodNotificationTemplate();
+ webTemplate.setSubject(subjectTemplate);
+ webTemplate.setBody(textTemplate);
+ ObjectNode additionalConfig = newObjectNode();
+ ObjectNode iconConfig = newObjectNode();
+ additionalConfig.set("icon", iconConfig);
+ ObjectNode buttonConfig = newObjectNode();
+ additionalConfig.set("actionButtonConfig", buttonConfig);
+ if (icon != null) {
+ iconConfig.put("enabled", true)
+ .put("icon", icon)
+ .put("color", "#757575");
+ } else {
+ iconConfig.put("enabled", false);
+ }
+ if (button != null) {
+ buttonConfig.put("enabled", true)
+ .put("text", button)
+ .put("linkType", "LINK")
+ .put("link", link);
+ } else {
+ buttonConfig.put("enabled", false);
+ }
+ webTemplate.setAdditionalConfig(additionalConfig);
+ webTemplate.setEnabled(true);
+ templateConfig.setDeliveryMethodsTemplates(Map.of(
+ NotificationDeliveryMethod.WEB, webTemplate
+ ));
+ template.setConfiguration(templateConfig);
+ return notificationTemplateService.saveNotificationTemplate(tenantId, template);
+ }
+
+ private NotificationRule createRule(TenantId tenantId, String name, NotificationTemplateId templateId,
+ NotificationRuleTriggerConfig triggerConfig, List targets,
+ String description) {
+ NotificationRule rule = new NotificationRule();
+ rule.setTenantId(tenantId);
+ rule.setName(name);
+ rule.setTemplateId(templateId);
+ rule.setTriggerType(triggerConfig.getTriggerType());
+ rule.setTriggerConfig(triggerConfig);
+ if (rule.getTriggerType() == NotificationRuleTriggerType.ALARM) {
+ EscalatedNotificationRuleRecipientsConfig recipientsConfig = new EscalatedNotificationRuleRecipientsConfig();
+ recipientsConfig.setTriggerType(rule.getTriggerType());
+ recipientsConfig.setEscalationTable(Map.of(0, toUUIDs(targets)));
+ rule.setRecipientsConfig(recipientsConfig);
+ } else {
+ DefaultNotificationRuleRecipientsConfig recipientsConfig = new DefaultNotificationRuleRecipientsConfig();
+ recipientsConfig.setTriggerType(rule.getTriggerType());
+ recipientsConfig.setTargets(toUUIDs(targets));
+ rule.setRecipientsConfig(recipientsConfig);
+ }
+ NotificationRuleConfig additionalConfig = new NotificationRuleConfig();
+ additionalConfig.setDescription(description);
+ rule.setAdditionalConfig(additionalConfig);
+ return notificationRuleService.saveNotificationRule(tenantId, rule);
}
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java
index e1089b703a..13fa0cffd4 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java
@@ -25,13 +25,15 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.NotificationRequestStatus;
+import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter;
-import org.thingsboard.server.common.data.notification.targets.platform.OriginatorEntityOwnerUsersFilter;
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
+import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter;
import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter;
import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter;
import org.thingsboard.server.common.data.page.PageData;
@@ -46,6 +48,8 @@ import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
+import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
+
@Service
@Slf4j
@RequiredArgsConstructor
@@ -78,6 +82,11 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl
return notificationTargetDao.findByTenantIdAndPageLink(tenantId, pageLink);
}
+ @Override
+ public PageData findNotificationTargetsByTenantIdAndSupportedNotificationType(TenantId tenantId, NotificationType notificationType, PageLink pageLink) {
+ return notificationTargetDao.findByTenantIdAndSupportedNotificationTypeAndPageLink(tenantId, notificationType, pageLink);
+ }
+
@Override
public List findNotificationTargetsByTenantIdAndIds(TenantId tenantId, List ids) {
return notificationTargetDao.findByTenantIdAndIds(tenantId, ids);
@@ -117,19 +126,34 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl
CustomerUsersFilter filter = (CustomerUsersFilter) usersFilter;
return userService.findCustomerUsers(tenantId, new CustomerId(filter.getCustomerId()), pageLink);
}
+ case TENANT_ADMINISTRATORS: {
+ TenantAdministratorsFilter filter = (TenantAdministratorsFilter) usersFilter;
+ if (!tenantId.equals(TenantId.SYS_TENANT_ID)) {
+ return userService.findTenantAdmins(tenantId, pageLink);
+ } else {
+ if (isNotEmpty(filter.getTenantsIds())) {
+ return userService.findTenantAdminsByTenantsIds(filter.getTenantsIds().stream()
+ .map(TenantId::fromUUID).collect(Collectors.toList()), pageLink);
+ } else if (isNotEmpty(filter.getTenantProfilesIds())) {
+ return userService.findTenantAdminsByTenantProfilesIds(filter.getTenantProfilesIds().stream()
+ .map(TenantProfileId::new).collect(Collectors.toList()), pageLink);
+ } else {
+ return userService.findAllTenantAdmins(pageLink);
+ }
+ }
+ }
case ALL_USERS: {
if (!tenantId.equals(TenantId.SYS_TENANT_ID)) {
return userService.findUsersByTenantId(tenantId, pageLink);
} else {
- return userService.findAllUsers(TenantId.SYS_TENANT_ID, pageLink);
+ return userService.findAllUsers(pageLink);
}
}
case ORIGINATOR_ENTITY_OWNER_USERS: {
- OriginatorEntityOwnerUsersFilter filter = (OriginatorEntityOwnerUsersFilter) usersFilter;
if (customerId != null && !customerId.isNullUid()) {
return userService.findCustomerUsers(tenantId, customerId, pageLink);
} else {
- return userService.findTenantAdmins(tenantId, pageLink); // TODO: or should we send to all users within tenant?
+ return userService.findTenantAdmins(tenantId, pageLink);
}
}
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java
index b3b0b6939c..55c4b7f77c 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.notification;
import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@@ -28,6 +29,8 @@ public interface NotificationTargetDao extends Dao {
PageData findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink);
+ PageData findByTenantIdAndSupportedNotificationTypeAndPageLink(TenantId tenantId, NotificationType notificationType, PageLink pageLink);
+
List findByTenantIdAndIds(TenantId tenantId, List ids);
void removeByTenantId(TenantId tenantId);
diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java
index 0a75e07bd1..65ab189081 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java
@@ -34,8 +34,6 @@ import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import static org.apache.commons.lang3.StringUtils.capitalize;
-
@Slf4j
public abstract class DataValidator> {
private static final Pattern EMAIL_PATTERN =
@@ -106,7 +104,7 @@ public abstract class DataValidator> {
protected void validateNumberOfEntitiesPerTenant(TenantId tenantId,
EntityType entityType) {
if (!apiLimitService.checkEntitiesLimit(tenantId, entityType)) {
- throw new DataValidationException(String.format("%ss limit reached", capitalize(entityType.name().toLowerCase().replaceAll("_", " "))));
+ throw new DataValidationException(entityType.getNormalName() + "s limit reached");
}
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java
index 8b9a6aef9d..58bf9f3f0b 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java
@@ -28,7 +28,7 @@ import java.util.UUID;
public interface AssetProfileRepository extends JpaRepository, ExportableEntityRepository {
- @Query("SELECT new org.thingsboard.server.common.data.asset.AssetProfileInfo(a.id, a.name, a.image, a.defaultDashboardId) " +
+ @Query("SELECT new org.thingsboard.server.common.data.asset.AssetProfileInfo(a.id, a.tenantId, a.name, a.image, a.defaultDashboardId) " +
"FROM AssetProfileEntity a " +
"WHERE a.id = :assetProfileId")
AssetProfileInfo findAssetProfileInfoById(@Param("assetProfileId") UUID assetProfileId);
@@ -39,7 +39,7 @@ public interface AssetProfileRepository extends JpaRepository findAssetProfileInfos(@Param("tenantId") UUID tenantId,
@@ -50,7 +50,7 @@ public interface AssetProfileRepository extends JpaRepository, ExportableEntityRepository {
- @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.image, d.defaultDashboardId, d.type, d.transportType) " +
+ @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.tenantId, d.name, d.image, d.defaultDashboardId, d.type, d.transportType) " +
"FROM DeviceProfileEntity d " +
"WHERE d.id = :deviceProfileId")
DeviceProfileInfo findDeviceProfileInfoById(@Param("deviceProfileId") UUID deviceProfileId);
@@ -40,14 +40,14 @@ public interface DeviceProfileRepository extends JpaRepository findDeviceProfileInfos(@Param("tenantId") UUID tenantId,
@Param("textSearch") String textSearch,
Pageable pageable);
- @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.image, d.defaultDashboardId, d.type, d.transportType) " +
+ @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.tenantId, d.name, d.image, d.defaultDashboardId, d.type, d.transportType) " +
"FROM DeviceProfileEntity d WHERE " +
"d.tenantId = :tenantId AND d.transportType = :transportType AND LOWER(d.searchText) LIKE LOWER(CONCAT('%', :textSearch, '%'))")
Page findDeviceProfileInfos(@Param("tenantId") UUID tenantId,
@@ -59,7 +59,7 @@ public interface DeviceProfileRepository extends JpaRepository findUnreadByRecipientIdAndPageLink(TenantId tenantId, UserId recipientId, PageLink pageLink) {
- return DaoUtil.toPageData(notificationRepository.findByRecipientIdAndStatusNot(recipientId.getId(), NotificationStatus.READ, DaoUtil.toPageable(pageLink)));
+ return DaoUtil.toPageData(notificationRepository.findByRecipientIdAndStatusNot(recipientId.getId(), NotificationStatus.READ,
+ Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink)));
}
@Override
public PageData findByRecipientIdAndPageLink(TenantId tenantId, UserId recipientId, PageLink pageLink) {
- return DaoUtil.toPageData(notificationRepository.findByRecipientId(recipientId.getId(), DaoUtil.toPageable(pageLink)));
+ return DaoUtil.toPageData(notificationRepository.findByRecipientId(recipientId.getId(),
+ Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink)));
}
@Override
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java
index c557b9c93a..53b8318d3f 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java
@@ -41,6 +41,7 @@ import org.thingsboard.server.dao.sql.JpaAbstractDao;
import org.thingsboard.server.dao.util.SqlDao;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -60,7 +61,10 @@ public class JpaNotificationRequestDao extends JpaAbstractDao findInfosByTenantIdAndOriginatorTypeAndPageLink(TenantId tenantId, EntityType originatorType, PageLink pageLink) {
return DaoUtil.pageToPageData(notificationRequestRepository.findInfosByTenantIdAndOriginatorEntityTypeAndSearchText(tenantId.getId(),
- originatorType, Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))).mapData(NotificationRequestInfoEntity::toData);
+ originatorType, Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink, Map.of(
+ "templateName", "t.name"
+ ))))
+ .mapData(NotificationRequestInfoEntity::toData);
}
@Override
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java
index ab6f263aed..46910f119f 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java
@@ -36,6 +36,7 @@ import org.thingsboard.server.dao.sql.JpaAbstractDao;
import org.thingsboard.server.dao.util.SqlDao;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
@Component
@@ -54,7 +55,10 @@ public class JpaNotificationRuleDao extends JpaAbstractDao findInfosByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) {
return DaoUtil.pageToPageData(notificationRuleRepository.findInfosByTenantIdAndSearchText(tenantId.getId(),
- Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))).mapData(NotificationRuleInfoEntity::toData);
+ Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink, Map.of(
+ "templateName", "t.name"
+ ))))
+ .mapData(NotificationRuleInfoEntity::toData);
}
@Override
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java
index 01869ee4d2..8e4f3af97c 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java
@@ -22,8 +22,9 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.data.id.UUIDBased;
+import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
+import org.thingsboard.server.common.data.notification.targets.platform.UsersFilterType;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.DaoUtil;
@@ -32,12 +33,11 @@ import org.thingsboard.server.dao.notification.NotificationTargetDao;
import org.thingsboard.server.dao.sql.JpaAbstractDao;
import org.thingsboard.server.dao.util.SqlDao;
+import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
-import static org.thingsboard.server.dao.DaoUtil.getId;
-
@Component
@SqlDao
@RequiredArgsConstructor
@@ -47,10 +47,20 @@ public class JpaNotificationTargetDao extends JpaAbstractDao findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) {
- return DaoUtil.toPageData(notificationTargetRepository.findByTenantIdAndNameContainingIgnoreCase(tenantId.getId(),
+ return DaoUtil.toPageData(notificationTargetRepository.findByTenantIdAndSearchText(tenantId.getId(),
Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink)));
}
+ @Override
+ public PageData findByTenantIdAndSupportedNotificationTypeAndPageLink(TenantId tenantId, NotificationType notificationType, PageLink pageLink) {
+ return DaoUtil.toPageData(notificationTargetRepository.findByTenantIdAndSearchTextAndUsersFilterTypeIfPresent(tenantId.getId(),
+ Strings.nullToEmpty(pageLink.getTextSearch()),
+ Arrays.stream(UsersFilterType.values())
+ .filter(type -> notificationType != NotificationType.GENERAL || !type.isForRules())
+ .map(Enum::name).collect(Collectors.toList()),
+ DaoUtil.toPageable(pageLink)));
+ }
+
@Override
public List findByTenantIdAndIds(TenantId tenantId, List ids) {
return DaoUtil.convertDataList(notificationTargetRepository.findByTenantIdAndIdIn(tenantId.getId(), DaoUtil.toUUIDs(ids)));
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java
index ebeb2a7a1b..4ab88095e5 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java
@@ -26,15 +26,25 @@ import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.server.common.data.notification.NotificationStatus;
import org.thingsboard.server.dao.model.sql.NotificationEntity;
-import java.util.List;
import java.util.UUID;
@Repository
public interface NotificationRepository extends JpaRepository {
- Page findByRecipientIdAndStatusNot(UUID recipientId, NotificationStatus status, Pageable pageable);
+ @Query("SELECT n FROM NotificationEntity n WHERE n.recipientId = :recipientId AND n.status <> :status " +
+ "AND (:searchText = '' OR lower(n.subject) LIKE lower(concat('%', :searchText, '%')) " +
+ "OR lower(n.text) LIKE lower(concat('%', :searchText, '%')))")
+ Page findByRecipientIdAndStatusNot(@Param("recipientId") UUID recipientId,
+ @Param("status") NotificationStatus status,
+ @Param("searchText") String searchText,
+ Pageable pageable);
- Page findByRecipientId(UUID recipientId, Pageable pageable);
+ @Query("SELECT n FROM NotificationEntity n WHERE n.recipientId = :recipientId " +
+ "AND (:searchText = '' OR lower(n.subject) LIKE lower(concat('%', :searchText, '%')) " +
+ "OR lower(n.text) LIKE lower(concat('%', :searchText, '%')))")
+ Page findByRecipientId(@Param("recipientId") UUID recipientId,
+ @Param("searchText") String searchText,
+ Pageable pageable);
@Modifying
@Transactional
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java
index d28b0dc95b..126295616f 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java
@@ -41,7 +41,7 @@ public interface NotificationRequestRepository extends JpaRepository findByTenantIdAndOriginatorEntityType(UUID tenantId, EntityType originatorType, Pageable pageable);
@Query(REQUEST_INFO_QUERY + " WHERE r.tenantId = :tenantId AND r.originatorEntityType = :originatorType " +
- "AND (:searchText = '' OR (t.name IS NOT NULL AND lower(t.name) LIKE lower(concat('%', :searchText, '%'))))")
+ "AND (:searchText = '' OR t.name IS NOT NULL AND lower(t.name) LIKE lower(concat('%', :searchText, '%')))")
Page findInfosByTenantIdAndOriginatorEntityTypeAndSearchText(@Param("tenantId") UUID tenantId,
@Param("originatorType") EntityType originatorType,
@Param("searchText") String searchText,
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java
index 4507cd0cd9..145905593f 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java
@@ -37,7 +37,7 @@ public interface NotificationRuleRepository extends JpaRepository findByTenantIdAndSearchText(@Param("tenantId") UUID tenantId,
@Param("searchText") String searchText,
Pageable pageable);
@@ -49,7 +49,7 @@ public interface NotificationRuleRepository extends JpaRepository findInfosByTenantIdAndSearchText(@Param("tenantId") UUID tenantId,
@Param("searchText") String searchText,
Pageable pageable);
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java
index 1b72f8d64a..a958055a33 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java
@@ -30,7 +30,20 @@ import java.util.UUID;
@Repository
public interface NotificationTargetRepository extends JpaRepository {
- Page findByTenantIdAndNameContainingIgnoreCase(UUID tenantId, String searchText, Pageable pageable);
+ @Query("SELECT t FROM NotificationTargetEntity t WHERE t.tenantId = :tenantId " +
+ "AND (:searchText = '' OR lower(t.name) LIKE lower(concat('%', :searchText, '%')))")
+ Page findByTenantIdAndSearchText(@Param("tenantId") UUID tenantId,
+ @Param("searchText") String searchText,
+ Pageable pageable);
+
+ @Query(value = "SELECT * FROM notification_target t WHERE t.tenant_id = :tenantId " +
+ "AND (:searchText = '' OR lower(t.name) LIKE lower(concat('%', :searchText, '%'))) " +
+ "AND (cast(t.configuration as json) ->> 'type' <> 'PLATFORM_USERS' OR " +
+ "cast(t.configuration as json) -> 'usersFilter' ->> 'type' IN :usersFilterTypes)", nativeQuery = true)
+ Page findByTenantIdAndSearchTextAndUsersFilterTypeIfPresent(@Param("tenantId") UUID tenantId,
+ @Param("searchText") String searchText,
+ @Param("usersFilterTypes") List usersFilterTypes,
+ Pageable pageable);
List findByTenantIdAndIdIn(UUID tenantId, List ids);
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java
index 860af9746a..1381c43710 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java
@@ -32,9 +32,9 @@ import java.util.UUID;
public interface NotificationTemplateRepository extends JpaRepository {
@Query("SELECT t FROM NotificationTemplateEntity t WHERE t.tenantId = :tenantId AND " +
- "t.notificationType IN :notificationTypes AND " +
- "(lower(t.name) LIKE lower(concat('%', :searchText, '%')) OR " +
- "lower(t.notificationType) LIKE lower(concat('%', :searchText, '%')))")
+ "t.notificationType IN :notificationTypes " +
+ "AND (:searchText = '' OR lower(t.name) LIKE lower(concat('%', :searchText, '%')) " +
+ "OR lower(t.notificationType) LIKE lower(concat('%', :searchText, '%')))")
Page findByTenantIdAndNotificationTypesAndSearchText(@Param("tenantId") UUID tenantId,
@Param("notificationTypes") List notificationTypes,
@Param("searchText") String searchText,
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java
index 4d6f39a546..309b151a72 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java
@@ -143,6 +143,10 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
}
EntityDataSortOrder sortOrder = pageLink.getSortOrder();
+ if (sortOrder != null && EntityKeyType.ALARM_FIELD.equals(sortOrder.getKey().getType()) && ASSIGNEE_KEY.equalsIgnoreCase(sortOrder.getKey().getKey())) {
+ sortOrder = new EntityDataSortOrder(new EntityKey(EntityKeyType.ALARM_FIELD, ASSIGNEE_EMAIL_KEY), sortOrder.getDirection());
+ }
+
List alarmFields = new ArrayList<>();
for (EntityKey key : query.getAlarmFields()) {
if (EntityKeyType.ALARM_FIELD.equals(key.getType()) && ASSIGNEE_KEY.equalsIgnoreCase(key.getKey())) {
@@ -260,6 +264,8 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
}
if (pageLink.getAssigneeId() != null) {
+ addAndIfNeeded(wherePart, addAnd);
+ addAnd = true;
ctx.addUuidParameter("assigneeId", pageLink.getAssigneeId().getId());
wherePart.append(" a.assignee_id = :assigneeId");
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantProfileDao.java
index 147d065e8b..6ba2337f5f 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantProfileDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantProfileDao.java
@@ -29,6 +29,8 @@ import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
import org.thingsboard.server.dao.tenant.TenantProfileDao;
import org.thingsboard.server.dao.util.SqlDao;
+import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
import java.util.UUID;
@@ -79,4 +81,10 @@ public class JpaTenantProfileDao extends JpaAbstractSearchTextDao findTenantProfilesByIds(TenantId tenantId, UUID[] ids) {
+ return DaoUtil.convertDataList(tenantProfileRepository.findByIdIn(Arrays.asList(ids)));
+ }
+
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantProfileRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantProfileRepository.java
index 21d44daa59..74cc24be86 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantProfileRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantProfileRepository.java
@@ -23,6 +23,7 @@ import org.springframework.data.repository.query.Param;
import org.thingsboard.server.common.data.EntityInfo;
import org.thingsboard.server.dao.model.sql.TenantProfileEntity;
+import java.util.List;
import java.util.UUID;
public interface TenantProfileRepository extends JpaRepository {
@@ -52,4 +53,6 @@ public interface TenantProfileRepository extends JpaRepository findByIdIn(List ids);
+
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java
index c745648b4b..4a41fc17a4 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java
@@ -21,6 +21,7 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
@@ -30,6 +31,7 @@ import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
import org.thingsboard.server.dao.user.UserDao;
import org.thingsboard.server.dao.util.SqlDao;
+import java.util.List;
import java.util.Objects;
import java.util.UUID;
@@ -101,10 +103,26 @@ public class JpaUserDao extends JpaAbstractSearchTextDao imple
}
@Override
- public PageData findAll(TenantId tenantId, PageLink pageLink) {
+ public PageData findAll(PageLink pageLink) {
return DaoUtil.toPageData(userRepository.findAll(DaoUtil.toPageable(pageLink)));
}
+ @Override
+ public PageData findAllByAuthority(Authority authority, PageLink pageLink) {
+ return DaoUtil.toPageData(userRepository.findAllByAuthority(authority, DaoUtil.toPageable(pageLink)));
+ }
+
+ @Override
+ public PageData findByAuthorityAndTenantsIds(Authority authority, List tenantsIds, PageLink pageLink) {
+ return DaoUtil.toPageData(userRepository.findByAuthorityAndTenantIdIn(authority, DaoUtil.toUUIDs(tenantsIds), DaoUtil.toPageable(pageLink)));
+ }
+
+ @Override
+ public PageData findByAuthorityAndTenantProfilesIds(Authority authority, List tenantProfilesIds, PageLink pageLink) {
+ return DaoUtil.toPageData(userRepository.findByAuthorityAndTenantProfilesIds(authority, DaoUtil.toUUIDs(tenantProfilesIds),
+ DaoUtil.toPageable(pageLink)));
+ }
+
@Override
public Long countByTenantId(TenantId tenantId) {
return userRepository.countByTenantId(tenantId.getId());
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java
index 8a7ee7d581..e00c73763b 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java
@@ -20,10 +20,10 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
-import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.model.sql.UserEntity;
+import java.util.Collection;
import java.util.UUID;
/**
@@ -50,6 +50,17 @@ public interface UserRepository extends JpaRepository {
@Param("searchText") String searchText,
Pageable pageable);
+ Page findAllByAuthority(Authority authority, Pageable pageable);
+
+ Page findByAuthorityAndTenantIdIn(Authority authority, Collection tenantsIds, Pageable pageable);
+
+ @Query("SELECT u FROM UserEntity u INNER JOIN TenantEntity t ON u.tenantId = t.id AND u.authority = :authority " +
+ "INNER JOIN TenantProfileEntity p ON t.tenantProfileId = p.id " +
+ "WHERE p.id IN :profiles")
+ Page findByAuthorityAndTenantProfilesIds(@Param("authority") Authority authority,
+ @Param("profiles") Collection tenantProfilesIds,
+ Pageable pageable);
+
Long countByTenantId(UUID tenantId);
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileDao.java
index 0875c7664e..d6353b213c 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileDao.java
@@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.Dao;
+import java.util.List;
import java.util.UUID;
public interface TenantProfileDao extends Dao {
@@ -38,4 +39,6 @@ public interface TenantProfileDao extends Dao {
EntityInfo findDefaultTenantProfileInfo(TenantId tenantId);
+ List findTenantProfilesByIds(TenantId tenantId, UUID[] ids);
+
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java
index b52bab1d3a..4262aca2e4 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java
@@ -40,6 +40,7 @@ import org.thingsboard.server.dao.service.Validator;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.UUID;
import static org.thingsboard.server.dao.service.Validator.validateId;
@@ -202,6 +203,11 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService findTenantProfilesByIds(TenantId tenantId, UUID[] ids) {
+ return tenantProfileDao.findTenantProfilesByIds(tenantId, ids);
+ }
+
@Override
public void deleteTenantProfiles(TenantId tenantId) {
log.trace("Executing deleteTenantProfiles");
diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java
index 428e886694..bf45034989 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java
@@ -195,7 +195,11 @@ public class TenantServiceImpl extends AbstractCachedEntityService 0) {
EntityTypeFilter filter = new EntityTypeFilter();
filter.setEntityType(entityType);
- long currentCount = entityService.countEntitiesByQuery(tenantId, null, new EntityCountQuery(filter));
- notificationRuleProcessingService.process(tenantId, entityType, limit, currentCount);
+ long currentCount = entityService.countEntitiesByQuery(tenantId, new CustomerId(EntityId.NULL_UUID), new EntityCountQuery(filter));
+ if (notificationRuleProcessingService != null) {
+ notificationRuleProcessingService.process(tenantId, EntitiesLimitTrigger.builder()
+ .entityType(entityType)
+ .currentCount(currentCount)
+ .limit(limit)
+ .build());
+ }
return currentCount < limit;
} else {
return true;
diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java
index de1d3dedcf..54d847a26e 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java
@@ -17,11 +17,14 @@ package org.thingsboard.server.dao.user;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.TenantEntityDao;
+import java.util.List;
import java.util.UUID;
public interface UserDao extends Dao, TenantEntityDao {
@@ -79,6 +82,12 @@ public interface UserDao extends Dao, TenantEntityDao {
*/
PageData findCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink);
- PageData findAll(TenantId tenantId, PageLink pageLink);
+ PageData findAll(PageLink pageLink);
+
+ PageData findAllByAuthority(Authority authority, PageLink pageLink);
+
+ PageData findByAuthorityAndTenantsIds(Authority authority, List tenantsIds, PageLink pageLink);
+
+ PageData findByAuthorityAndTenantProfilesIds(Authority authority, List tenantProfilesIds, PageLink pageLink);
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java
index 4b55cb099b..18f413ab17 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java
@@ -34,10 +34,12 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.id.UserCredentialsId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent;
import org.thingsboard.server.dao.entity.AbstractEntityService;
@@ -46,6 +48,7 @@ import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -250,8 +253,23 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
}
@Override
- public PageData findAllUsers(TenantId tenantId, PageLink pageLink) {
- return userDao.findAll(tenantId, pageLink);
+ public PageData findAllTenantAdmins(PageLink pageLink) {
+ return userDao.findAllByAuthority(Authority.TENANT_ADMIN, pageLink);
+ }
+
+ @Override
+ public PageData findTenantAdminsByTenantsIds(List tenantsIds, PageLink pageLink) {
+ return userDao.findByAuthorityAndTenantsIds(Authority.TENANT_ADMIN, tenantsIds, pageLink);
+ }
+
+ @Override
+ public PageData findTenantAdminsByTenantProfilesIds(List tenantProfilesIds, PageLink pageLink) {
+ return userDao.findByAuthorityAndTenantProfilesIds(Authority.TENANT_ADMIN, tenantProfilesIds, pageLink);
+ }
+
+ @Override
+ public PageData findAllUsers(PageLink pageLink) {
+ return userDao.findAll(pageLink);
}
@Override
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmCommentServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmCommentServiceTest.java
index 1144ec6513..e79e00515b 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmCommentServiceTest.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmCommentServiceTest.java
@@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmCommentInfo;
+import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.UserId;
@@ -79,7 +80,7 @@ public abstract class BaseAlarmCommentServiceTest extends AbstractServiceTest {
@Test
- public void testSaveAndFetchAlarmComment() throws ExecutionException, InterruptedException {
+ public void testCreateAndFetchAlarmComment() throws ExecutionException, InterruptedException {
AlarmComment alarmComment = AlarmComment.builder().alarmId(alarm.getId())
.userId(user.getId())
.type(OTHER)
@@ -142,7 +143,7 @@ public abstract class BaseAlarmCommentServiceTest extends AbstractServiceTest {
}
@Test
- public void testDeleteAlarmComment() throws ExecutionException, InterruptedException {
+ public void testSaveAlarmComment() throws ExecutionException, InterruptedException {
UserId userId = new UserId(UUID.randomUUID());
AlarmComment alarmComment = AlarmComment.builder().alarmId(alarm.getId())
.userId(userId)
@@ -152,13 +153,12 @@ public abstract class BaseAlarmCommentServiceTest extends AbstractServiceTest {
AlarmComment createdComment = alarmCommentService.createOrUpdateAlarmComment(tenantId, alarmComment);
- Assert.assertNotNull(createdComment);
- Assert.assertNotNull(createdComment.getId());
-
- alarmCommentService.deleteAlarmComment(tenantId, createdComment.getId());
+ createdComment.setType(AlarmCommentType.SYSTEM);
+ createdComment.setUserId(null);
+ alarmCommentService.saveAlarmComment(tenantId, createdComment);
AlarmComment fetched = alarmCommentService.findAlarmCommentByIdAsync(tenantId, createdComment.getId()).get();
-
- Assert.assertNull("Alarm comment was returned when it was expected to be null", fetched);
+ Assert.assertNull(fetched.getUserId());
+ Assert.assertEquals(AlarmCommentType.SYSTEM, fetched.getType());
}
}
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java
index 73a489ab68..9c81bfd232 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java
@@ -267,6 +267,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
pageLink.setPage(0);
pageLink.setPageSize(10);
pageLink.setAssigneeId(tenantUser.getId());
+ pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ALARM_FIELD, "assignee")));
PageData assignedAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(created.getOriginator()));
Assert.assertNotNull(assignedAlarms.getData());
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetProfileServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetProfileServiceTest.java
index df0cb464fc..02bf4b1b1a 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetProfileServiceTest.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetProfileServiceTest.java
@@ -254,7 +254,7 @@ public abstract class BaseAssetProfileServiceTest extends AbstractServiceTest {
Collections.sort(loadedAssetProfileInfos, assetProfileInfoIdComparator);
List assetProfileInfos = assetProfiles.stream()
- .map(assetProfile -> new AssetProfileInfo(assetProfile.getId(),
+ .map(assetProfile -> new AssetProfileInfo(assetProfile.getId(), assetProfile.getTenantId(),
assetProfile.getName(), assetProfile.getImage(), assetProfile.getDefaultDashboardId())).collect(Collectors.toList());
Assert.assertEquals(assetProfileInfos, loadedAssetProfileInfos);
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java
index cdfa73fac9..fa43d98374 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java
@@ -353,7 +353,7 @@ public abstract class BaseDeviceProfileServiceTest extends AbstractServiceTest {
Collections.sort(loadedDeviceProfileInfos, deviceProfileInfoIdComparator);
List deviceProfileInfos = deviceProfiles.stream()
- .map(deviceProfile -> new DeviceProfileInfo(deviceProfile.getId(),
+ .map(deviceProfile -> new DeviceProfileInfo(deviceProfile.getId(), deviceProfile.getTenantId(),
deviceProfile.getName(), deviceProfile.getImage(), deviceProfile.getDefaultDashboardId(),
deviceProfile.getType(), deviceProfile.getTransportType())).collect(Collectors.toList());
diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties
index a2a21abc11..fc46ba431a 100644
--- a/dao/src/test/resources/application-test.properties
+++ b/dao/src/test/resources/application-test.properties
@@ -65,6 +65,8 @@ cache.specs.otaPackagesData.maxSize=100000
cache.specs.edges.timeToLiveInMinutes=1440
cache.specs.edges.maxSize=100000
+cache.specs.notificationRules.timeToLiveInMinutes=1440
+cache.specs.notificationRules.maxSize=10000
redis.connection.host=localhost
redis.connection.port=6379
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java
index 3820563a8a..e88397b781 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java
@@ -49,6 +49,7 @@ import java.net.URL;
import java.time.Duration;
import java.util.stream.Collectors;
+import static org.assertj.core.api.Assertions.fail;
import static org.thingsboard.server.msa.TestProperties.getBaseUiUrl;
import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
@@ -122,7 +123,7 @@ abstract public class AbstractDriverBaseTest extends AbstractContainerTest {
try {
wait.until(ExpectedConditions.urlContains(urlPath));
} catch (WebDriverException e) {
- log.error("This URL path is missing");
+ return fail("URL not contains " + urlPath);
}
return driver.getCurrentUrl().contains(urlPath);
}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/AssetProfileEditMenuTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/AssetProfileEditMenuTest.java
index 0d02bd9434..d94fb029ad 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/AssetProfileEditMenuTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/AssetProfileEditMenuTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.assetProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.openqa.selenium.Keys;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
@@ -54,8 +56,10 @@ public class AssetProfileEditMenuTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Asset profiles smoke")
+ @Feature("Edit asset profile")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Change name by edit menu")
public void changeName() {
String name = ENTITY_NAME + random();
String newName = "Changed" + getRandomNumber();
@@ -77,8 +81,10 @@ public class AssetProfileEditMenuTest extends AbstractDriverBaseTest {
Assert.assertEquals(nameAfter, newName);
}
+ @Epic("Asset profiles smoke")
+ @Feature("Edit asset profile")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Delete name and save")
public void deleteName() {
String name = ENTITY_NAME + random();
testRestClient.postAssetProfile(EntityPrototypes.defaultAssetProfile(name));
@@ -92,8 +98,10 @@ public class AssetProfileEditMenuTest extends AbstractDriverBaseTest {
Assert.assertFalse(profilesPage.doneBtnEditViewVisible().isEnabled());
}
+ @Epic("Asset profiles smoke")
+ @Feature("Edit asset profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Save only with space")
public void saveWithOnlySpaceInName() {
String name = ENTITY_NAME + random();
testRestClient.postAssetProfile(EntityPrototypes.defaultAssetProfile(name));
@@ -110,8 +118,10 @@ public class AssetProfileEditMenuTest extends AbstractDriverBaseTest {
Assert.assertEquals(profilesPage.warningMessage().getText(), EMPTY_ASSET_PROFILE_MESSAGE);
}
+ @Epic("Asset profiles smoke")
+ @Feature("Edit asset profile")
@Test(priority = 30, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "editMenuDescription")
- @Description
+ @Description("Write the description and save the changes/Change the description and save the changes/Delete the description and save the changes")
public void editDescription(String description, String newDescription, String finalDescription) {
String name = ENTITY_NAME + random();
testRestClient.postAssetProfile(EntityPrototypes.defaultAssetProfile(name, description));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/CreateAssetProfileImportTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/CreateAssetProfileImportTest.java
index 275eb866ce..8816f878e7 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/CreateAssetProfileImportTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/CreateAssetProfileImportTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.assetProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -55,8 +57,10 @@ public class CreateAssetProfileImportTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Asset profiles smoke")
+ @Feature("Import asset profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Import asset profile")
public void importAssetProfile() {
sideBarMenuView.openAssetProfiles();
profilesPage.openImportAssetProfileView();
@@ -69,8 +73,10 @@ public class CreateAssetProfileImportTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.entity(IMPORT_ASSET_PROFILE_NAME).isDisplayed());
}
+ @Epic("Asset profiles smoke")
+ @Feature("Import asset profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Import txt file")
public void importTxtFile() {
sideBarMenuView.openDeviceProfiles();
profilesPage.openImportDeviceProfileView();
@@ -80,8 +86,10 @@ public class CreateAssetProfileImportTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.importingFile(EMPTY_IMPORT_MESSAGE).isDisplayed());
}
+ @Epic("Asset profiles smoke")
+ @Feature("Import asset profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Drop json file and delete it")
public void addFileToImportAndRemove() {
sideBarMenuView.openAssetProfiles();
profilesPage.openImportAssetProfileView();
@@ -93,8 +101,10 @@ public class CreateAssetProfileImportTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.entityIsNotPresent(IMPORT_ASSET_PROFILE_NAME));
}
+ @Epic("Asset profiles smoke")
+ @Feature("Import asset profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Import asset profile with same name")
public void importAssetProfileWithSameName() {
String name = IMPORT_ASSET_PROFILE_NAME;
testRestClient.postAssetProfile(EntityPrototypes.defaultAssetProfile(name));
@@ -111,8 +121,10 @@ public class CreateAssetProfileImportTest extends AbstractDriverBaseTest {
Assert.assertEquals(profilesPage.warningMessage().getText(), SAME_NAME_WARNING_ASSET_PROFILE_MESSAGE);
}
+ @Epic("Asset profiles smoke")
+ @Feature("Import asset profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Import asset profile without refresh")
public void importAssetProfileWithoutRefresh() {
sideBarMenuView.openAssetProfiles();
profilesPage.openImportAssetProfileView();
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/CreateAssetProfileTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/CreateAssetProfileTest.java
index 01e1139328..bc5b04cd43 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/CreateAssetProfileTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/CreateAssetProfileTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.assetProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -52,8 +54,10 @@ public class CreateAssetProfileTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Asset profiles smoke")
+ @Feature("Create asset profile")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Add asset profile after specifying the name (text/numbers /special characters)")
public void createAssetProfile() {
String name = ENTITY_NAME + random();
@@ -68,8 +72,10 @@ public class CreateAssetProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.entity(name).isDisplayed());
}
+ @Epic("Asset profiles smoke")
+ @Feature("Create asset profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Add asset profile after specifying the name with details")
public void createAssetProfileWithDetails() {
String name = ENTITY_NAME + random();
String ruleChain = "Root Rule Chain";
@@ -103,8 +109,10 @@ public class CreateAssetProfileTest extends AbstractDriverBaseTest {
Assert.assertEquals(description, profilesPage.getDescription());
}
+ @Epic("Asset profiles smoke")
+ @Feature("Create asset profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Create asset profile with the same name")
public void createAssetProfileWithSameName() {
String name = ENTITY_NAME + random();
testRestClient.postAssetProfile(EntityPrototypes.defaultAssetProfile(name));
@@ -122,8 +130,10 @@ public class CreateAssetProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.addAssetProfileView().isDisplayed());
}
+ @Epic("Asset profiles smoke")
+ @Feature("Create asset profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Add asset profile without the name")
public void createAssetProfileWithoutName() {
sideBarMenuView.openAssetProfiles();
profilesPage.openCreateAssetProfileView();
@@ -131,8 +141,10 @@ public class CreateAssetProfileTest extends AbstractDriverBaseTest {
Assert.assertFalse(profilesPage.addBtnV().isEnabled());
}
+ @Epic("Asset profiles smoke")
+ @Feature("Create asset profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Create asset profile only with spase in name")
public void createAssetProfileWithOnlySpaceInName() {
sideBarMenuView.openAssetProfiles();
profilesPage.openCreateAssetProfileView();
@@ -146,8 +158,10 @@ public class CreateAssetProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.addAssetProfileView().isDisplayed());
}
+ @Epic("Asset profiles smoke")
+ @Feature("Create asset profile")
@Test(priority = 30, groups = "smoke")
- @Description
+ @Description("Add asset profile after specifying the name (text/numbers /special characters) without refresh")
public void createAssetProfileWithoutRefresh() {
String name = ENTITY_NAME + random();
@@ -161,8 +175,10 @@ public class CreateAssetProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.entity(name).isDisplayed());
}
+ @Epic("Asset profiles smoke")
+ @Feature("Create asset profile")
@Test(priority = 40, groups = "smoke")
- @Description
+ @Description("Go to asset profile documentation page")
public void documentation() {
String urlPath = "docs/user-guide/asset-profiles/";
@@ -170,6 +186,6 @@ public class CreateAssetProfileTest extends AbstractDriverBaseTest {
profilesPage.profileNames().get(0).click();
profilesPage.goToProfileHelpPage();
- Assert.assertTrue(urlContains(urlPath));
+ Assert.assertTrue(urlContains(urlPath), "URL not contains " + urlPath);
}
}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/DeleteAssetProfileTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/DeleteAssetProfileTest.java
index 1525849df7..8241685a25 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/DeleteAssetProfileTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/DeleteAssetProfileTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.assetProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -40,8 +42,10 @@ public class DeleteAssetProfileTest extends AbstractDriverBaseTest {
profilesPage = new ProfilesPageHelper(driver);
}
+ @Epic("Asset profiles smoke")
+ @Feature("Delete one asset profile")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Remove the asset profile by clicking on the trash icon in the right side of asset profile")
public void removeAssetProfile() {
String name = ENTITY_NAME + random();
testRestClient.postAssetProfile(EntityPrototypes.defaultAssetProfile(name));
@@ -54,8 +58,10 @@ public class DeleteAssetProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.entityIsNotPresent(name));
}
+ @Epic("Asset profiles smoke")
+ @Feature("Delete one asset profile")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Remove the asset profile by clicking on the 'Delete asset profile' btn in the entity view")
public void removeAssetProfileFromView() {
String name = ENTITY_NAME + random();
testRestClient.postAssetProfile(EntityPrototypes.defaultAssetProfile(name));
@@ -69,8 +75,10 @@ public class DeleteAssetProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.entityIsNotPresent(name));
}
+ @Epic("Asset profiles smoke")
+ @Feature("Delete one asset profile")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Remove asset profile by mark in the checkbox and then click on the trash can icon in the menu that appears at the top")
public void removeSelectedAssetProfile() {
String name = ENTITY_NAME + random();
testRestClient.postAssetProfile(EntityPrototypes.defaultAssetProfile(name));
@@ -84,16 +92,20 @@ public class DeleteAssetProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.entityIsNotPresent(name));
}
+ @Epic("Asset profiles smoke")
+ @Feature("Delete one asset profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the default asset profile by clicking on the trash icon in the right side of asset profile")
public void removeDefaultAssetProfile() {
sideBarMenuView.openAssetProfiles();
Assert.assertFalse(profilesPage.deleteBtn("default").isEnabled());
}
+ @Epic("Asset profiles smoke")
+ @Feature("Delete one asset profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the Default asset profile by clicking on the 'Delete asset profile' btn in the entity view")
public void removeDefaultAssetProfileFromView() {
sideBarMenuView.openAssetProfiles();
profilesPage.entity("default").click();
@@ -101,8 +113,10 @@ public class DeleteAssetProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.deleteAssetProfileFromViewBtnIsNotDisplayed());
}
+ @Epic("Asset profiles smoke")
+ @Feature("Delete one asset profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the default asset profile by mark in the checkbox and then click on the trash can icon in the menu that appears at the top")
public void removeSelectedDefaultAssetProfile() {
sideBarMenuView.openAssetProfiles();
@@ -110,8 +124,10 @@ public class DeleteAssetProfileTest extends AbstractDriverBaseTest {
Assert.assertFalse(profilesPage.presentCheckBox("default").isDisplayed());
}
+ @Epic("Asset profiles smoke")
+ @Feature("Delete one asset profile")
@Test(priority = 30, groups = "smoke")
- @Description
+ @Description("Remove the asset profile by clicking on the trash icon in the right side of asset profile without refresh")
public void removeAssetProfileWithoutRefresh() {
String name = ENTITY_NAME + random();
testRestClient.postAssetProfile(EntityPrototypes.defaultAssetProfile(name));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/DeleteSeveralAssetProfilesTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/DeleteSeveralAssetProfilesTest.java
index 40436c6ea0..ffed0483c0 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/DeleteSeveralAssetProfilesTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/DeleteSeveralAssetProfilesTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.assetProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -39,8 +41,10 @@ public class DeleteSeveralAssetProfilesTest extends AbstractDriverBaseTest {
profilesPage = new ProfilesPageHelper(driver);
}
+ @Epic("Asset profiles smoke")
+ @Feature("Delete several asset profile")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Remove several asset profiles by mark in the checkbox and then click on the trash can icon in the menu that appears at the top")
public void canDeleteSeveralAssetProfilesByTopBtn() {
String name1 = ENTITY_NAME + random() + "1";
String name2 = ENTITY_NAME + random() + "2";
@@ -57,8 +61,11 @@ public class DeleteSeveralAssetProfilesTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.profileIsNotPresent(name2));
}
+ @Epic("Asset profiles smoke")
+ @Feature("Delete several asset profile")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Remove several asset profiles by mark all the asset profiles on the page by clicking in the topmost " +
+ "checkbox and then clicking on the trash icon in the menu that appears")
public void selectAllDAssetProfiles() {
String name1 = ENTITY_NAME + random() + "1";
String name2 = ENTITY_NAME + random() +"2";
@@ -75,8 +82,11 @@ public class DeleteSeveralAssetProfilesTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.profileIsNotPresent(name2));
}
+ @Epic("Asset profiles smoke")
+ @Feature("Delete several asset profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the default asset profile by mark all the asset profiles on the page by clicking in the " +
+ "topmost checkbox and then clicking on the trash icon in the menu that appears")
public void removeDefaultAssetProfile() {
sideBarMenuView.openAssetProfiles();
profilesPage.selectAllCheckBox().click();
@@ -85,8 +95,11 @@ public class DeleteSeveralAssetProfilesTest extends AbstractDriverBaseTest {
Assert.assertFalse(profilesPage.deleteBtn("default").isEnabled());
}
+ @Epic("Asset profiles smoke")
+ @Feature("Delete several asset profile")
@Test(priority = 30, groups = "smoke")
- @Description
+ @Description("Remove several asset profiles by mark in the checkbox and then click on the trash can icon in the menu " +
+ "that appears at the top without refresh")
public void deleteSeveralAssetProfilesByTopBtnWithoutRefresh() {
String name1 = ENTITY_NAME + random() + "1";
String name2 = ENTITY_NAME + random() + "2";
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/MakeAssetProfileDefaultTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/MakeAssetProfileDefaultTest.java
index 761c390338..c7c56d197b 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/MakeAssetProfileDefaultTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/MakeAssetProfileDefaultTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.assetProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -47,8 +49,10 @@ public class MakeAssetProfileDefaultTest extends AbstractDriverBaseTest {
testRestClient.deleteAssetProfile(getAssetProfileByName(name).getId());
}
+ @Epic("Asset profiles smoke")
+ @Feature("Make asset profile default")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Make asset profile default by clicking on the 'Make asset profile default' icon in the right corner")
public void makeDeviceProfileDefaultByRightCornerBtn() {
String name = ENTITY_NAME + random();
testRestClient.postAssetProfile(EntityPrototypes.defaultAssetProfile(name));
@@ -61,8 +65,10 @@ public class MakeAssetProfileDefaultTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.defaultCheckbox(name).isDisplayed());
}
+ @Epic("Asset profiles smoke")
+ @Feature("Make asset profile default")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Make asset profile default by clicking on the 'Make asset profile default' button in the entity view")
public void makeDeviceProfileDefaultFromView() {
String name = ENTITY_NAME + random();
testRestClient.postAssetProfile(EntityPrototypes.defaultAssetProfile(name));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/SearchAssetProfileTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/SearchAssetProfileTest.java
index 16de1924e7..3197c56d50 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/SearchAssetProfileTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/SearchAssetProfileTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.assetProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -48,8 +50,11 @@ public class SearchAssetProfileTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Asset profiles smoke")
+ @Feature("Search asset profile")
@Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "assetProfileSearch")
- @Description
+ @Description("Search asset profile by first word in the name/Search asset profile by second word in the name/" +
+ "Search asset profile by symbol in the name/Search asset profile by number in the name")
public void searchFirstWord(String name, String namePath) {
testRestClient.postAssetProfile(EntityPrototypes.defaultAssetProfile(name));
this.name = name;
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/SortByNameTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/SortByNameTest.java
index 6104e80e80..b5cf7a97d9 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/SortByNameTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assetProfileSmoke/SortByNameTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.assetProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -48,8 +50,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Asset profiles smoke")
+ @Feature("Sort by name")
@Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSort")
- @Description
+ @Description("Sort asset profile 'UP'")
public void specialCharacterUp(String name) {
testRestClient.postAssetProfile(defaultAssetProfile(name));
this.name = name;
@@ -61,8 +65,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
Assert.assertEquals(profilesPage.getProfileName(), name);
}
+ @Epic("Asset profiles smoke")
+ @Feature("Sort by name")
@Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForAllSort")
- @Description
+ @Description("Sort asset profile 'UP'")
public void allSortUp(String assetProfile, String assetProfileSymbol, String assetProfileNumber) {
testRestClient.postAssetProfile(defaultAssetProfile(assetProfileSymbol));
testRestClient.postAssetProfile(defaultAssetProfile(assetProfile));
@@ -86,8 +92,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
Assert.assertEquals(thirdAssetProfile, assetProfile);
}
+ @Epic("Asset profiles smoke")
+ @Feature("Sort by name")
@Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSort")
- @Description
+ @Description("Sort asset profile 'DAWN'")
public void specialCharacterDown(String name) {
testRestClient.postAssetProfile(defaultAssetProfile(name));
this.name = name;
@@ -99,8 +107,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
Assert.assertEquals(profilesPage.getProfileName(), name);
}
+ @Epic("Asset profiles smoke")
+ @Feature("Sort by name")
@Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForAllSort")
- @Description
+ @Description("Sort asset profile 'DOWN'")
public void allSortDown(String assetProfile, String assetProfileSymbol, String assetProfileNumber) {
testRestClient.postAssetProfile(defaultAssetProfile(assetProfileSymbol));
testRestClient.postAssetProfile(defaultAssetProfile(assetProfile));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/CreateCustomerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/CreateCustomerTest.java
index b0659ac328..81fcd6a020 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/CreateCustomerTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/CreateCustomerTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.customerSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.openqa.selenium.Keys;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
@@ -52,8 +54,10 @@ public class CreateCustomerTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Customers smoke tests")
+ @Feature("Create customer")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Add customer specifying the name (text/numbers /special characters)")
public void createCustomer() {
String customerName = ENTITY_NAME + random();
@@ -68,8 +72,10 @@ public class CreateCustomerTest extends AbstractDriverBaseTest {
Assert.assertTrue(customerPage.customer(customerName).isDisplayed());
}
+ @Epic("Customers smoke tests")
+ @Feature("Create customer")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Add customer after specifying the name (text/numbers /special characters) with full information")
public void createCustomerWithFullInformation() {
String customerName = ENTITY_NAME + random();
String text = "Text";
@@ -112,8 +118,10 @@ public class CreateCustomerTest extends AbstractDriverBaseTest {
Assert.assertEquals(customerPage.getCustomerCity(), text);
}
+ @Epic("Customers smoke tests")
+ @Feature("Create customer")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Add customer without the name")
public void createCustomerWithoutName() {
sideBarMenuView.customerBtn().click();
customerPage.plusBtn().click();
@@ -121,8 +129,10 @@ public class CreateCustomerTest extends AbstractDriverBaseTest {
Assert.assertFalse(customerPage.addBtnV().isEnabled());
}
+ @Epic("Customers smoke tests")
+ @Feature("Create customer")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Create customer only with spase in name")
public void createCustomerWithOnlySpace() {
sideBarMenuView.customerBtn().click();
customerPage.plusBtn().click();
@@ -136,8 +146,10 @@ public class CreateCustomerTest extends AbstractDriverBaseTest {
Assert.assertTrue(customerPage.addEntityView().isDisplayed());
}
+ @Epic("Customers smoke tests")
+ @Feature("Create customer")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Create a customer with the same name")
public void createCustomerSameName() {
sideBarMenuView.customerBtn().click();
customerPage.setCustomerName();
@@ -153,8 +165,10 @@ public class CreateCustomerTest extends AbstractDriverBaseTest {
Assert.assertTrue(customerPage.addEntityView().isDisplayed());
}
+ @Epic("Customers smoke tests")
+ @Feature("Create customer")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Add customer specifying the name (text/numbers /special characters) without refresh")
public void createCustomerWithoutRefresh() {
String customerName = ENTITY_NAME + random();
@@ -168,8 +182,10 @@ public class CreateCustomerTest extends AbstractDriverBaseTest {
Assert.assertTrue(customerPage.customer(customerName).isDisplayed());
}
+ @Epic("Customers smoke tests")
+ @Feature("Create customer")
@Test(priority = 40, groups = "smoke")
- @Description
+ @Description("Go to customer documentation page")
public void documentation() {
String urlPath = "docs/user-guide/ui/customers/";
@@ -178,6 +194,6 @@ public class CreateCustomerTest extends AbstractDriverBaseTest {
customerPage.customer(customerPage.getCustomerName()).click();
customerPage.goToHelpPage();
- Assert.assertTrue(urlContains(urlPath));
+ Assert.assertTrue(urlContains(urlPath), "URL contains " + urlPath);
}
}
\ No newline at end of file
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/CustomerEditMenuTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/CustomerEditMenuTest.java
index 76a4abb65d..c07e450bc4 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/CustomerEditMenuTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/CustomerEditMenuTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.customerSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -68,8 +70,10 @@ public class CustomerEditMenuTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Customers smoke tests")
+ @Feature("Edit customer")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Change title by edit menu")
public void changeTitle() {
String customerName = "Changed" + getRandomNumber();
testRestClient.postCustomer(defaultCustomerPrototype(ENTITY_NAME + random()));
@@ -89,8 +93,10 @@ public class CustomerEditMenuTest extends AbstractDriverBaseTest {
Assert.assertEquals(titleAfter, customerName);
}
+ @Epic("Customers smoke tests")
+ @Feature("Edit customer")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Delete title and save")
public void deleteTitle() {
sideBarMenuView.customerBtn().click();
customerPage.entityTitles().get(0).click();
@@ -100,8 +106,10 @@ public class CustomerEditMenuTest extends AbstractDriverBaseTest {
Assert.assertFalse(customerPage.doneBtnEditViewVisible().isEnabled());
}
+ @Epic("Customers smoke tests")
+ @Feature("Edit customer")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Save only with space in title")
public void saveOnlyWithSpace() {
sideBarMenuView.customerBtn().click();
customerPage.setCustomerName();
@@ -117,8 +125,10 @@ public class CustomerEditMenuTest extends AbstractDriverBaseTest {
Assert.assertEquals(customerPage.getCustomerName(), customerPage.getHeaderName());
}
+ @Epic("Customers smoke tests")
+ @Feature("Edit customer")
@Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "editMenuDescription")
- @Description
+ @Description("Write the description and save the changes/Change the description and save the changes/Delete the description and save the changes")
public void editDescription(String description, String newDescription, String finalDescription) {
String name = ENTITY_NAME + random();
testRestClient.postCustomer(EntityPrototypes.defaultCustomerPrototype(name, description));
@@ -134,8 +144,10 @@ public class CustomerEditMenuTest extends AbstractDriverBaseTest {
Assert.assertEquals(customerPage.getDescription(), finalDescription);
}
+ @Epic("Customers smoke tests")
+ @Feature("Edit customer")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Assigned dashboard from dashboards page")
public void assignedDashboardFromDashboard() {
String customerName = ENTITY_NAME + random();
testRestClient.postCustomer(defaultCustomerPrototype(customerName));
@@ -161,8 +173,10 @@ public class CustomerEditMenuTest extends AbstractDriverBaseTest {
Assert.assertEquals(customerPage.getDashboardFromView(), dashboardPage.getDashboardTitle());
}
+ @Epic("Customers smoke tests")
+ @Feature("Edit customer")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Assigned dashboard")
public void assignedDashboard() {
String customerName = ENTITY_NAME + random();
testRestClient.postCustomer(defaultCustomerPrototype(customerName));
@@ -187,8 +201,10 @@ public class CustomerEditMenuTest extends AbstractDriverBaseTest {
Assert.assertEquals(customerPage.getDashboard(), customerPage.getDashboardFromView());
}
+ @Epic("Customers smoke tests")
+ @Feature("Edit customer")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Assigned dashboard without hide")
public void assignedDashboardWithoutHide() {
String customerName = ENTITY_NAME + random();
testRestClient.postCustomer(defaultCustomerPrototype(customerName));
@@ -220,8 +236,10 @@ public class CustomerEditMenuTest extends AbstractDriverBaseTest {
Assert.assertTrue(customerPage.timeBtn().isDisplayed());
}
+ @Epic("Customers smoke tests")
+ @Feature("Edit customer")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Add phone number")
public void addPhoneNumber() {
String customerName = ENTITY_NAME + random();
testRestClient.postCustomer(defaultCustomerPrototype(customerName));
@@ -237,8 +255,10 @@ public class CustomerEditMenuTest extends AbstractDriverBaseTest {
Assert.assertTrue(customerPage.phoneNumberEntityView().getAttribute("value").contains(number));
}
+ @Epic("Customers smoke tests")
+ @Feature("Edit customer")
@Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "incorrectPhoneNumber")
- @Description
+ @Description("Add incorrect phone number")
public void addIncorrectPhoneNumber(String number) {
sideBarMenuView.customerBtn().click();
customerPage.entityTitles().get(0).click();
@@ -251,8 +271,10 @@ public class CustomerEditMenuTest extends AbstractDriverBaseTest {
Assert.assertEquals(customerPage.errorMessage().getText(), PHONE_NUMBER_ERROR_MESSAGE);
}
+ @Epic("Customers smoke tests")
+ @Feature("Edit customer")
@Test(priority = 30, groups = "smoke")
- @Description
+ @Description("Add all information")
public void addAllInformation() {
String customerName = ENTITY_NAME + random();
testRestClient.postCustomer(defaultCustomerPrototype(customerName));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/DeleteCustomerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/DeleteCustomerTest.java
index 67b0e5ef9f..6421c30c31 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/DeleteCustomerTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/DeleteCustomerTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.customerSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -43,8 +45,10 @@ public class DeleteCustomerTest extends AbstractDriverBaseTest {
ruleChainsPage = new RuleChainsPageHelper(driver);
}
+ @Epic("Customers smoke tests")
+ @Feature("Delete customer")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Remove the customer by clicking on the trash icon in the right side of refresh")
public void removeCustomerByRightSideBtn() {
String customer = ENTITY_NAME + random();
testRestClient.postCustomer(defaultCustomerPrototype(customer));
@@ -56,8 +60,10 @@ public class DeleteCustomerTest extends AbstractDriverBaseTest {
Assert.assertTrue(customerPage.entityIsNotPresent(deletedCustomer));
}
+ @Epic("Customers smoke tests")
+ @Feature("Delete customer")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove customer by mark in the checkbox and then click on the trash can icon in the menu that appears at the top")
public void removeSelectedCustomer() {
String customerName = ENTITY_NAME + random();
testRestClient.postCustomer(defaultCustomerPrototype(customerName));
@@ -69,8 +75,10 @@ public class DeleteCustomerTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.entityIsNotPresent(deletedCustomer));
}
+ @Epic("Customers smoke tests")
+ @Feature("Delete customer")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the customer by clicking on the 'Delete customer' btn in the entity view")
public void removeFromCustomerView() {
String customerName = ENTITY_NAME + random();
testRestClient.postCustomer(defaultCustomerPrototype(customerName));
@@ -84,8 +92,10 @@ public class DeleteCustomerTest extends AbstractDriverBaseTest {
Assert.assertTrue(customerPage.entityIsNotPresent(customerName));
}
+ @Epic("Customers smoke tests")
+ @Feature("Delete customer")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the customer by clicking on the trash icon in the right side of customer without refresh")
public void removeCustomerByRightSideBtnWithoutRefresh() {
String customer = ENTITY_NAME + random();
testRestClient.postCustomer(defaultCustomerPrototype(customer));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/DeleteSeveralCustomerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/DeleteSeveralCustomerTest.java
index c198ad36fc..be3e9b65ed 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/DeleteSeveralCustomerTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/DeleteSeveralCustomerTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.customerSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -40,8 +42,10 @@ public class DeleteSeveralCustomerTest extends AbstractDriverBaseTest {
customerPage = new CustomerPageHelper(driver);
}
+ @Epic("Customers smoke tests")
+ @Feature("Delete several customer")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Remove several customers by mark in the checkbox and then click on the trash can icon in the menu that appears at the top")
public void canDeleteSeveralCustomersByTopBtn() {
String title1 = ENTITY_NAME + random() + "1";
String title2 = ENTITY_NAME + random() + "2";
@@ -56,8 +60,11 @@ public class DeleteSeveralCustomerTest extends AbstractDriverBaseTest {
Assert.assertTrue(customerPage.customerIsNotPresent(title2));
}
+ @Epic("Customers smoke tests")
+ @Feature("Delete several customer")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Remove several customers by mark all the Customers on the page by clicking in the topmost checkbox " +
+ "and then clicking on the trash icon in the menu that appears")
public void selectAllCustomers() {
sideBarMenuView.customerBtn().click();
customerPage.selectAllCheckBox().click();
@@ -68,8 +75,11 @@ public class DeleteSeveralCustomerTest extends AbstractDriverBaseTest {
Assert.assertTrue(customerPage.warningPopUpTitle().getText().contains(String.valueOf(customerPage.markCheckbox().size())));
}
+ @Epic("Customers smoke tests")
+ @Feature("Delete several customer")
@Test(priority = 30, groups = "smoke")
- @Description
+ @Description("Remove several customers by mark in the checkbox and then click on the trash can icon in the menu " +
+ "that appears at the top without refresh")
public void deleteSeveralCustomersByTopBtnWithoutRefresh() {
String title1 = ENTITY_NAME + random() + "1";
String title2 = ENTITY_NAME + random() + "2";
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersAssetsTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersAssetsTest.java
index 80467b1075..13555aad01 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersAssetsTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersAssetsTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.customerSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -37,8 +39,10 @@ public class ManageCustomersAssetsTest extends AbstractDriverBaseTest {
customerPage = new CustomerPageHelper(driver);
}
+ @Epic("Customers smoke tests")
+ @Feature("Manage customer assets")
@Test(groups = "smoke")
- @Description
+ @Description("Open manage window by right corner btn")
public void openWindowByRightCornerBtn() {
sideBarMenuView.customerBtn().click();
customerPage.setCustomerName();
@@ -50,8 +54,10 @@ public class ManageCustomersAssetsTest extends AbstractDriverBaseTest {
Assert.assertTrue(customerPage.customerManageWindowIconHead().getText().contains(manage));
}
+ @Epic("Customers smoke tests")
+ @Feature("Manage customer assets")
@Test(groups = "smoke")
- @Description
+ @Description("Open manage window by btn in entity view")
public void openWindowByView() {
sideBarMenuView.customerBtn().click();
customerPage.setCustomerName();
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersDashboardsTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersDashboardsTest.java
index 27d682918e..670fbd941a 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersDashboardsTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersDashboardsTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.customerSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -36,8 +38,10 @@ public class ManageCustomersDashboardsTest extends AbstractDriverBaseTest {
customerPage = new CustomerPageHelper(driver);
}
+ @Epic("Customers smoke tests")
+ @Feature("Manage customer dashboards")
@Test(groups = "smoke")
- @Description
+ @Description("Open manage window by right corner btn")
public void openWindowByRightCornerBtn() {
sideBarMenuView.customerBtn().click();
customerPage.setCustomerName();
@@ -49,8 +53,10 @@ public class ManageCustomersDashboardsTest extends AbstractDriverBaseTest {
Assert.assertTrue(customerPage.customerManageWindowIconHead().getText().contains(manage));
}
+ @Epic("Customers smoke tests")
+ @Feature("Manage customer dashboards")
@Test(groups = "smoke")
- @Description
+ @Description("Open manage window by btn in entity view")
public void openWindowByView() {
sideBarMenuView.customerBtn().click();
customerPage.setCustomerName();
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersDevicesTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersDevicesTest.java
index f6a6e4c433..bb5fe0d41a 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersDevicesTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersDevicesTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.customerSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -36,8 +38,10 @@ public class ManageCustomersDevicesTest extends AbstractDriverBaseTest {
customerPage = new CustomerPageHelper(driver);
}
+ @Epic("Customers smoke tests")
+ @Feature("Manage customer devices")
@Test(groups = "smoke")
- @Description
+ @Description("Open manage window by right corner btn")
public void openWindowByRightCornerBtn() {
sideBarMenuView.customerBtn().click();
customerPage.setCustomerName();
@@ -49,8 +53,10 @@ public class ManageCustomersDevicesTest extends AbstractDriverBaseTest {
Assert.assertTrue(customerPage.customerManageWindowIconHead().getText().contains(manage));
}
+ @Epic("Customers smoke tests")
+ @Feature("Manage customer devices")
@Test(groups = "smoke")
- @Description
+ @Description("Open manage window by btn in entity view")
public void openWindowByView() {
sideBarMenuView.customerBtn().click();
customerPage.setCustomerName();
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersEdgesTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersEdgesTest.java
index a32ab02b7a..5a34d106c6 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersEdgesTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersEdgesTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.customerSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -37,8 +39,10 @@ public class ManageCustomersEdgesTest extends AbstractDriverBaseTest {
customerPage = new CustomerPageHelper(driver);
}
+ @Epic("Customers smoke tests")
+ @Feature("Manage customer edges")
@Test(groups = "smoke")
- @Description
+ @Description("Open manage window by right corner btn")
public void openWindowByRightCornerBtn() {
sideBarMenuView.customerBtn().click();
customerPage.setCustomerName();
@@ -50,8 +54,10 @@ public class ManageCustomersEdgesTest extends AbstractDriverBaseTest {
Assert.assertTrue(customerPage.customerManageWindowIconHead().getText().contains(iconText));
}
+ @Epic("Customers smoke tests")
+ @Feature("Manage customer edges")
@Test(groups = "smoke")
- @Description
+ @Description("Open manage window by btn in entity view")
public void openWindowByView() {
sideBarMenuView.customerBtn().click();
customerPage.setCustomerName();
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersUsersTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersUsersTest.java
index 632f76422e..9fe16410ec 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersUsersTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersUsersTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.customerSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -37,8 +39,10 @@ public class ManageCustomersUsersTest extends AbstractDriverBaseTest {
customerPage = new CustomerPageHelper(driver);
}
+ @Epic("Customers smoke tests")
+ @Feature("Manage customer users")
@Test(groups = "smoke")
- @Description
+ @Description("Open manage window by right corner btn")
public void openWindowByRightCornerBtn() {
sideBarMenuView.customerBtn().click();
customerPage.setCustomerName();
@@ -50,8 +54,10 @@ public class ManageCustomersUsersTest extends AbstractDriverBaseTest {
Assert.assertTrue(customerPage.customerManageWindowIconHead().getText().contains(iconText));
}
+ @Epic("Customers smoke tests")
+ @Feature("Manage customer users")
@Test(groups = "smoke")
- @Description
+ @Description("Open manage window by btn in entity view")
public void openWindowByView() {
sideBarMenuView.customerBtn().click();
customerPage.setCustomerName();
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/SearchCustomerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/SearchCustomerTest.java
index 2ff2a6fef9..8e317073d0 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/SearchCustomerTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/SearchCustomerTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.customerSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -39,8 +41,10 @@ public class SearchCustomerTest extends AbstractDriverBaseTest {
customerPage = new CustomerPageHelper(driver);
}
+ @Epic("Customers smoke tests")
+ @Feature("Search customer")
@Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "customerNameForSearchByFirstAndSecondWord")
- @Description
+ @Description("Search customer by first word in the name/*CHANGE TESTCASE*")
public void searchFirstWord(String namePath) {
sideBarMenuView.customerBtn().click();
customerPage.searchEntity(namePath);
@@ -48,8 +52,10 @@ public class SearchCustomerTest extends AbstractDriverBaseTest {
customerPage.allEntity().forEach(x -> Assert.assertTrue(x.getText().contains(namePath)));
}
+ @Epic("Customers smoke tests")
+ @Feature("Search customer")
@Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSearchBySymbolAndNumber")
- @Description
+ @Description("Search customer by symbol in the name/Search customer by number in the name")
public void searchNumber(String name, String namePath) {
testRestClient.postCustomer(defaultCustomerPrototype(name));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/SortByNameTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/SortByNameTest.java
index 296ab2c847..f60e7997a4 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/SortByNameTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/SortByNameTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.customerSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -48,8 +50,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Customers smoke tests")
+ @Feature("Sort customers by name")
@Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSort")
- @Description
+ @Description("Sort customers 'UP'")
public void specialCharacterUp(String title) {
testRestClient.postCustomer(defaultCustomerPrototype(title));
this.customerName = title;
@@ -61,8 +65,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
Assert.assertEquals(customerPage.getCustomerName(), title);
}
+ @Epic("Customers smoke tests")
+ @Feature("Sort customers by name")
@Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForAllSort")
- @Description
+ @Description("Sort customers 'UP'")
public void allSortUp(String customer, String customerSymbol, String customerNumber) {
testRestClient.postCustomer(defaultCustomerPrototype(customerSymbol));
testRestClient.postCustomer(defaultCustomerPrototype(customer));
@@ -86,8 +92,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
Assert.assertEquals(thirdCustomer, customer);
}
+ @Epic("Customers smoke tests")
+ @Feature("Sort customers by name")
@Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSort")
- @Description
+ @Description("Sort customers 'DOWN'")
public void specialCharacterDown(String title) {
testRestClient.postCustomer(defaultCustomerPrototype(title));
customerName = title;
@@ -99,8 +107,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
Assert.assertEquals(customerPage.getCustomerName(), title);
}
+ @Epic("Customers smoke tests")
+ @Feature("Sort customers by name")
@Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForAllSort")
- @Description
+ @Description("Sort customers 'DOWN'")
public void allSortDown(String customer, String customerSymbol, String customerNumber) {
testRestClient.postCustomer(defaultCustomerPrototype(customerSymbol));
testRestClient.postCustomer(defaultCustomerPrototype(customer));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileImportTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileImportTest.java
index 1c530d33ad..436f19c3d4 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileImportTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileImportTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.deviceProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -55,8 +57,10 @@ public class CreateDeviceProfileImportTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Device profile smoke tests")
+ @Feature("Create device profile")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Import device profile")
public void importDeviceProfile() {
sideBarMenuView.openDeviceProfiles();
profilesPage.openImportDeviceProfileView();
@@ -69,8 +73,10 @@ public class CreateDeviceProfileImportTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.entity(IMPORT_DEVICE_PROFILE_NAME).isDisplayed());
}
+ @Epic("Device profile smoke tests")
+ @Feature("Create device profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Import txt file")
public void importTxtFile() {
sideBarMenuView.openDeviceProfiles();
profilesPage.openImportDeviceProfileView();
@@ -80,8 +86,10 @@ public class CreateDeviceProfileImportTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.importingFile(EMPTY_IMPORT_MESSAGE).isDisplayed());
}
+ @Epic("Device profile smoke tests")
+ @Feature("Create device profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Drop json file and delete it")
public void addFileTiImportAndRemove() {
sideBarMenuView.openDeviceProfiles();
profilesPage.openImportDeviceProfileView();
@@ -93,8 +101,10 @@ public class CreateDeviceProfileImportTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.entityIsNotPresent(IMPORT_DEVICE_PROFILE_NAME));
}
+ @Epic("Device profile smoke tests")
+ @Feature("Create device profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Import device profile with same name")
public void importDeviceProfileWithSameName() {
String name = IMPORT_DEVICE_PROFILE_NAME;
testRestClient.postDeviceProfile(EntityPrototypes.defaultDeviceProfile(name));
@@ -111,8 +121,10 @@ public class CreateDeviceProfileImportTest extends AbstractDriverBaseTest {
Assert.assertEquals(profilesPage.warningMessage().getText(), SAME_NAME_WARNING_DEVICE_PROFILE_MESSAGE);
}
+ @Epic("Device profile smoke tests")
+ @Feature("Create device profile")
@Test(priority = 30, groups = "smoke")
- @Description
+ @Description("Import device profile without refresh")
public void importDeviceProfileWithoutRefresh() {
sideBarMenuView.openDeviceProfiles();
profilesPage.openImportDeviceProfileView();
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileTest.java
index 4e57dc8c11..d5f9791241 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/CreateDeviceProfileTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.deviceProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.openqa.selenium.Keys;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
@@ -54,8 +56,10 @@ public class CreateDeviceProfileTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Device profile smoke tests")
+ @Feature("Create device profile")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Add device profile after specifying the name (text/numbers /special characters)")
public void createDeviceProfile() {
String name = ENTITY_NAME + random();
@@ -70,8 +74,10 @@ public class CreateDeviceProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.entity(name).isDisplayed());
}
+ @Epic("Device profile smoke tests")
+ @Feature("Create device profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Add device profile after specifying the name with details")
public void createDeviceProfileWithDetails() {
String name = ENTITY_NAME + random();
String ruleChain = "Root Rule Chain";
@@ -105,8 +111,10 @@ public class CreateDeviceProfileTest extends AbstractDriverBaseTest {
Assert.assertEquals(description, profilesPage.getDescription());
}
+ @Epic("Device profile smoke tests")
+ @Feature("Create device profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Create a device profile with the same name")
public void createDeviseProfileWithSameName() {
String name = ENTITY_NAME + random();
testRestClient.postDeviceProfile(EntityPrototypes.defaultDeviceProfile(name));
@@ -124,8 +132,10 @@ public class CreateDeviceProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.addDeviceProfileView().isDisplayed());
}
+ @Epic("Device profile smoke tests")
+ @Feature("Create device profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Add device profile without the name")
public void createDeviceProfileWithoutName() {
sideBarMenuView.openDeviceProfiles();
profilesPage.openCreateDeviceProfileView();
@@ -137,8 +147,10 @@ public class CreateDeviceProfileTest extends AbstractDriverBaseTest {
Assert.assertEquals(profilesPage.errorMessage().getText(), NAME_IS_REQUIRED_MESSAGE);
}
+ @Epic("Device profile smoke tests")
+ @Feature("Create device profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Create device profile only with spase in name")
public void createDeviseProfileWithOnlySpaceInName() {
sideBarMenuView.openDeviceProfiles();
profilesPage.openCreateDeviceProfileView();
@@ -152,8 +164,10 @@ public class CreateDeviceProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.addDeviceProfileView().isDisplayed());
}
+ @Epic("Device profile smoke tests")
+ @Feature("Create device profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Add device profile after specifying the name (text/numbers /special characters) without refresh")
public void createDeviceProfileWithoutRefresh() {
String name = ENTITY_NAME + random();
@@ -167,8 +181,10 @@ public class CreateDeviceProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.entity(name).isDisplayed());
}
+ @Epic("Device profile smoke tests")
+ @Feature("Create device profile")
@Test(priority = 30, groups = "smoke")
- @Description
+ @Description("Go to device profile documentation page")
public void documentation() {
String urlPath = "docs/user-guide/device-profiles/";
@@ -176,7 +192,7 @@ public class CreateDeviceProfileTest extends AbstractDriverBaseTest {
profilesPage.profileNames().get(0).click();
profilesPage.goToProfileHelpPage();
- Assert.assertTrue(urlContains(urlPath));
+ Assert.assertTrue(urlContains(urlPath), "URL contains " + urlPath);
}
}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeleteDeviceProfileTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeleteDeviceProfileTest.java
index 3dd6c2b28d..ccc97b8c94 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeleteDeviceProfileTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeleteDeviceProfileTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.deviceProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -40,8 +42,10 @@ public class DeleteDeviceProfileTest extends AbstractDriverBaseTest {
profilesPage = new ProfilesPageHelper(driver);
}
+ @Epic("Device profile smoke tests")
+ @Feature("Delete one device profile")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Remove the device profile by clicking on the trash icon in the right side of device profile")
public void removeDeviceProfile() {
String name = ENTITY_NAME + random();
testRestClient.postDeviceProfile(EntityPrototypes.defaultDeviceProfile(name));
@@ -54,8 +58,10 @@ public class DeleteDeviceProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.entityIsNotPresent(name));
}
+ @Epic("Device profile smoke tests")
+ @Feature("Delete one device profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the device profile by clicking on the 'Delete device profile' btn in the entity view")
public void removeDeviceProfileFromView() {
String name = ENTITY_NAME + random();
testRestClient.postDeviceProfile(EntityPrototypes.defaultDeviceProfile(name));
@@ -69,8 +75,10 @@ public class DeleteDeviceProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.entityIsNotPresent(name));
}
+ @Epic("Device profile smoke tests")
+ @Feature("Delete one device profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove device profile by mark in the checkbox and then click on the trash can icon in the menu that appears at the top")
public void removeSelectedDeviceProfile() {
String name = ENTITY_NAME + random();
testRestClient.postDeviceProfile(EntityPrototypes.defaultDeviceProfile(name));
@@ -84,16 +92,20 @@ public class DeleteDeviceProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.entityIsNotPresent(name));
}
+ @Epic("Device profile smoke tests")
+ @Feature("Delete one device profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the default device profile by clicking on the trash icon in the right side of device profile")
public void removeDefaultDeviceProfile() {
sideBarMenuView.openDeviceProfiles();
Assert.assertFalse(profilesPage.deleteBtn("default").isEnabled());
}
+ @Epic("Device profile smoke tests")
+ @Feature("Delete one device profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the Default device profile by clicking on the 'Delete device profile' btn in the entity view")
public void removeDefaultDeviceProfileFromView() {
sideBarMenuView.openDeviceProfiles();
profilesPage.entity("default").click();
@@ -101,17 +113,20 @@ public class DeleteDeviceProfileTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.deleteDeviceProfileFromViewBtnIsNotDisplayed());
}
+ @Epic("Device profile smoke tests")
+ @Feature("Delete one device profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the default device profile by mark in the checkbox and then click on the trash can icon in the menu that appears at the top")
public void removeSelectedDefaultDeviceProfile() {
sideBarMenuView.openDeviceProfiles();
Assert.assertNotNull(profilesPage.presentCheckBox("default"));
Assert.assertFalse(profilesPage.presentCheckBox("default").isDisplayed());
}
-
+ @Epic("Device profile smoke tests")
+ @Feature("Delete one device profile")
@Test(priority = 30, groups = "smoke")
- @Description
+ @Description("Remove the device profile by clicking on the trash icon in the right side of device profile without refresh")
public void removeDeviceProfileWithoutRefresh() {
String name = ENTITY_NAME + random();
testRestClient.postDeviceProfile(EntityPrototypes.defaultDeviceProfile(name));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeleteSeveralDeviceProfilesTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeleteSeveralDeviceProfilesTest.java
index 8459474c38..a6b0b4e930 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeleteSeveralDeviceProfilesTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeleteSeveralDeviceProfilesTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.deviceProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -39,8 +41,10 @@ public class DeleteSeveralDeviceProfilesTest extends AbstractDriverBaseTest {
profilesPage = new ProfilesPageHelper(driver);
}
+ @Epic("Device profile smoke tests")
+ @Feature("Delete several device profiles")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Remove several device profiles by mark in the checkbox and then click on the trash can icon in the menu that appears at the top")
public void canDeleteSeveralDeviceProfilesByTopBtn() {
String name1 = ENTITY_NAME + random() + "1";
String name2 = ENTITY_NAME + random() + "2";
@@ -57,8 +61,10 @@ public class DeleteSeveralDeviceProfilesTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.profileIsNotPresent(name2));
}
+ @Epic("Device profile smoke tests")
+ @Feature("Delete several device profiles")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove several device profiles by mark all the device profiles on the page by clicking in the topmost checkbox and then clicking on the trash icon in the menu that appears")
public void selectAllDeviceProfiles() {
sideBarMenuView.openDeviceProfiles();
profilesPage.selectAllCheckBox().click();
@@ -69,8 +75,10 @@ public class DeleteSeveralDeviceProfilesTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.warningPopUpTitle().getText().contains(String.valueOf(profilesPage.markCheckbox().size())));
}
+ @Epic("Device profile smoke tests")
+ @Feature("Delete several device profiles")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the default device profile by mark all the device profiles on the page by clicking in the topmost checkbox and then clicking on the trash icon in the menu that appears")
public void removeDefaultDeviceProfile() {
sideBarMenuView.openDeviceProfiles();
profilesPage.selectAllCheckBox().click();
@@ -79,8 +87,10 @@ public class DeleteSeveralDeviceProfilesTest extends AbstractDriverBaseTest {
Assert.assertFalse(profilesPage.deleteBtn("default").isEnabled());
}
+ @Epic("Device profile smoke tests")
+ @Feature("Delete several device profiles")
@Test(priority = 30, groups = "smoke")
- @Description
+ @Description("Remove several device profiles by mark in the checkbox and then click on the trash can icon in the menu that appears at the top without refresh")
public void deleteSeveralDeviceProfilesByTopBtnWithoutRefresh() {
String name1 = ENTITY_NAME + random() + "1";
String name2 = ENTITY_NAME + random() + "2";
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeviceProfileEditMenuTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeviceProfileEditMenuTest.java
index 9d889f2e54..12a7ed5616 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeviceProfileEditMenuTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/DeviceProfileEditMenuTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.deviceProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.openqa.selenium.Keys;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
@@ -54,8 +56,10 @@ public class DeviceProfileEditMenuTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Device profile smoke tests")
+ @Feature("Edit device profile")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Change name by edit menu")
public void changeName() {
String name = ENTITY_NAME + random();
String newName = "Changed" + getRandomNumber();
@@ -77,8 +81,10 @@ public class DeviceProfileEditMenuTest extends AbstractDriverBaseTest {
Assert.assertEquals(titleAfter, newName);
}
+ @Epic("Device profile smoke tests")
+ @Feature("Edit device profile")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Delete name and save")
public void deleteName() {
String name = ENTITY_NAME + random();
testRestClient.postDeviceProfile(EntityPrototypes.defaultDeviceProfile(name));
@@ -92,8 +98,10 @@ public class DeviceProfileEditMenuTest extends AbstractDriverBaseTest {
Assert.assertFalse(profilesPage.doneBtnEditViewVisible().isEnabled());
}
+ @Epic("Device profile smoke tests")
+ @Feature("Edit device profile")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Save only with space in name")
public void saveWithOnlySpaceInName() {
String name = ENTITY_NAME + random();
testRestClient.postDeviceProfile(EntityPrototypes.defaultDeviceProfile(name));
@@ -110,8 +118,10 @@ public class DeviceProfileEditMenuTest extends AbstractDriverBaseTest {
Assert.assertEquals(profilesPage.warningMessage().getText(), EMPTY_DEVICE_PROFILE_MESSAGE);
}
+ @Epic("Device profile smoke tests")
+ @Feature("Edit device profile")
@Test(priority = 30, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "editMenuDescription")
- @Description
+ @Description("Write the description and save the changes/Change the description and save the changes/Delete the description and save the changes")
public void editDescription(String description, String newDescription, String finalDescription) {
String name = ENTITY_NAME + random();
testRestClient.postDeviceProfile(EntityPrototypes.defaultDeviceProfile(name, description));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/MakeDeviceProfileDefaultTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/MakeDeviceProfileDefaultTest.java
index 9ef711eee4..c9b746bdd6 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/MakeDeviceProfileDefaultTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/MakeDeviceProfileDefaultTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.deviceProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -41,8 +43,10 @@ public class MakeDeviceProfileDefaultTest extends AbstractDriverBaseTest {
testRestClient.setDefaultDeviceProfile(getDeviceProfileByName("default").getId());
}
+ @Epic("Device profile smoke tests")
+ @Feature("Make device profile default")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Make device profile default by clicking on the 'Make device profile default' icon in the right corner")
public void makeDeviceProfileDefaultByRightCornerBtn() {
sideBarMenuView.openDeviceProfiles();
profilesPage.setProfileName();
@@ -53,8 +57,10 @@ public class MakeDeviceProfileDefaultTest extends AbstractDriverBaseTest {
Assert.assertTrue(profilesPage.defaultCheckbox(profile).isDisplayed());
}
+ @Epic("Device profile smoke tests")
+ @Feature("Make device profile default")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Make device profile default by clicking on the 'Make device profile default' button in the entity view")
public void makeDeviceProfileDefaultFromView() {
sideBarMenuView.openDeviceProfiles();
profilesPage.setProfileName();
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SearchDeviceProfileTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SearchDeviceProfileTest.java
index bb6b82b3a9..8482fa6cbc 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SearchDeviceProfileTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SearchDeviceProfileTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.deviceProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -49,8 +51,11 @@ public class SearchDeviceProfileTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Device profiles smoke")
+ @Feature("Search device profile")
@Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "deviceProfileSearch")
- @Description
+ @Description("Search device profile by first word in the name/Search device profile by second word in the name/" +
+ "Search device profile by symbol in the name/Search asset profile by number in the name")
public void searchFirstWord(String name, String namePath) {
testRestClient.postDeviceProfile(defaultDeviceProfile(name));
this.name = name;
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SortByNameTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SortByNameTest.java
index a6c1a7b0cf..93dc659f6e 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SortByNameTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/deviceProfileSmoke/SortByNameTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.deviceProfileSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -48,8 +50,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Device profiles smoke")
+ @Feature("Sort device profile by name")
@Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSort")
- @Description
+ @Description("Sort device profile 'UP'")
public void specialCharacterUp(String name) {
testRestClient.postDeviceProfile(defaultDeviceProfile(name));
this.name = name;
@@ -61,8 +65,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
Assert.assertEquals(profilesPage.getProfileName(), name);
}
+ @Epic("Device profiles smoke")
+ @Feature("Sort device profile by name")
@Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForAllSort")
- @Description
+ @Description("Sort device profile 'UP'")
public void allSortUp(String deviceProfile, String deviceProfileSymbol, String deviceProfileNumber) {
testRestClient.postDeviceProfile(defaultDeviceProfile(deviceProfileSymbol));
testRestClient.postDeviceProfile(defaultDeviceProfile(deviceProfile));
@@ -86,8 +92,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
Assert.assertEquals(thirdDeviceProfile, deviceProfile);
}
+ @Epic("Device profiles smoke")
+ @Feature("Sort device profile by name")
@Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSort")
- @Description
+ @Description("Sort device profile 'DOWN'")
public void specialCharacterDown(String name) {
testRestClient.postDeviceProfile(defaultDeviceProfile(name));
this.name = name;
@@ -99,8 +107,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
Assert.assertEquals(profilesPage.getProfileName(), name);
}
+ @Epic("Device profiles smoke")
+ @Feature("Sort device profile by name")
@Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForAllSort")
- @Description
+ @Description("Sort device profile 'DOWN'")
public void allSortDown(String deviceProfile, String deviceProfileSymbol, String deviceProfileNumber) {
testRestClient.postDeviceProfile(defaultDeviceProfile(deviceProfileSymbol));
testRestClient.postDeviceProfile(defaultDeviceProfile(deviceProfile));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/CreateRuleChainImportTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/CreateRuleChainImportTest.java
index c3a0a88ff1..dd2f8a5895 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/CreateRuleChainImportTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/CreateRuleChainImportTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.openqa.selenium.WebElement;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
@@ -59,8 +61,10 @@ public class CreateRuleChainImportTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Import rule chain")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Drop json file")
public void importRuleChain() {
sideBarMenuView.ruleChainsBtn().click();
ruleChainsPage.openImportRuleChainView();
@@ -70,8 +74,10 @@ public class CreateRuleChainImportTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.importingFile(IMPORT_RULE_CHAIN_FILE_NAME).isDisplayed());
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Import rule chain")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Drop json file and delete it")
public void importRuleChainAndDeleteFile() {
sideBarMenuView.ruleChainsBtn().click();
ruleChainsPage.openImportRuleChainView();
@@ -83,8 +89,10 @@ public class CreateRuleChainImportTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.entityIsNotPresent(IMPORT_RULE_CHAIN_FILE_NAME));
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Import rule chain")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Import txt file")
public void importTxtFile() {
sideBarMenuView.ruleChainsBtn().click();
ruleChainsPage.openImportRuleChainView();
@@ -94,8 +102,10 @@ public class CreateRuleChainImportTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.importingFile(EMPTY_IMPORT_MESSAGE).isDisplayed());
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Import rule chain")
@Test(priority = 30, groups = "smoke")
- @Description
+ @Description("Import rule chain")
public void importRuleChainAndSave() {
sideBarMenuView.ruleChainsBtn().click();
ruleChainsPage.openImportRuleChainView();
@@ -103,7 +113,6 @@ public class CreateRuleChainImportTest extends AbstractDriverBaseTest {
ruleChainsPage.importBrowseFileBtn().click();
WebElement doneBtn = openRuleChainPage.doneBtn();
doneBtn.click();
- //openRuleChainPage.waitUntilBtnDisable(doneBtn);
ruleChainName = IMPORT_RULE_CHAIN_NAME;
sideBarMenuView.ruleChainsBtn().click();
@@ -111,8 +120,10 @@ public class CreateRuleChainImportTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.entity(IMPORT_RULE_CHAIN_NAME).isDisplayed());
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Import rule chain")
@Test(priority = 40, groups = "smoke")
- @Description
+ @Description("Import rule chain with same name")
public void importRuleChainAndSaveWithSameName() {
String ruleChainName = IMPORT_RULE_CHAIN_NAME;
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
@@ -124,7 +135,6 @@ public class CreateRuleChainImportTest extends AbstractDriverBaseTest {
ruleChainsPage.importBrowseFileBtn().click();
WebElement doneBtn = openRuleChainPage.doneBtn();
doneBtn.click();
- //openRuleChainPage.waitUntilBtnDisable(doneBtn);
sideBarMenuView.ruleChainsBtn().click();
boolean entityNotNull = ruleChainsPage.entity(ruleChainName) != null;
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/CreateRuleChainTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/CreateRuleChainTest.java
index 4e1ec60f10..e8bdaff9da 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/CreateRuleChainTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/CreateRuleChainTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -53,8 +55,10 @@ public class CreateRuleChainTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Create rule chain")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Add rule chain after specifying the name (text/numbers /special characters)")
public void createRuleChain() {
String ruleChainName = ENTITY_NAME + random();
@@ -70,8 +74,10 @@ public class CreateRuleChainTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.entity(ruleChainName).isDisplayed());
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Create rule chain")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Add rule chain after specifying the name and description (text/numbers /special characters)")
public void createRuleChainWithDescription() {
String ruleChainName = ENTITY_NAME + random();
@@ -89,8 +95,10 @@ public class CreateRuleChainTest extends AbstractDriverBaseTest {
Assert.assertEquals(ruleChainsPage.descriptionEntityView().getAttribute("value"), ruleChainName);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Create rule chain")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Add rule chain without the name")
public void createRuleChainWithoutName() {
sideBarMenuView.ruleChainsBtn().click();
ruleChainsPage.openCreateRuleChainView();
@@ -98,8 +106,10 @@ public class CreateRuleChainTest extends AbstractDriverBaseTest {
Assert.assertFalse(ruleChainsPage.addBtnV().isEnabled());
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Create rule chain")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Create rule chain only with spase in name")
public void createRuleChainWithOnlySpace() {
sideBarMenuView.ruleChainsBtn().click();
ruleChainsPage.openCreateRuleChainView();
@@ -113,8 +123,10 @@ public class CreateRuleChainTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.addEntityView().isDisplayed());
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Create rule chain")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Create a rule chain with the same name")
public void createRuleChainWithSameName() {
String ruleChainName = ENTITY_NAME + random();
testRestClient.postRuleChain(EntityPrototypes.defaultRuleChainPrototype(ruleChainName));
@@ -138,8 +150,10 @@ public class CreateRuleChainTest extends AbstractDriverBaseTest {
entityIsDisplayed.forEach(Assert::assertTrue);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Create rule chain")
@Test(priority = 30, groups = "smoke")
- @Description
+ @Description("Add rule chain after specifying the name (text/numbers /special characters) without refresh")
public void createRuleChainWithoutRefresh() {
String ruleChainName = ENTITY_NAME + random();
@@ -153,8 +167,10 @@ public class CreateRuleChainTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.entity(ruleChainName).isDisplayed());
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Create rule chain")
@Test(priority = 40, groups = "smoke")
- @Description
+ @Description("Go to rule chain documentation page")
public void documentation() {
String urlPath = "docs/user-guide/ui/rule-chains/";
@@ -163,6 +179,6 @@ public class CreateRuleChainTest extends AbstractDriverBaseTest {
ruleChainsPage.detailsBtn(ruleChainsPage.getRuleChainName()).click();
ruleChainsPage.goToHelpPage();
- Assert.assertTrue(urlContains(urlPath));
+ Assert.assertTrue(urlContains(urlPath), "URL contains " + urlPath);
}
}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/DeleteRuleChainTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/DeleteRuleChainTest.java
index d7e9065ddb..e1f27d7bab 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/DeleteRuleChainTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/DeleteRuleChainTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -41,8 +43,10 @@ public class DeleteRuleChainTest extends AbstractDriverBaseTest {
ruleChainsPage = new RuleChainsPageHelper(driver);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Delete rule chain")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Remove the rule chain by clicking on the trash icon in the right side of rule chain")
public void removeRuleChainByRightSideBtn() {
String ruleChainName = ENTITY_NAME + random();
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
@@ -54,8 +58,10 @@ public class DeleteRuleChainTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.entityIsNotPresent(deletedRuleChain));
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Delete rule chain")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove rule chain by mark in the checkbox and then click on the trash can icon in the menu that appears at the top")
public void removeSelectedRuleChain() {
String ruleChainName = ENTITY_NAME + random();
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
@@ -67,8 +73,10 @@ public class DeleteRuleChainTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.entityIsNotPresent(deletedRuleChain));
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Delete rule chain")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the rule chain by clicking on the 'Delete rule chain' btn in the entity view")
public void removeFromRuleChainView() {
String ruleChainName = ENTITY_NAME + random();
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
@@ -81,34 +89,41 @@ public class DeleteRuleChainTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.entityIsNotPresent(deletedRuleChain));
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Delete rule chain")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the root rule chain by clicking on the trash icon in the right side of rule chain")
public void removeRootRuleChain() {
sideBarMenuView.ruleChainsBtn().click();
Assert.assertFalse(ruleChainsPage.deleteBtn(ROOT_RULE_CHAIN_NAME).isEnabled());
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Delete rule chain")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove root rule chain by mark in the checkbox and then click on the trash can icon in the menu that appears at the top")
public void removeSelectedRootRuleChain() {
sideBarMenuView.ruleChainsBtn().click();
ruleChainsPage.assertCheckBoxIsNotDisplayed(ROOT_RULE_CHAIN_NAME);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Delete rule chain")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the root rule chain by clicking on the 'Delete rule chain' btn in the entity view")
public void removeFromRootRuleChainView() {
sideBarMenuView.ruleChainsBtn().click();
ruleChainsPage.detailsBtn(ROOT_RULE_CHAIN_NAME).click();
- ruleChainsPage.deleteBtnFromView();
Assert.assertTrue(ruleChainsPage.deleteBtnInRootRuleChainIsNotDisplayed());
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Delete rule chain")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Remove the rule chain with device profile by clicking on the trash icon in the right side of rule chain")
public void removeProfileRuleChainByRightSideBtn() {
String deletedRuleChain = "Thermostat";
@@ -124,8 +139,10 @@ public class DeleteRuleChainTest extends AbstractDriverBaseTest {
Assert.assertEquals(ruleChainsPage.warningMessage().getText(), DELETE_RULE_CHAIN_WITH_PROFILE_MESSAGE);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Delete rule chain")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the rule chain with device profile by mark in the checkbox and then click on the trash can icon in the menu that appears at the top")
public void removeSelectedProfileRuleChain() {
sideBarMenuView.ruleChainsBtn().click();
String deletedRuleChain = ruleChainsPage.deleteSelected("Thermostat");
@@ -138,8 +155,10 @@ public class DeleteRuleChainTest extends AbstractDriverBaseTest {
Assert.assertEquals(ruleChainsPage.warningMessage().getText(), DELETE_RULE_CHAIN_WITH_PROFILE_MESSAGE);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Delete rule chain")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the rule chain with device profile by clicking on the 'Delete rule chain' btn in the entity view")
public void removeFromProfileRuleChainView() {
String deletedRuleChain = "Thermostat";
@@ -154,8 +173,10 @@ public class DeleteRuleChainTest extends AbstractDriverBaseTest {
Assert.assertEquals(ruleChainsPage.warningMessage().getText(), DELETE_RULE_CHAIN_WITH_PROFILE_MESSAGE);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Delete rule chain")
@Test(priority = 30, groups = "smoke")
- @Description
+ @Description("Remove the rule chain by clicking on the trash icon in the right side of rule chain without refresh")
public void removeRuleChainByRightSideBtnWithoutRefresh() {
String ruleChainName = ENTITY_NAME + random();
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/DeleteSeveralRuleChainsTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/DeleteSeveralRuleChainsTest.java
index cbfe27462f..068fbbf71b 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/DeleteSeveralRuleChainsTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/DeleteSeveralRuleChainsTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -41,8 +43,11 @@ public class DeleteSeveralRuleChainsTest extends AbstractDriverBaseTest {
ruleChainsPage = new RuleChainsPageHelper(driver);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Delete several rule chains")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Remove several rule chains by mark in the checkbox and then click on the trash can icon in the menu " +
+ "that appears at the top")
public void canDeleteSeveralRuleChainsByTopBtn() {
String ruleChainName = ENTITY_NAME + random();
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName + 1));
@@ -55,8 +60,11 @@ public class DeleteSeveralRuleChainsTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.ruleChainsIsNotPresent(ruleChainName));
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Delete several rule chains")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Remove several rule chains by mark all the rule chains on the page by clicking in the topmost checkbox" +
+ " and then clicking on the trash icon in the menu that appears")
public void selectAllRuleChain() {
String ruleChainName = ENTITY_NAME + random();
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName + 1));
@@ -71,8 +79,11 @@ public class DeleteSeveralRuleChainsTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.ruleChainsIsNotPresent(ruleChainName));
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Delete several rule chains")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the root rule chain by mark in the checkbox and then click on the trash can icon in the menu " +
+ "that appears at the top")
public void removeRootRuleChain() {
sideBarMenuView.ruleChainsBtn().click();
ruleChainsPage.selectAllCheckBox().click();
@@ -80,8 +91,11 @@ public class DeleteSeveralRuleChainsTest extends AbstractDriverBaseTest {
Assert.assertFalse(ruleChainsPage.deleteBtn(ROOT_RULE_CHAIN_NAME).isEnabled());
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Delete several rule chains")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Remove the root rule chain by mark all the rule chains on the page by clicking in the topmost checkbox" +
+ " and then clicking on the trash icon in the menu that appears")
public void removeSelectedRootRuleChain() {
sideBarMenuView.ruleChainsBtn().click();
ruleChainsPage.selectAllCheckBox().click();
@@ -89,8 +103,11 @@ public class DeleteSeveralRuleChainsTest extends AbstractDriverBaseTest {
ruleChainsPage.assertCheckBoxIsNotDisplayed(ROOT_RULE_CHAIN_NAME);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Delete several rule chains")
@Test(priority = 30, groups = "smoke")
- @Description
+ @Description("Remove several rule chains by mark in the checkbox and then click on the trash can icon in the menu " +
+ "that appears at the top without refresh")
public void deleteSeveralRuleChainsByTopBtnWithoutRefresh() {
String ruleChainName = ENTITY_NAME + random();
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName + 1));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/MakeRuleChainRootTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/MakeRuleChainRootTest.java
index 6b6bbc43bd..cf252ea22a 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/MakeRuleChainRootTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/MakeRuleChainRootTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -42,8 +44,10 @@ public class MakeRuleChainRootTest extends AbstractDriverBaseTest {
testRestClient.setRootRuleChain(getRuleChainByName("Root Rule Chain").getId());
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Make rule chain root")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Make rule chain root by clicking on the 'Make rule chain root' icon in the right corner")
public void makeRuleChainRootByRightCornerBtn() {
sideBarMenuView.ruleChainsBtn().click();
ruleChainsPage.setRuleChainNameWithoutRoot(0);
@@ -54,8 +58,10 @@ public class MakeRuleChainRootTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.rootCheckBoxEnable(ruleChain).isDisplayed());
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Make rule chain root")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Make rule chain root by clicking on the 'Make rule chain root' button in the entity view")
public void makeRuleChainRootFromView() {
sideBarMenuView.ruleChainsBtn().click();
ruleChainsPage.setRuleChainNameWithoutRoot(0);
@@ -68,8 +74,10 @@ public class MakeRuleChainRootTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.rootCheckBoxEnable(ruleChain).isDisplayed());
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Make rule chain root")
@Test(priority = 30, groups = "smoke")
- @Description
+ @Description("Make multiple root rule chains (only one rule chain can be root)")
public void multiplyRoot() {
sideBarMenuView.ruleChainsBtn().click();
ruleChainsPage.setRuleChainNameWithoutRoot(0);
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/OpenRuleChainTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/OpenRuleChainTest.java
index 05c59b0d7d..220b718ae8 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/OpenRuleChainTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/OpenRuleChainTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -53,8 +55,10 @@ public class OpenRuleChainTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Open rule chain")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Open the rule chain by clicking on its name")
public void openRuleChainByRightCornerBtn() {
String ruleChainName = ENTITY_NAME + random();
testRestClient.postRuleChain(EntityPrototypes.defaultRuleChainPrototype(ruleChainName));
@@ -70,8 +74,10 @@ public class OpenRuleChainTest extends AbstractDriverBaseTest {
Assert.assertEquals(ruleChainName, openRuleChainPage.getHeadName());
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Open rule chain")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Open the rule chain by clicking on the 'Open rule chain' button in the entity view")
public void openRuleChainByViewBtn() {
String ruleChainName = ENTITY_NAME + random();
testRestClient.postRuleChain(EntityPrototypes.defaultRuleChainPrototype(ruleChainName));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/RuleChainEditMenuTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/RuleChainEditMenuTest.java
index 144ca0e836..5b380221b6 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/RuleChainEditMenuTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/RuleChainEditMenuTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -54,8 +56,10 @@ public class RuleChainEditMenuTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Edit rule chain")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Change name by edit menu")
public void changeName() {
String newRuleChainName = "Changed" + getRandomNumber();
String ruleChainName = ENTITY_NAME + random();
@@ -77,8 +81,10 @@ public class RuleChainEditMenuTest extends AbstractDriverBaseTest {
Assert.assertEquals(newRuleChainName, nameAfter);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Edit rule chain")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Delete name and save")
public void deleteName() {
String ruleChainName = ENTITY_NAME + random();
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
@@ -92,8 +98,10 @@ public class RuleChainEditMenuTest extends AbstractDriverBaseTest {
Assert.assertFalse(ruleChainsPage.doneBtnEditViewVisible().isEnabled());
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Edit rule chain")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Save only with space")
public void saveOnlyWithSpace() {
String ruleChainName = ENTITY_NAME +random();
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
@@ -110,8 +118,10 @@ public class RuleChainEditMenuTest extends AbstractDriverBaseTest {
Assert.assertEquals(ruleChainsPage.warningMessage().getText(), EMPTY_RULE_CHAIN_MESSAGE);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Edit rule chain")
@Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "editMenuDescription")
- @Description
+ @Description("Write the description and save the changes/Change the description and save the changes/Delete the description and save the changes")
public void editDescription(String description, String newDescription, String finalDescription) {
String name = ENTITY_NAME + random();
testRestClient.postRuleChain(EntityPrototypes.defaultRuleChainPrototype(name, description));
@@ -127,8 +137,10 @@ public class RuleChainEditMenuTest extends AbstractDriverBaseTest {
Assert.assertEquals(ruleChainsPage.getDescription(), finalDescription);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Edit rule chain")
@Test(priority = 20, groups = "smoke")
- @Description
+ @Description("Enable debug mode/Disable debug mode")
public void debugMode() {
String ruleChainName = ENTITY_NAME + random();
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SearchRuleChainTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SearchRuleChainTest.java
index 29c5b21556..b34f59d420 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SearchRuleChainTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SearchRuleChainTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -39,8 +41,10 @@ public class SearchRuleChainTest extends AbstractDriverBaseTest {
ruleChainsPage = new RuleChainsPageHelper(driver);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Search rule chain")
@Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "ruleChainNameForSearchByFirstAndSecondWord")
- @Description
+ @Description("Search rule chain by first word in the name/Search rule chain by second word in the name")
public void searchFirstWord(String namePath) {
sideBarMenuView.ruleChainsBtn().click();
ruleChainsPage.searchEntity(namePath);
@@ -49,8 +53,10 @@ public class SearchRuleChainTest extends AbstractDriverBaseTest {
Assert.assertTrue(ruleChainsPage.getRuleChainName().contains(namePath));
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Search rule chain")
@Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSearchBySymbolAndNumber")
- @Description
+ @Description("Search rule chain by symbol in the name/Search rule chain by number in the name")
public void searchNumber(String name, String namePath) {
testRestClient.postRuleChain(defaultRuleChainPrototype(name));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SortByNameTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SortByNameTest.java
index 84cd1028e8..3a5f02d61d 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SortByNameTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SortByNameTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -49,8 +51,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Sort rule chain by name")
@Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSort")
- @Description
+ @Description("Sort rule chain 'UP'")
public void specialCharacterUp(String ruleChainName) {
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
this.ruleChainName = ruleChainName;
@@ -62,8 +66,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
Assert.assertEquals(ruleChainsPage.getRuleChainName(), ruleChainName);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Sort rule chain by name")
@Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForAllSort")
- @Description
+ @Description("Sort rule chain 'UP'")
public void allSortUp(String ruleChain, String ruleChainSymbol, String ruleChainNumber) {
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainSymbol));
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChain));
@@ -87,8 +93,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
Assert.assertEquals(thirdRuleChain, ruleChain);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Sort rule chain by name")
@Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSort")
- @Description
+ @Description("Sort rule chain 'DOWN'")
public void specialCharacterDown(String ruleChainName) {
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
this.ruleChainName = ruleChainName;
@@ -100,8 +108,10 @@ public class SortByNameTest extends AbstractDriverBaseTest {
Assert.assertEquals(ruleChainsPage.getRuleChainName(), ruleChainName);
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Sort rule chain by name")
@Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForAllSort")
- @Description
+ @Description("Sort rule chain 'DOWN'")
public void allSortDown(String ruleChain, String ruleChainSymbol, String ruleChainNumber) {
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainSymbol));
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChain));
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SortByTimeTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SortByTimeTest.java
index 4654654fb5..6c8a912526 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SortByTimeTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SortByTimeTest.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
import io.qameta.allure.Description;
+import io.qameta.allure.Epic;
+import io.qameta.allure.Feature;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -49,8 +51,10 @@ public class SortByTimeTest extends AbstractDriverBaseTest {
}
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Sort rule chain by time")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Sort rule chain 'DOWN'")
public void sortByTimeDown() {
String ruleChain = ENTITY_NAME;
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChain));
@@ -65,8 +69,10 @@ public class SortByTimeTest extends AbstractDriverBaseTest {
Assert.assertNotNull(ruleChainsPage.createdTimeEntity(ruleChain, lastCreated));
}
+ @Epic("Rule chains smoke tests")
+ @Feature("Sort rule chain by time")
@Test(priority = 10, groups = "smoke")
- @Description
+ @Description("Sort rule chain 'UP'")
public void sortByTimeUp() {
String ruleChain = ENTITY_NAME;
testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChain));
diff --git a/pom.xml b/pom.xml
index 7b6d565ead..b577bf4142 100755
--- a/pom.xml
+++ b/pom.xml
@@ -67,6 +67,7 @@
2.13.4
2.13.4.2
1.3.4
+ 4.2.1
2.2.6
3.0.0
2.0.0-M5
@@ -828,6 +829,7 @@
**/apache/cassandra/io/**
.run/**
**/NetworkReceive.java
+ **/lwm2m-registry/**
JAVADOC_STYLE
@@ -1431,6 +1433,11 @@
classmate
${fasterxml-classmate.version}
+
+ com.auth0
+ java-jwt
+ ${auth0-jwt.version}
+
com.github.fge
json-schema-validator
diff --git a/rest-client/pom.xml b/rest-client/pom.xml
index cb85c2134e..1bf93e4dc4 100644
--- a/rest-client/pom.xml
+++ b/rest-client/pom.xml
@@ -47,6 +47,10 @@
org.thingsboard.common
util
+
+ com.auth0
+ java-jwt
+
diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java
index 33524a9fa1..e84113524a 100644
--- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java
+++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java
@@ -15,6 +15,7 @@
*/
package org.thingsboard.rest.client;
+import com.auth0.jwt.JWT;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -28,9 +29,6 @@ import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
-import org.springframework.http.client.ClientHttpRequestExecution;
-import org.springframework.http.client.ClientHttpRequestInterceptor;
-import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.support.HttpRequestWrapper;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@@ -67,6 +65,8 @@ import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.UpdateMessage;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm;
+import org.thingsboard.server.common.data.alarm.AlarmComment;
+import org.thingsboard.server.common.data.alarm.AlarmCommentInfo;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
@@ -85,6 +85,7 @@ import org.thingsboard.server.common.data.edge.EdgeInfo;
import org.thingsboard.server.common.data.edge.EdgeInstallInstructions;
import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
+import org.thingsboard.server.common.data.id.AlarmCommentId;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.AssetProfileId;
@@ -162,7 +163,6 @@ import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import java.io.Closeable;
-import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
@@ -172,6 +172,7 @@ import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.StringUtils.isEmpty;
@@ -179,16 +180,24 @@ import static org.thingsboard.server.common.data.StringUtils.isEmpty;
/**
* @author Andrew Shvayka
*/
-public class RestClient implements ClientHttpRequestInterceptor, Closeable {
+public class RestClient implements Closeable {
private static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization";
+ private static final long AVG_REQUEST_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
+ protected static final String ACTIVATE_TOKEN_REGEX = "/api/noauth/activate?activateToken=";
+
+ private final ObjectMapper objectMapper = new ObjectMapper();
+ private final ExecutorService service = ThingsBoardExecutors.newWorkStealingPool(10, getClass());
protected final RestTemplate restTemplate;
+ protected final RestTemplate loginRestTemplate;
protected final String baseURL;
- private String token;
- private String refreshToken;
- private final ObjectMapper objectMapper = new ObjectMapper();
- private ExecutorService service = ThingsBoardExecutors.newWorkStealingPool(10, getClass());
- protected static final String ACTIVATE_TOKEN_REGEX = "/api/noauth/activate?activateToken=";
+ private String username;
+ private String password;
+ private String mainToken;
+ private String refreshToken;
+ private long mainTokenExpTs;
+ private long refreshTokenExpTs;
+ private long clientServerTimeDiff;
public RestClient(String baseURL) {
this(new RestTemplate(), baseURL);
@@ -196,23 +205,25 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable {
public RestClient(RestTemplate restTemplate, String baseURL) {
this.restTemplate = restTemplate;
+ this.loginRestTemplate = new RestTemplate(restTemplate.getRequestFactory());
this.baseURL = baseURL;
- }
-
- @Override
- public ClientHttpResponse intercept(HttpRequest request, byte[] bytes, ClientHttpRequestExecution execution) throws IOException {
- HttpRequest wrapper = new HttpRequestWrapper(request);
- wrapper.getHeaders().set(JWT_TOKEN_HEADER_PARAM, "Bearer " + token);
- ClientHttpResponse response = execution.execute(wrapper, bytes);
- if (response.getStatusCode() == HttpStatus.UNAUTHORIZED) {
- synchronized (this) {
- restTemplate.getInterceptors().remove(this);
- refreshToken();
- wrapper.getHeaders().set(JWT_TOKEN_HEADER_PARAM, "Bearer " + token);
- return execution.execute(wrapper, bytes);
+ this.restTemplate.getInterceptors().add((request, bytes, execution) -> {
+ HttpRequest wrapper = new HttpRequestWrapper(request);
+ long calculatedTs = System.currentTimeMillis() + clientServerTimeDiff + AVG_REQUEST_TIMEOUT;
+ if (calculatedTs > mainTokenExpTs) {
+ synchronized (RestClient.this) {
+ if (calculatedTs > mainTokenExpTs) {
+ if (calculatedTs < refreshTokenExpTs) {
+ refreshToken();
+ } else {
+ doLogin();
+ }
+ }
+ }
}
- }
- return response;
+ wrapper.getHeaders().set(JWT_TOKEN_HEADER_PARAM, "Bearer " + mainToken);
+ return execution.execute(wrapper, bytes);
+ });
}
public RestTemplate getRestTemplate() {
@@ -220,7 +231,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable {
}
public String getToken() {
- return token;
+ return mainToken;
}
public String getRefreshToken() {
@@ -230,22 +241,32 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable {
public void refreshToken() {
Map refreshTokenRequest = new HashMap<>();
refreshTokenRequest.put("refreshToken", refreshToken);
- ResponseEntity tokenInfo = restTemplate.postForEntity(baseURL + "/api/auth/token", refreshTokenRequest, JsonNode.class);
- setTokenInfo(tokenInfo.getBody());
+ long ts = System.currentTimeMillis();
+ ResponseEntity tokenInfo = loginRestTemplate.postForEntity(baseURL + "/api/auth/token", refreshTokenRequest, JsonNode.class);
+ setTokenInfo(ts, tokenInfo.getBody());
}
public void login(String username, String password) {
+ this.username = username;
+ this.password = password;
+ doLogin();
+ }
+
+ private void doLogin() {
+ long ts = System.currentTimeMillis();
Map loginRequest = new HashMap<>();
loginRequest.put("username", username);
loginRequest.put("password", password);
- ResponseEntity tokenInfo = restTemplate.postForEntity(baseURL + "/api/auth/login", loginRequest, JsonNode.class);
- setTokenInfo(tokenInfo.getBody());
+ ResponseEntity tokenInfo = loginRestTemplate.postForEntity(baseURL + "/api/auth/login", loginRequest, JsonNode.class);
+ setTokenInfo(ts, tokenInfo.getBody());
}
- private void setTokenInfo(JsonNode tokenInfo) {
- this.token = tokenInfo.get("token").asText();
+ private synchronized void setTokenInfo(long ts, JsonNode tokenInfo) {
+ this.mainToken = tokenInfo.get("token").asText();
this.refreshToken = tokenInfo.get("refreshToken").asText();
- restTemplate.getInterceptors().add(this);
+ this.mainTokenExpTs = JWT.decode(this.mainToken).getExpiresAtAsInstant().toEpochMilli();
+ this.refreshTokenExpTs = JWT.decode(refreshToken).getExpiresAtAsInstant().toEpochMilli();
+ this.clientServerTimeDiff = JWT.decode(this.mainToken).getIssuedAtAsInstant().toEpochMilli() - ts;
}
public Optional getAdminSettings(String key) {
@@ -477,6 +498,29 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable {
return restTemplate.postForEntity(baseURL + "/api/alarm", alarm, Alarm.class).getBody();
}
+ public AlarmComment saveAlarmComment(AlarmId alarmId, AlarmComment alarmComment) {
+ return restTemplate.postForEntity(baseURL + "/api/alarm/{alarmId}/comment", alarmComment, AlarmComment.class, alarmId.getId()).getBody();
+ }
+
+ public void deleteAlarmComment(AlarmId alarmId, AlarmCommentId alarmCommentId) {
+ restTemplate.delete(baseURL + "/api/alarm/{alarmId}/comment/{alarmCommentId}",
+ alarmId.getId(), alarmCommentId.getId());
+ }
+
+ public PageData getAlarmComments(AlarmId alarmId, PageLink pageLink) {
+ String urlSecondPart = "/api/alarm/{alarmId}/comment";
+ Map params = new HashMap<>();
+ params.put("alarmId", alarmId.getId().toString());
+
+ return restTemplate.exchange(
+ baseURL + urlSecondPart + "&" + getUrlParams(pageLink),
+ HttpMethod.GET,
+ HttpEntity.EMPTY,
+ new ParameterizedTypeReference>() {
+ },
+ params).getBody();
+ }
+
public Optional getAssetById(AssetId assetId) {
try {
ResponseEntity asset = restTemplate.getForEntity(baseURL + "/api/asset/{assetId}", Asset.class, assetId.getId());
@@ -3558,9 +3602,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable {
@Override
public void close() {
- if (service != null) {
- service.shutdown();
- }
+ service.shutdown();
}
}
diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java
index 3ce142fa63..0102d9c645 100644
--- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java
+++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java
@@ -47,6 +47,7 @@ public interface MailService {
void sendTwoFaVerificationEmail(String email, String verificationCode, int expirationTimeSeconds) throws ThingsboardException;
void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail) throws ThingsboardException;
+
void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender, long timeout) throws ThingsboardException;
void sendApiFeatureStateEmail(ApiFeature apiFeature, ApiUsageStateValue stateValue, String email, ApiUsageStateMailMessage msg) throws ThingsboardException;
diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java
index eccc4f1423..b263e8da17 100644
--- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java
+++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java
@@ -19,8 +19,11 @@ import org.thingsboard.server.common.data.id.NotificationId;
import org.thingsboard.server.common.data.id.NotificationRequestId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
+import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import org.thingsboard.server.common.data.notification.NotificationRequest;
+import java.util.Set;
+
public interface NotificationCenter {
NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest);
@@ -29,11 +32,12 @@ public interface NotificationCenter {
void sendBasicNotification(TenantId tenantId, UserId recipientId, String subject, String text);
-
void markNotificationAsRead(TenantId tenantId, UserId recipientId, NotificationId notificationId);
void markAllNotificationsAsRead(TenantId tenantId, UserId recipientId);
void deleteNotification(TenantId tenantId, UserId recipientId, NotificationId notificationId);
+ Set getAvailableDeliveryMethods(TenantId tenantId);
+
}
diff --git a/ui-ngx/angular.json b/ui-ngx/angular.json
index 53336ab8f7..c30640de2b 100644
--- a/ui-ngx/angular.json
+++ b/ui-ngx/angular.json
@@ -29,6 +29,11 @@
"assets": [
"src/thingsboard.ico",
"src/assets",
+ {
+ "glob": "*.svg",
+ "input": "./node_modules/@mdi/svg/svg/",
+ "output": "/assets/mdi/"
+ },
{
"glob": "worker-html.js",
"input": "./node_modules/ace-builds/src-noconflict/",
diff --git a/ui-ngx/package.json b/ui-ngx/package.json
index 307649d4b2..8e2f2033cb 100644
--- a/ui-ngx/package.json
+++ b/ui-ngx/package.json
@@ -36,6 +36,7 @@
"@material-ui/core": "4.12.3",
"@material-ui/icons": "4.11.2",
"@material-ui/pickers": "3.3.10",
+ "@mdi/svg": "^7.1.96",
"@messageformat/core": "^3.0.1",
"@ngrx/effects": "^15.3.0",
"@ngrx/store": "^15.3.0",
diff --git a/ui-ngx/src/app/app.component.ts b/ui-ngx/src/app/app.component.ts
index 00cb27a890..8f612da5a1 100644
--- a/ui-ngx/src/app/app.component.ts
+++ b/ui-ngx/src/app/app.component.ts
@@ -47,8 +47,13 @@ export class AppComponent implements OnInit {
console.log(`ThingsBoard Version: ${env.tbVersion}`);
- this.matIconRegistry.addSvgIconSetInNamespace('mdi',
- this.domSanitizer.bypassSecurityTrustResourceUrl('./assets/mdi.svg'));
+ this.matIconRegistry.addSvgIconResolver((name, namespace) => {
+ if (namespace === 'mdi') {
+ return this.domSanitizer.bypassSecurityTrustResourceUrl(`./assets/mdi/${name}.svg`);
+ } else {
+ return null;
+ }
+ });
this.matIconRegistry.addSvgIconLiteral(
'google-logo',
diff --git a/ui-ngx/src/app/core/auth/auth.actions.ts b/ui-ngx/src/app/core/auth/auth.actions.ts
index 22bd93291e..67b122d67f 100644
--- a/ui-ngx/src/app/core/auth/auth.actions.ts
+++ b/ui-ngx/src/app/core/auth/auth.actions.ts
@@ -24,7 +24,8 @@ export enum AuthActionTypes {
LOAD_USER = '[Auth] Load User',
UPDATE_USER_DETAILS = '[Auth] Update User Details',
UPDATE_LAST_PUBLIC_DASHBOARD_ID = '[Auth] Update Last Public Dashboard Id',
- UPDATE_HAS_REPOSITORY = '[Auth] Change Has Repository'
+ UPDATE_HAS_REPOSITORY = '[Auth] Change Has Repository',
+ UPDATE_OPENED_MENU_SECTION = '[Preferences] Update Opened Menu Section'
}
export class ActionAuthAuthenticated implements Action {
@@ -61,5 +62,12 @@ export class ActionAuthUpdateHasRepository implements Action {
constructor(readonly payload: { hasRepository: boolean }) {}
}
+export class ActionPreferencesUpdateOpenedMenuSection implements Action {
+ readonly type = AuthActionTypes.UPDATE_OPENED_MENU_SECTION;
+
+ constructor(readonly payload: { path: string; opened: boolean }) {}
+}
+
export type AuthActions = ActionAuthAuthenticated | ActionAuthUnauthenticated |
- ActionAuthLoadUser | ActionAuthUpdateUserDetails | ActionAuthUpdateLastPublicDashboardId | ActionAuthUpdateHasRepository;
+ ActionAuthLoadUser | ActionAuthUpdateUserDetails | ActionAuthUpdateLastPublicDashboardId | ActionAuthUpdateHasRepository |
+ ActionPreferencesUpdateOpenedMenuSection;
diff --git a/ui-ngx/src/app/core/auth/auth.effects.ts b/ui-ngx/src/app/core/auth/auth.effects.ts
new file mode 100644
index 0000000000..d2ebe2b4c5
--- /dev/null
+++ b/ui-ngx/src/app/core/auth/auth.effects.ts
@@ -0,0 +1,42 @@
+///
+/// Copyright © 2016-2023 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import { Injectable } from '@angular/core';
+import { Actions, createEffect, ofType } from '@ngrx/effects';
+import { select, Store } from '@ngrx/store';
+import { AppState } from '@core/core.state';
+import { UserSettingsService } from '@core/http/user-settings.service';
+import { mergeMap, withLatestFrom } from 'rxjs/operators';
+import { AuthActions, AuthActionTypes } from '@core/auth/auth.actions';
+import { selectAuthState } from '@core/auth/auth.selectors';
+
+@Injectable()
+export class AuthEffects {
+ constructor(
+ private actions$: Actions,
+ private store: Store,
+ private userSettingsService: UserSettingsService
+ ) {
+ }
+
+ persistOpenedMenuSections = createEffect(() => this.actions$.pipe(
+ ofType(
+ AuthActionTypes.UPDATE_OPENED_MENU_SECTION,
+ ),
+ withLatestFrom(this.store.pipe(select(selectAuthState))),
+ mergeMap(([action, state]) => this.userSettingsService.putUserSettings({ openedMenuSections: state.userSettings.openedMenuSections }))
+ ), {dispatch: false});
+}
diff --git a/ui-ngx/src/app/core/auth/auth.models.ts b/ui-ngx/src/app/core/auth/auth.models.ts
index 5c35321fb8..8fd1c214b5 100644
--- a/ui-ngx/src/app/core/auth/auth.models.ts
+++ b/ui-ngx/src/app/core/auth/auth.models.ts
@@ -15,6 +15,7 @@
///
import { AuthUser, User } from '@shared/models/user.model';
+import { UserSettings } from '@shared/models/user-settings.models';
export interface SysParamsState {
userTokenAccessEnabled: boolean;
@@ -22,6 +23,7 @@ export interface SysParamsState {
edgesSupportEnabled: boolean;
hasRepository: boolean;
tbelEnabled: boolean;
+ userSettings: UserSettings;
}
export interface AuthPayload extends SysParamsState {
diff --git a/ui-ngx/src/app/core/auth/auth.reducer.ts b/ui-ngx/src/app/core/auth/auth.reducer.ts
index 02054f1a87..51786543e6 100644
--- a/ui-ngx/src/app/core/auth/auth.reducer.ts
+++ b/ui-ngx/src/app/core/auth/auth.reducer.ts
@@ -16,6 +16,7 @@
import { AuthPayload, AuthState } from './auth.models';
import { AuthActions, AuthActionTypes } from './auth.actions';
+import { initialUserSettings } from '@shared/models/user-settings.models';
const emptyUserAuthState: AuthPayload = {
authUser: null,
@@ -25,7 +26,8 @@ const emptyUserAuthState: AuthPayload = {
allowedDashboardIds: [],
edgesSupportEnabled: false,
hasRepository: false,
- tbelEnabled: false
+ tbelEnabled: false,
+ userSettings: initialUserSettings
};
export const initialState: AuthState = {
@@ -35,10 +37,10 @@ export const initialState: AuthState = {
...emptyUserAuthState
};
-export function authReducer(
+export const authReducer = (
state: AuthState = initialState,
action: AuthActions
-): AuthState {
+): AuthState => {
switch (action.type) {
case AuthActionTypes.AUTHENTICATED:
return { ...state, isAuthenticated: true, ...action.payload };
@@ -59,7 +61,19 @@ export function authReducer(
case AuthActionTypes.UPDATE_HAS_REPOSITORY:
return { ...state, ...action.payload};
+ case AuthActionTypes.UPDATE_OPENED_MENU_SECTION:
+ const openedMenuSections = new Set(state.userSettings.openedMenuSections);
+ if (action.payload.opened) {
+ if (!openedMenuSections.has(action.payload.path)) {
+ openedMenuSections.add(action.payload.path);
+ }
+ } else {
+ openedMenuSections.delete(action.payload.path);
+ }
+ const userSettings = {...state.userSettings, ...{ openedMenuSections: Array.from(openedMenuSections)}};
+ return { ...state, ...{ userSettings }};
+
default:
return state;
}
-}
+};
diff --git a/ui-ngx/src/app/core/auth/auth.selectors.ts b/ui-ngx/src/app/core/auth/auth.selectors.ts
index 426c204352..45162565d9 100644
--- a/ui-ngx/src/app/core/auth/auth.selectors.ts
+++ b/ui-ngx/src/app/core/auth/auth.selectors.ts
@@ -20,6 +20,7 @@ import { AppState } from '../core.state';
import { AuthState } from './auth.models';
import { take } from 'rxjs/operators';
import { AuthUser } from '@shared/models/user.model';
+import { UserSettings } from '@shared/models/user-settings.models';
export const selectAuthState = createFeatureSelector< AuthState>(
'auth'
@@ -65,18 +66,45 @@ export const selectTbelEnabled = createSelector(
(state: AuthState) => state.tbelEnabled
);
-export function getCurrentAuthState(store: Store): AuthState {
+export const selectUserSettings = createSelector(
+ selectAuthState,
+ (state: AuthState) => state.userSettings
+);
+
+export const selectOpenedMenuSections = createSelector(
+ selectAuthState,
+ (state: AuthState) => state.userSettings.openedMenuSections
+);
+
+
+export const getCurrentAuthState = (store: Store): AuthState => {
let state: AuthState;
store.pipe(select(selectAuth), take(1)).subscribe(
val => state = val
);
return state;
-}
+};
-export function getCurrentAuthUser(store: Store): AuthUser {
+export const getCurrentAuthUser = (store: Store): AuthUser => {
let authUser: AuthUser;
store.pipe(select(selectAuthUser), take(1)).subscribe(
val => authUser = val
);
return authUser;
-}
+};
+
+export const getCurrentUserSettings = (store: Store): UserSettings => {
+ let userSettings: UserSettings;
+ store.pipe(select(selectUserSettings), take(1)).subscribe(
+ val => userSettings = val
+ );
+ return userSettings;
+};
+
+export const getCurrentOpenedMenuSections = (store: Store): string[] => {
+ let openedMenuSections: string[];
+ store.pipe(select(selectOpenedMenuSections), take(1)).subscribe(
+ val => openedMenuSections = val
+ );
+ return openedMenuSections;
+};
diff --git a/ui-ngx/src/app/core/auth/auth.service.ts b/ui-ngx/src/app/core/auth/auth.service.ts
index 30291435e4..311de3d7a0 100644
--- a/ui-ngx/src/app/core/auth/auth.service.ts
+++ b/ui-ngx/src/app/core/auth/auth.service.ts
@@ -48,6 +48,8 @@ import { OAuth2ClientInfo, PlatformType } from '@shared/models/oauth2.models';
import { isMobileApp } from '@core/utils';
import { TwoFactorAuthProviderType, TwoFaProviderInfo } from '@shared/models/two-factor-auth.models';
import { UserPasswordPolicy } from '@shared/models/settings.models';
+import { UserSettings } from '@shared/models/user-settings.models';
+import { UserSettingsService } from '@core/http/user-settings.service';
@Injectable({
providedIn: 'root'
@@ -66,6 +68,7 @@ export class AuthService {
private dashboardService: DashboardService,
private adminService: AdminService,
private translate: TranslateService,
+ private userSettingsService: UserSettingsService,
private dialog: MatDialog
) {
}
@@ -127,7 +130,8 @@ export class AuthService {
}
public checkTwoFaVerificationCode(providerType: TwoFactorAuthProviderType, verificationCode: number): Observable {
- return this.http.post(`/api/auth/2fa/verification/check?providerType=${providerType}&verificationCode=${verificationCode}`,
+ return this.http.post
+ (`/api/auth/2fa/verification/check?providerType=${providerType}&verificationCode=${verificationCode}`,
null, defaultHttpOptions(false, true)).pipe(
tap((loginResponse: LoginResponse) => {
this.setUserFromJwtToken(loginResponse.token, loginResponse.refreshToken, true);
@@ -490,12 +494,17 @@ export class AuthService {
}
}
+ private loadUserSettings(): Observable {
+ return this.userSettingsService.loadUserSettings();
+ }
+
private loadSystemParams(authPayload: AuthPayload): Observable {
const sources = [this.loadIsUserTokenAccessEnabled(authPayload.authUser),
this.fetchAllowedDashboardIds(authPayload),
this.loadIsEdgesSupportEnabled(),
this.loadHasRepository(authPayload.authUser),
this.loadTbelEnabled(authPayload.authUser),
+ this.loadUserSettings(),
this.timeService.loadMaxDatapointsLimit()];
return forkJoin(sources)
.pipe(map((data) => {
@@ -504,10 +513,9 @@ export class AuthService {
const edgesSupportEnabled: boolean = data[2] as boolean;
const hasRepository: boolean = data[3] as boolean;
const tbelEnabled: boolean = data[4] as boolean;
- return {userTokenAccessEnabled, allowedDashboardIds, edgesSupportEnabled, hasRepository, tbelEnabled};
- }, catchError((err) => {
- return of({});
- })));
+ const userSettings = data[5] as UserSettings;
+ return {userTokenAccessEnabled, allowedDashboardIds, edgesSupportEnabled, hasRepository, tbelEnabled, userSettings};
+ }, catchError((err) => of({}))));
}
public refreshJwtToken(loadUserElseStoreJwtToken = true): Observable {
diff --git a/ui-ngx/src/app/core/auth/public-api.ts b/ui-ngx/src/app/core/auth/public-api.ts
index 2bb7215296..dc9dc049fd 100644
--- a/ui-ngx/src/app/core/auth/public-api.ts
+++ b/ui-ngx/src/app/core/auth/public-api.ts
@@ -15,6 +15,7 @@
///
export * from './auth.actions';
+export * from './auth.effects';
export * from './auth.models';
export * from './auth.reducer';
export * from './auth.selectors';
diff --git a/ui-ngx/src/app/core/core.state.ts b/ui-ngx/src/app/core/core.state.ts
index 195cc09ca2..644816513e 100644
--- a/ui-ngx/src/app/core/core.state.ts
+++ b/ui-ngx/src/app/core/core.state.ts
@@ -32,6 +32,7 @@ import { SettingsEffects } from '@app/core/settings/settings.effects';
import { NotificationState } from '@app/core/notification/notification.models';
import { notificationReducer } from '@app/core/notification/notification.reducer';
import { NotificationEffects } from '@app/core/notification/notification.effects';
+import { AuthEffects } from '@core/auth/auth.effects';
export const reducers: ActionReducerMap = {
load: loadReducer,
@@ -49,6 +50,7 @@ if (!env.production) {
}
export const effects: Type[] = [
+ AuthEffects,
SettingsEffects,
NotificationEffects
];
diff --git a/ui-ngx/src/app/core/http/alarm.service.ts b/ui-ngx/src/app/core/http/alarm.service.ts
index 076f590199..91db205c2d 100644
--- a/ui-ngx/src/app/core/http/alarm.service.ts
+++ b/ui-ngx/src/app/core/http/alarm.service.ts
@@ -78,6 +78,12 @@ export class AlarmService {
defaultHttpOptionsFromConfig(config));
}
+ public getAllAlarms(query: AlarmQuery,
+ config?: RequestConfig): Observable> {
+ return this.http.get>(`/api/alarms${query.toQuery()}`,
+ defaultHttpOptionsFromConfig(config));
+ }
+
public getHighestAlarmSeverity(entityId: EntityId, alarmSearchStatus: AlarmSearchStatus, alarmStatus: AlarmStatus,
config?: RequestConfig): Observable {
let url = `/api/alarm/highestSeverity/${entityId.entityType}/${entityId.id}`;
diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts
index 16d3d5126b..f4a7217b34 100644
--- a/ui-ngx/src/app/core/http/entity.service.ts
+++ b/ui-ngx/src/app/core/http/entity.service.ts
@@ -89,6 +89,8 @@ import { DeviceProfileService } from '@core/http/device-profile.service';
import { QueueService } from '@core/http/queue.service';
import { AssetProfileService } from '@core/http/asset-profile.service';
import { NotificationService } from '@core/http/notification.service';
+import { TenantProfileService } from '@core/http/tenant-profile.service';
+import { NotificationType } from '@shared/models/notification.models';
@Injectable({
providedIn: 'root'
@@ -112,6 +114,7 @@ export class EntityService {
private otaPackageService: OtaPackageService,
private widgetService: WidgetService,
private deviceProfileService: DeviceProfileService,
+ private tenantProfileService: TenantProfileService,
private assetProfileService: AssetProfileService,
private utils: UtilsService,
private queueService: QueueService,
@@ -241,6 +244,9 @@ export class EntityService {
(id) => this.deviceProfileService.getDeviceProfileInfo(id, config),
entityIds);
break;
+ case EntityType.TENANT_PROFILE:
+ observable = this.tenantProfileService.getTenantProfilesByIds(entityIds, config);
+ break;
case EntityType.ASSET_PROFILE:
observable = this.getEntitiesByIdsObservable(
(id) => this.assetProfileService.getAssetProfileInfo(id, config),
@@ -399,6 +405,10 @@ export class EntityService {
pageLink.sortOrder.property = 'name';
entitiesObservable = this.deviceProfileService.getDeviceProfileInfos(pageLink, null, config);
break;
+ case EntityType.TENANT_PROFILE:
+ pageLink.sortOrder.property = 'name';
+ entitiesObservable = this.tenantProfileService.getTenantProfiles(pageLink, config);
+ break;
case EntityType.ASSET_PROFILE:
pageLink.sortOrder.property = 'name';
entitiesObservable = this.assetProfileService.getAssetProfileInfos(pageLink, config);
@@ -409,7 +419,7 @@ export class EntityService {
break;
case EntityType.NOTIFICATION_TARGET:
pageLink.sortOrder.property = 'name';
- entitiesObservable = this.notificationService.getNotificationTargets(pageLink, config);
+ entitiesObservable = this.notificationService.getNotificationTargets(pageLink, subType as NotificationType, config);
break;
}
return entitiesObservable;
diff --git a/ui-ngx/src/app/core/http/notification.service.ts b/ui-ngx/src/app/core/http/notification.service.ts
index 722dcdd045..b4ca3b78f9 100644
--- a/ui-ngx/src/app/core/http/notification.service.ts
+++ b/ui-ngx/src/app/core/http/notification.service.ts
@@ -34,7 +34,7 @@ import {
SlackConversation
} from '@shared/models/notification.models';
import { User } from '@shared/models/user.model';
-import { isDefinedAndNotNull } from '@core/utils';
+import { isDefinedAndNotNull, isNotEmptyStr } from '@core/utils';
@Injectable({
providedIn: 'root'
@@ -51,6 +51,10 @@ export class NotificationService {
defaultHttpOptionsFromConfig(config));
}
+ public deleteNotification(id: string, config?: RequestConfig): Observable {
+ return this.http.delete(`/api/notification/${id}`, defaultHttpOptionsFromConfig(config));
+ }
+
public markNotificationAsRead(id: string, config?: RequestConfig): Observable {
return this.http.put(`/api/notification/${id}/read`, defaultHttpOptionsFromConfig(config));
}
@@ -127,9 +131,13 @@ export class NotificationService {
defaultHttpOptionsFromConfig(config));
}
- public getNotificationTargets(pageLink: PageLink, config?: RequestConfig): Observable> {
- return this.http.get>(`/api/notification/targets${pageLink.toQuery()}`,
- defaultHttpOptionsFromConfig(config));
+ public getNotificationTargets(pageLink: PageLink, type?: NotificationType,
+ config?: RequestConfig): Observable> {
+ let url = `/api/notification/targets${pageLink.toQuery()}`;
+ if (isNotEmptyStr(type)) {
+ url += `¬ificationType=${type}`;
+ }
+ return this.http.get>(url, defaultHttpOptionsFromConfig(config));
}
public getRecipientsForNotificationTargetConfig(notificationTarget: NotificationTarget, pageLink: PageLink,
diff --git a/ui-ngx/src/app/core/http/public-api.ts b/ui-ngx/src/app/core/http/public-api.ts
index 125fe3b274..5a511b8f60 100644
--- a/ui-ngx/src/app/core/http/public-api.ts
+++ b/ui-ngx/src/app/core/http/public-api.ts
@@ -40,4 +40,5 @@ export * from './tenant.service';
export * from './tenant-profile.service';
export * from './ui-settings.service';
export * from './user.service';
+export * from './user-settings.service';
export * from './widget.service';
diff --git a/ui-ngx/src/app/core/http/tenant-profile.service.ts b/ui-ngx/src/app/core/http/tenant-profile.service.ts
index d58f5d0509..26c8cd0045 100644
--- a/ui-ngx/src/app/core/http/tenant-profile.service.ts
+++ b/ui-ngx/src/app/core/http/tenant-profile.service.ts
@@ -22,6 +22,8 @@ import { Observable } from 'rxjs';
import { PageData } from '@shared/models/page/page-data';
import { TenantProfile } from '@shared/models/tenant.model';
import { EntityInfoData } from '@shared/models/entity.models';
+import { sortEntitiesByIds } from '@shared/models/base-data';
+import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
@@ -64,4 +66,11 @@ export class TenantProfileService {
return this.http.get>(`/api/tenantProfileInfos${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config));
}
+ public getTenantProfilesByIds(tenantProfileIds: Array