diff --git a/application/pom.xml b/application/pom.xml
index 4330300adf..2d6dda0bbe 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -341,6 +341,10 @@
Java-WebSocket
test
+
+ org.jboss.aerogear
+ aerogear-otp-java
+
diff --git a/application/src/main/data/json/system/widget_bundles/charts.json b/application/src/main/data/json/system/widget_bundles/charts.json
index 2772c696bf..de32604dbb 100644
--- a/application/src/main/data/json/system/widget_bundles/charts.json
+++ b/application/src/main/data/json/system/widget_bundles/charts.json
@@ -54,7 +54,7 @@
},
{
"alias": "pie",
- "name": "Pie- Flot",
+ "name": "Pie - Flot",
"image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAZTElEQVR42u2deXwUVbbH/e+tM/Pe5/Oe82beNjo6znvjvFFnRkFxF1f2RREQWQQEEZFNFDUCOjAoCKjsq+xG1hBCWAJkJSEJCQGyJyQkZN+7urq7lvt+1RU61dWd0Emqqm911/2cPzq9pdP1zT3nnnvO795BrGENHcYd1ldgDQss3YYoiDcr+NQkLvqQc/sGx5eL2Q9n2aePt49/lRk1gBn0jNeTk+4kKb8iab8jGX1JzkCS9xYp/YxUriMNsYQtJUSwvs4wBovjhGtXXAf2OZZF2KeNs738hO3Zh7swr9ee/5uuLOFnJP1PJG8iqVxLWtOIwFpghfrgef7yJefWdex7k20v9euapJ6DpbL4f5AmtpKFpPFMWEEW+mCJba3ciShHxAfwaN2CSRuwvCazfyJXhpHq3YRrssAyLU92O3fmBPvJHNuL/XrMk8ZgdUxj/0hyBpPaH0J4DgtBsISyUuemb5khz/aeJ73A8ljSz0nBdNKWaYFFdQjFnY6xz5igIU+6g+WxrKdJbSQRXRZYNA2Xkzt53P7mCD2QMggs2S7cI6UtQsI/mhwsp9MVuZsZ/oJ+SBkKlmwp/00qVhPBboEVlEhKwFqPeX2Q3kgFAaz22esucnMzEXkLLAOjqUvp9kmvG4NU0MCSLeMR0nTeAkv/JEJDvWPZZ7bnHjGSqmCCJdnfkrwJxFVrgaUTUyJ39AAz+FmDkaIALDkx8QtStRXfggWWxhMVu3B2UJCiBSzZsOftvGmBpc1AdkrbbKeJwZKnLqTsLbB6NRwOVLAEFynqwJIN1RMCY4HVI/dXU4WKKBqoohEsacHYh7AlFljdTChcSApWnG4asOStxsaTFlgB79Ac+sHWvw89VNELllQo8fek4jsLrNvnFFw7NlGFFO1gyVY0l7YcPU1guZxsxPxuXW/7lDH2ccP9PsQMec6x8i+OpREd9wx73rlulVRRM3pQqIEFuzaWquIIasByONC/EOBlZka+BESE4kLpdcsi/D6Hi4mSZsD6uvaXDHxKrLop5Ofy2RmoKWVee0W6//m+zIgXQwQsKcs1mJ6tazrAcrDsvBkBUoVnYgdanuE6A4ud+w68qhIs9qNZuEeqTu7fR2xscKxahjudG9YIuVdCByxYdn/C2yywblE1a0rgXol9f6pr/0601rj2bPcP1suPCxU3hMI8L7A+nYceL0xRuC1WVznXfs2MGUxYO/v+2yEFlsTWCzRUdAUbLI7DXNKzgLozsFz7vseUhhyYlysc+bLIMPCPgFJ6dMoYZDTwY+jEWEq7/AoRHGEMlig4Pl/Y45WaX7DsU8aiRhkNgzINHrCkh2ZM4M7EcvFxQNmx+EOxuZEZ2j80wZJi+dHBbZ0NJljONct7kwLwA1b/PgjPxdpqhOq+YHXE/oOeERvq5PIbrAMCyZmZDyxY8QfhCJaUBe1dbskXLOf61e57PsOmNUwui5A2sF941OuFRyL5zIv2qW9g16i9dGLm5BAEC4Zu7LACi8/OtL3wmOZgiS3Nfn8dInelQ8RyAZ0XfGYanxzPDHyaO3Uc81xogoW8fJD2fIIAltjUiOxl77Phfmasb1cg/+kx6XcxNtzoSKI+31coKoDsh0RhVaVzw2rccHy5RLS1hSZYcpmNJFUSHjOWkH8NSQHNwfKlQRVjIQcmlBbLvdHwhnziOUycXPRhoSg/ZMGCpf/Z+Bqb4AXvrN0+a6rBYEl2q+Me+Qg8Kj2npZmdMy2UwYLlTwmrdIPo2rsjmLvL/ftA/koV2ocmWLC6A6EJVp29flHKkjp7ndotFubZXnmKwoqGUAMLxVuOG6EGlkjEiOTFAw4Nee3Y6MTKJB+3yLJzpltgGZGRN6rPxyCwTpSeBFWyDTw0dEvONk7g1W4xco8Flu5WtSN0wGpgG1+NGu0BS7Y55+bXMDVqt1h+3Tb4GQssHS35F8b0vhoB1vKLK1RUyQbaEioS/RRmBVxCY4HVE8sdHwpgZdde9kuVxy1uyN7kElxqt9jrDR8LrK6sOd7cYAmiMP30zC7Akm3W2Tk3bVXq194otw3pb4GlT+vYw3rXPugLVuz1k7elSraRUa+fv+Hzb+RyOT6db4Gli1XvMitYTt45/sRbAYIl28r01Q5eXaEGtb6QAiv+J+TKSHJ9CSmaRzL7eVVQQY/U16CEKwXdvyTF8yVZ79R7NdLfulvXQlMdwYosONAtqmR7N25WRVuF2i1WVtj0l+0zAqzkfydtWV7vVrGm/SHmmr8EIC+JeCf+C2Gvk7YMKTaClDc02WQF+ZT/7NWH0bMhUS+wMPGMOf5mD8CCjYgaFVd+zo9bXLTA9GBV73TDtJok/iu5+EB7KhwnDMhTSOpvOyz7RSmZWX+kvf0Gt3HUCspgnDWkYIa7ju9D6eSLXmpS6tbVoxdYh4uO9owqpVtkefVczSecNTdYrgapGl32brDy5dIb5k/utEYv61npNlwnYm1MUbjNlkntqam/kbpxsp6jthJQF7A4gXszZlIvwYJhRVneWq52DjXV9tcGmBWspH+TJh5VHhyTk9pj/lIqdIH+u2dq4VulJ99YITlHlME0xEg/9j7SQsQmcqYBC46s91TJNuzoyKNFx3zI5RxLFpp+VZj5qDR7MXmSg1M9hNAeAwqRHU/uR2r2kbqDkltEmO+qk3LoFFc96ALW7HPztAJLthXpX7NcMN2i9mAhikc8LjrJpSd8jtz5Z4kbKPdh/ehnzruTOKskfSxok6b8lx8oe3B2gSnAym3I05Yq2aaeeqe0RV1iK9bV2kcPMh9YWOW1pkvxuMSHz6OF70m/pTSik6hoPWk6K6kpO9xBgrOaXHqqt59HhzNXtAdrVcY3eoAlu8WY0hPq38fzjuWLzQRWwk9JU5z0Pshj+W1/gJwaFmsIs/x4z35S7JV2v/QO9cekpWXNHikN0cuPVDiTdrCwjkMOXSewZPviwjKbSy1PwKcl6yqppRlY4EaOacq+6FQ0BgNHB/g9+tCWTa4vdq8NS0nxgvaaY6651zWAd2qed9AYrNNlZ3SlSraJsZPzGvLVbrGh3j52KO1gVax25+TqSPmXHVbykcIrZUguEikuP2V6LxHmqjTh4Ta8IVJcCMKg0Y2Ma++9s9aCuRqD9XFShAFgwYYeGXm0+JivW3SuXEo1WC0X/M3zpbf0PF6UfkQnYBdutN0nPtauy43EWPbzWkggDaEXrFZn6+DDw40BS7YlF/7S6mxT7/9kZdj6Pxr6m9DwqkjWe3KtvY/8+BZKwQq8lkFDm3Bi8rWGXLVbbG3R9rCdEKxu8DXkyegEC004xoMFG3Jk+KHCI6KqTUAQeik6EnZgSQI19IGFKtARR0cFBSzZFqd8AV+sDroyL2riFsMCLGTXtFMx1QysrNrsIFIl2/gTk67WX/Nxi60QzbLACmxtkUIdWNuu7Ag6WDCsHiLzD/hxixvXWGDd3q5/Th1YaOeiASzZFiZ+2sg2qVeL13IC6aYPa7CynqELLFQhDzkygh6wYCgzvFSTpXaLba32qWMtsDpPOvxM2henB6ycuitUUSXboMPD9uTuQ6eQN1yic9sGC6xOTdodpwasAwWHKASrK7eYd7VbAl1hBFblOorA+ip9JbVgwcYeH3+pVu0WCcPY351ogeVzEuIkisAKpCs1uIaW642XN/sqkTgDOxMqjMBCZwclYKHCHblvysGSbUH8wnq2Xu0WC3JtrzxpgdVRv6pFk7QGYKEN0BRUyTY6etzFap/4FLqVs9+2wGo3NALRABauk4nAkt0i0rm86CPQtX+nBZZkDbFUgIW6KHOBJdu88wtq7WqlKKGkyDbg6XAH6+YWKsDakrPdjGDJupUJFT66ldIZd9PDGiy5+SzoYKE3y6RgKVaLnNotHtwfvmDlT6UCLMPKkfWz98/NrWaq1W7xRpnt1gka4QVWziAqwII+jNnBcrvFMUmVyT6boE72o/fDDiz00NIA1qTYqd2v+Rzx+YWlu3P3brq81bdtesaZ9+Cevr+6a1HK5wMPD1VmCjbnbN1+5Xt06eiEF5RIsKGudotHIsMLrIt/oAIsXO9u1h2MK24uUb6DUpoGSt2iKCq3t+W6CbQrQmW5sKkI96CvUBYdQfUVtmu0ZWtm3OzKtptd/cEhD1bKr6gACw3K3bpyp8vi3DAdgWrytNPvymdVQIYUD70RMx7ppRZnyztnZo46Nja9Gk12ZG3Wejz0WfISlO9hHYeahSZH03dZ63Dn1ivb8xsLNJ+33LqVCeELFmqUaQBL6a0CMVSmo0DeswuEgk9yqzH/o8RPiCRWc1Z+CK4QP6KtHrfhOjGTye1lNUwtfOjE2ClovF6QsFA/t+irWykN1O+iWRRVAGhKhpTjleHk0uPkwq87mv5CYVfHhGBhKsLE4/nxVNlpvAmKW3B78slpmJYKGguRBZAnJE/R87iYCXbOjiejRAclVlgxIOOPH3WN6BHt3Wit6MZ3AVVPKC+0JJOGaKlNHgkhibxhkqILRPpk5TRTlPtRAdahoT2+crPOzsXshYsHByffsz9fipTh4HDkDtwibnjEIFD9DGVlrN3gFpelLW92tLwe/Ybeq8W/pn2lYVMU4RolrVGQV/sjqfhWEquF4AzIQ00BIpvzf0dHK+xPTDljKaN4xONITs49/4HnThADKSx4PflUgeiS477d1ZjwcIwKXBWYRljmgVJzmxn3vnpLUdcBHTaokrakkvoo94T3uSRpBJ3IS09KEx48lEEz1k+pAKtn7YSYhwqaCuH4wIdyRQY3h/vBHHhCGyreH1kJ1WtBGw68wJPlzT4UiM6LX6A5VXC+qtTDyjTnq4fsC8461mc6owq5tJt8YaNQw4iMi/DGHKoFVwvVNQiHeFxt0Ryp0bTd1WrUbg89SxrAAgQ9kPRAHyJeixhcef/e3P24c3Xmt55oDD+WtZapTndCTD311HS8Q2pVGpaWUKZEGkJbqoZHvYrFqfLP3HPVddfati7s7rVt/Xbaxh9jP09y7MhxnSrlMqv50mahkRUdHA7pMIQ8HMBku0Iaz0i6WTdWSdLwODkHMjXpD5Hk/whYAv4uKsDC6qy7PQ7ykYV78/arHkISC/fvurZH/nHa6RnEWyIQ01hJc+muXOkJ2ISR979XZ35jczFadmEcGlZp80plxV3n7lnf1jVYgdgTu5jJMezSFMfeq64TJRJ5ZS1ik0N08kaRJwd5IK96d0eQB/IuPugO8txgQdiNBrDkFFTghgwWXoXQGycMeExe+k05NQ0ZBBiYW5+9EXMVnolTnDyv/TgxAndiwsNteMOUmxeQPoUYCTKuGoKFHKzyD7xUzf/vRg2ouq09tLXt9cP2j887NmS6oou41Eq+qFGok1ytaJSrZaRVrS2bCrAikhd167JhBvJ9E0w/8qNIZV1vaa9gRIYdGzuqVadMlbxz3MA2ELd80kcJn2hF1YnrXmVuZS3Cn7fZDKAqEHvQTd4n8Y5v0p2H8rkLlVKQV2cX25wiJxCqhgZgKaNvDSulxsVMDMSrQq9Bw4p7TJzKPw3h0dO7GUqouq3dt77tlf3MO7Hs12nOfddc58v4q3VCRavY6pRcrWg6sLArHALVDVKmI3W5UvSB5cjwg3azUBWI/X5z24DIjiDvXBl/rV7wBHkibWBFl8SEAFWzz81X9kwjlH47hg0lqgKxR7YzWNVevMlTARa2is1O1fiYt1Qpq0UJjnCjymMplXSAZa72L7+p2jaXl5DpxkuusKUKVsuIVICFTQ/PSs10hpRVNVPj5dmLuF+vC1+q/m+zjZYYy9TVydjkVv4h2KX57Ya2cJ6uhh6wUwQW5aIgnVl8RaLyr0BO6MEttnCmCjYvzkERWEeKokxH1Z7cvco/AXvJj+9kwpwq2O6rLorAKm4uNhdVqzLWKD+/nSNwARZVsNx6gSKwkAFC+5RZqPowYaGyXwPbcJOPsxZSsPs32bTalNRM3BZVnaagasqp6aq+Z9RXWUjJNuaoZmeAaQYWKhTop2pU9FgUzis/9pp0p8WTx1amOqkDCwfa0J6yOjKs3l0N0bHmKODutnhSWEY1Tx1Y8C/BPfLktoYKQeUHTq7gf7PegqnD/rjNpmHVl5aHNOHsU2qpSqvyUvHLbxD+EPYpK5XNPcPSVd3gGWfK4+ikCi38ys9ZZRMf/d6iSm3HizhKwTL+IMxADCXO3h9SfGGflQhVG6ICfDOUgtWDMmW9DU36yto91O++cdRKhPqxt0+w2pKgMVhny8/TQ9XMuFnKdlPwNeeMlQj1b2hWoxosNNiMiKJibYhDmvBhlJ/trylWyqrT9aDmvRgag0WkdtNvgk4V6sMgdaT8VHuvuiyAOrPFiQ7NMdAeLBo2pMtby72Wq2g3XWcB1KmhmccEYGGg4y+IVKGRVflhcmqF322ykgudGhoVKW3/8h3Q7AsWVcdLTig/STlN7aZ0GqZz04CFQ7b005/twjZd3qJqN312j5Wy6srw/eikGaELWMRbr9YY+wJakoqUlYMnIw5aKavb2F6N6kWNAwviad1VU+5du+k8Vbvp9FgrZXUb67PDxnLEZGBhHCw06DxfSHOr2k2XJBpRu4d+npkn2VUXnRHxjs62iXDxVH36D221QUALSTWIaYVGebvRYDl5FxQ79KYKCmnYo1T+3i1ZRqSssI19vdlrjlQmYCF7NOsUe76cRyHKD7kuZe3vjVYBC1UIxbQ4xL47JLagvGX8CgNiJ7oK1OgIFrmlpK1ru2mVzesMHGxNGJOyinbXAnyb7vyfjW399zKVbZKE1ZO7GJkem0t0/2tJH0kJ1oRjdjyAplB8SMgPQQoLdy5NdmbV8AaDBalLXS+9vmAh7plxRsdeVpXUVnqVce2mQArikZ5ft/2y5FZkHRGA9WMeB4YmRrMqsKbGsJjbZHFACAzBZaPnDLpqow4bus4YGMnorWp0h87vT7LrLutE1dkb57wy/k0CwpdgxSuoR8VneHm/V6Q1LsquAuuR7dJkFpnr2nDJiRkOzz9bxkfmGrrdhGpsTfRkggwWxtLU5Xq3mzbYxaeCoZA2JYZFSAcpUdHtFlWP+oIl97AfLeBiiqUpDSJp+OQG/z98eM5hwEU3AixoskP/WEOqVqSvUr4/2k2HBandFLo0jDucQjLWN8HhFyyl/Abar1HJgynk4e02Y0JDQIyPGiJguaP4WK2o+iBe3W46JdgKaYjZ8+oFBE+DIpnAwdqZ44IDHfCDFPjjaRAPMiCjC+VSY664QWAhJ66J/uzkU9NU7aYLzwWn3fSBLTZMOZ4fITiLD7Mi1RkgWHCImGif2cMk3eBPl3K/32zDJUcaQtfPPP6YnRg17jDsN+EUiV624Y+KHqNqN/0uI2i1e/Bi2DW6/1bdBBpffSnvDCysCiH+CQla3MY2+V+SpVfNj3Og6ly/D/ynbbZaRgxBsDBwvlLv2k3rle92tDCY7aabsqT8QkYVj1wUwnZMPwi2VM0/nYE19qi9oEG4b0P7cjK2hEMvA3SOURel30rwdCln5LU2FCyMrzN6qN1d1FysfB/oZN4XVIW0e9e34UQdOXKXCKvmh/xoDzx493z4wT8y1bb28H/0Eb1irIh4h8EX2miwcAxOD+T/cAKF8k0QKSvjmyAaRCX7fm/rZSHhPe43uVe3tuyX9jMsR0IcLCJJnNV065zBHwsOKl+O/+/HrHbT7uQXcLiG8Vc5CGARt4J3gIcM4uxn5QtxtsdL+63avW60oaZW8kG5xMEBCyOqOPq2VH2c9Jmq3RRRi4ULJYUxlIKFseHypi6owgH3vGC1m/bcliQ5gnhxgwkWah+WpX3pv900+k3UoCqf/FWq1W7aDZt9mhVEEqZgub0bF5G82F+7abPyacjxWKx0S4iBD/Ypc0EGS05ALEhYqATLc16hPFBYco+lkNYdHVEHH/SrSgFYMlsfJ0XIVGXWZCkfstpNu0uVJ2drgSUNdENAAulYSbTyTpSHo57EwiVAe+s4S8NcRRdYUrwleqWHcTojasktXAI/qoQX6bmYNIHl7RzJq4eslFWghn4ykbIrSClYM09ZKatA98KDmAU1H1iJN/g/WmIeAZRYBWvHxqxgYdxsE30LUSzzmKem2QKrB0tFIrd0Wqaq2sPXYudovnR0gyWPY0VcEBsGKXR/BteChixYGPV2EdsUFlUo7pDLTS2wtBzoY3kgXM8pwVIGDdOieS6WmcAi7gN257o7PMMHKVQ/o3e5ySGa60qZDCx5QJslTE7axdIvs5o34zUyJVjEXfd3II/rG7rF75CiOFzACaJJr49pwZKHSyAH8zlZlSpkDP8t2y67nLypr4zJwfLgBakqCE2FwCy1I8fl4EPgmoQEWPJAqwVU9tDzacbQHh8bWoTmdXyhDJZnFDUJixIcpjhA9cEtNuTQc+uF0LsKIQiWPND7C3GzGbEsdGZp4wmypZBcO1nCuYRQ/fpDFyzPQKkuNoWgihb05CqSnJBSxlpP27NMLbCCPFBgmV0jrM1wojDcMA1cFOyjYhEiR/jVghg+X3Y4gaUcWMxfrhWwloSiFYpzNOQMJEGTGLlytKwheOKF8PyCwxUs38kM4tgomkPSFSdNoH58UjSLmQY6ETg/AusACKzBk3oM4sfP72NeO2yHvPYHZx1opoVWEV5ew4jWl2mBZQ0LLGuYbfw/UfikHjIsMFkAAAAASUVORK5CYII=",
"description": "Displays latest values of the attributes or timeseries data for multiple entities in a pie chart. Supports numeric values only.",
"descriptor": {
@@ -69,7 +69,7 @@
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-flot-pie-widget-settings",
"dataKeySettingsDirective": "tb-flot-pie-key-settings",
- "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.6114638304362894,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.9955906536344441,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.9430835931647599,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"radius\":1,\"fontColor\":\"#545454\",\"fontSize\":10,\"decimals\":1,\"legend\":{\"show\":true,\"position\":\"nw\",\"labelBoxBorderColor\":\"#CCCCCC\",\"backgroundColor\":\"#F0F0F0\",\"backgroundOpacity\":0.85},\"innerRadius\":0,\"showLabels\":true,\"showPercentages\":true,\"stroke\":{\"width\":5},\"tilt\":1,\"animatedPie\":false},\"title\":\"Pie- Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.6114638304362894,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.9955906536344441,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.9430835931647599,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"radius\":1,\"fontColor\":\"#545454\",\"fontSize\":10,\"decimals\":1,\"legend\":{\"show\":true,\"position\":\"nw\",\"labelBoxBorderColor\":\"#CCCCCC\",\"backgroundColor\":\"#F0F0F0\",\"backgroundOpacity\":0.85},\"innerRadius\":0,\"showLabels\":true,\"showPercentages\":true,\"stroke\":{\"width\":5},\"tilt\":1,\"animatedPie\":false},\"title\":\"Pie - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
{
diff --git a/application/src/main/data/upgrade/3.3.4/schema_update.sql b/application/src/main/data/upgrade/3.3.4/schema_update.sql
new file mode 100644
index 0000000000..7584d2f732
--- /dev/null
+++ b/application/src/main/data/upgrade/3.3.4/schema_update.sql
@@ -0,0 +1,37 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE IF NOT EXISTS queue (
+ id uuid NOT NULL CONSTRAINT queue_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ tenant_id uuid,
+ name varchar(255),
+ topic varchar(255),
+ poll_interval int,
+ partitions int,
+ consumer_per_partition boolean,
+ pack_processing_timeout bigint,
+ submit_strategy varchar(255),
+ processing_strategy varchar(255),
+ additional_info varchar
+);
+
+CREATE TABLE IF NOT EXISTS user_auth_settings (
+ id uuid NOT NULL CONSTRAINT user_auth_settings_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ user_id uuid UNIQUE NOT NULL CONSTRAINT fk_user_auth_settings_user_id REFERENCES tb_user(id),
+ two_fa_settings varchar
+);
diff --git a/application/src/main/data/upgrade/3.3.4/schema_update_device_profile.sql b/application/src/main/data/upgrade/3.3.4/schema_update_device_profile.sql
new file mode 100644
index 0000000000..e0b0cc7f1a
--- /dev/null
+++ b/application/src/main/data/upgrade/3.3.4/schema_update_device_profile.sql
@@ -0,0 +1,49 @@
+--
+-- Copyright © 2016-2022 The Thingsboard Authors
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+ALTER TABLE device_profile
+ ADD COLUMN IF NOT EXISTS default_queue_id uuid;
+
+DO
+$$
+ BEGIN
+ IF EXISTS
+ (SELECT column_name
+ FROM information_schema.columns
+ WHERE table_name = 'device_profile'
+ AND column_name = 'default_queue_name'
+ )
+ THEN
+ UPDATE device_profile
+ SET default_queue_id = q.id
+ FROM queue as q
+ WHERE default_queue_name = q.name;
+ END IF;
+ END
+$$;
+
+DO
+$$
+ BEGIN
+ IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'fk_default_queue_device_profile') THEN
+ ALTER TABLE device_profile
+ ADD CONSTRAINT fk_default_queue_device_profile FOREIGN KEY (default_queue_id) REFERENCES queue (id);
+ END IF;
+ END;
+$$;
+
+ALTER TABLE device_profile
+ DROP COLUMN IF EXISTS default_queue_name;
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 1be741a8c1..500ad60524 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
@@ -40,6 +40,7 @@ import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbActorMsg;
@@ -63,6 +64,7 @@ import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.nosql.CassandraBufferedRateReadExecutor;
import org.thingsboard.server.dao.nosql.CassandraBufferedRateWriteExecutor;
import org.thingsboard.server.dao.ota.OtaPackageService;
+import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.rule.RuleChainService;
@@ -330,6 +332,11 @@ public class ActorSystemContext {
@Getter
private TbRpcService tbRpcService;
+ @Lazy
+ @Autowired(required = false)
+ @Getter
+ private QueueService queueService;
+
@Value("${actors.session.max_concurrent_sessions_per_device:1}")
@Getter
private long maxConcurrentSessionsPerDevice;
@@ -495,8 +502,13 @@ public class ActorSystemContext {
return partitionService.resolve(serviceType, tenantId, entityId);
}
+ public TopicPartitionInfo resolve(ServiceType serviceType, QueueId queueId, TenantId tenantId, EntityId entityId) {
+ return partitionService.resolve(serviceType, queueId, tenantId, entityId);
+ }
+
+ @Deprecated
public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId) {
- return partitionService.resolve(serviceType, queueName, tenantId, entityId);
+ return partitionService.resolve(serviceType, tenantId, entityId, queueName);
}
public String getServiceId() {
diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
index 7c2e45477b..2817b1e359 100644
--- a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
@@ -30,8 +30,6 @@ import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.actors.tenant.TenantActor;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant;
-import org.thingsboard.server.common.data.TenantProfile;
-import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
@@ -45,24 +43,20 @@ import org.thingsboard.server.common.msg.queue.RuleEngineException;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.tenant.TenantService;
-import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
import java.util.HashSet;
-import java.util.Optional;
import java.util.Set;
@Slf4j
public class AppActor extends ContextAwareActor {
- private final TbTenantProfileCache tenantProfileCache;
private final TenantService tenantService;
private final Set deletedTenants;
private volatile boolean ruleChainsInitialized;
private AppActor(ActorSystemContext systemContext) {
super(systemContext);
- this.tenantProfileCache = systemContext.getTenantProfileCache();
this.tenantService = systemContext.getTenantService();
this.deletedTenants = new HashSet<>();
}
@@ -125,28 +119,12 @@ public class AppActor extends ContextAwareActor {
private void initTenantActors() {
log.info("Starting main system actor.");
try {
- // This Service may be started for specific tenant only.
- Optional isolatedTenantId = systemContext.getServiceInfoProvider().getIsolatedTenant();
- if (isolatedTenantId.isPresent()) {
- Tenant tenant = systemContext.getTenantService().findTenantById(isolatedTenantId.get());
- if (tenant != null) {
- log.debug("[{}] Creating tenant actor", tenant.getId());
- getOrCreateTenantActor(tenant.getId());
- log.debug("Tenant actor created.");
- } else {
- log.error("[{}] Tenant with such ID does not exist", isolatedTenantId.get());
- }
- } else if (systemContext.isTenantComponentsInitEnabled()) {
+ if (systemContext.isTenantComponentsInitEnabled()) {
PageDataIterable tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT);
- boolean isRuleEngine = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE);
- boolean isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE);
for (Tenant tenant : tenantIterator) {
- TenantProfile tenantProfile = tenantProfileCache.get(tenant.getTenantProfileId());
- if (isCore || (isRuleEngine && !tenantProfile.isIsolatedTbRuleEngine())) {
- log.debug("[{}] Creating tenant actor", tenant.getId());
- getOrCreateTenantActor(tenant.getId());
- log.debug("[{}] Tenant actor created.", tenant.getId());
- }
+ log.debug("[{}] Creating tenant actor", tenant.getId());
+ getOrCreateTenantActor(tenant.getId());
+ log.debug("[{}] Tenant actor created.", tenant.getId());
}
}
log.info("Main system actor started.");
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
index 66fe935d68..3538800d38 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
@@ -46,6 +46,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
@@ -57,7 +58,6 @@ import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.TbMsgProcessingStackItem;
-import org.thingsboard.server.common.msg.queue.ServiceQueue;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.asset.AssetService;
@@ -72,6 +72,7 @@ import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.nosql.CassandraStatementTask;
import org.thingsboard.server.dao.nosql.TbResultSetFuture;
import org.thingsboard.server.dao.ota.OtaPackageService;
+import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.rule.RuleChainService;
@@ -161,15 +162,24 @@ class DefaultTbContext implements TbContext {
}
@Override
+ @Deprecated
public void enqueue(TbMsg tbMsg, String queueName, Runnable onSuccess, Consumer onFailure) {
TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName);
enqueue(tpi, tbMsg, onFailure, onSuccess);
}
+ @Override
+ public void enqueue(TbMsg tbMsg, QueueId queueId, Runnable onSuccess, Consumer onFailure) {
+ TopicPartitionInfo tpi = resolvePartition(tbMsg, queueId);
+ enqueue(tpi, tbMsg, onFailure, onSuccess);
+ }
+
private void enqueue(TopicPartitionInfo tpi, TbMsg tbMsg, Consumer onFailure, Runnable onSuccess) {
if (!tbMsg.isValid()) {
log.trace("[{}] Skip invalid message: {}", getTenantId(), tbMsg);
- onFailure.accept(new IllegalArgumentException("Source message is no longer valid!"));
+ if (onFailure != null) {
+ onFailure.accept(new IllegalArgumentException("Source message is no longer valid!"));
+ }
return;
}
TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder()
@@ -213,41 +223,45 @@ class DefaultTbContext implements TbContext {
}
@Override
- public void enqueueForTellNext(TbMsg tbMsg, String queueName, String relationType, Runnable onSuccess, Consumer onFailure) {
- TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName);
- enqueueForTellNext(tpi, queueName, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure);
+ public void enqueueForTellNext(TbMsg tbMsg, QueueId queueId, String relationType, Runnable onSuccess, Consumer onFailure) {
+ TopicPartitionInfo tpi = resolvePartition(tbMsg, queueId);
+ enqueueForTellNext(tpi, queueId, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure);
}
@Override
- public void enqueueForTellNext(TbMsg tbMsg, String queueName, Set relationTypes, Runnable onSuccess, Consumer onFailure) {
- TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName);
- enqueueForTellNext(tpi, queueName, tbMsg, relationTypes, null, onSuccess, onFailure);
+ public void enqueueForTellNext(TbMsg tbMsg, QueueId queueId, Set relationTypes, Runnable onSuccess, Consumer onFailure) {
+ TopicPartitionInfo tpi = resolvePartition(tbMsg, queueId);
+ enqueueForTellNext(tpi, queueId, tbMsg, relationTypes, null, onSuccess, onFailure);
}
+ private TopicPartitionInfo resolvePartition(TbMsg tbMsg, QueueId queueId) {
+ return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueId, getTenantId(), tbMsg.getOriginator());
+ }
+
+ @Deprecated
private TopicPartitionInfo resolvePartition(TbMsg tbMsg, String queueName) {
- if (StringUtils.isEmpty(queueName)) {
- queueName = ServiceQueue.MAIN;
- }
return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator());
}
private TopicPartitionInfo resolvePartition(TbMsg tbMsg) {
- return resolvePartition(tbMsg, tbMsg.getQueueName());
+ return resolvePartition(tbMsg, tbMsg.getQueueId());
}
private void enqueueForTellNext(TopicPartitionInfo tpi, TbMsg source, Set relationTypes, String failureMessage, Runnable onSuccess, Consumer onFailure) {
- enqueueForTellNext(tpi, source.getQueueName(), source, relationTypes, failureMessage, onSuccess, onFailure);
+ enqueueForTellNext(tpi, source.getQueueId(), source, relationTypes, failureMessage, onSuccess, onFailure);
}
- private void enqueueForTellNext(TopicPartitionInfo tpi, String queueName, TbMsg source, Set relationTypes, String failureMessage, Runnable onSuccess, Consumer onFailure) {
+ private void enqueueForTellNext(TopicPartitionInfo tpi, QueueId queueId, TbMsg source, Set relationTypes, String failureMessage, Runnable onSuccess, Consumer onFailure) {
if (!source.isValid()) {
log.trace("[{}] Skip invalid message: {}", getTenantId(), source);
- onFailure.accept(new IllegalArgumentException("Source message is no longer valid!"));
+ if (onFailure != null) {
+ onFailure.accept(new IllegalArgumentException("Source message is no longer valid!"));
+ }
return;
}
RuleChainId ruleChainId = nodeCtx.getSelf().getRuleChainId();
RuleNodeId ruleNodeId = nodeCtx.getSelf().getId();
- TbMsg tbMsg = TbMsg.newMsg(source, queueName, ruleChainId, ruleNodeId);
+ TbMsg tbMsg = TbMsg.newMsg(source, queueId, ruleChainId, ruleNodeId);
TransportProtos.ToRuleEngineMsg.Builder msg = TransportProtos.ToRuleEngineMsg.newBuilder()
.setTenantIdMSB(getTenantId().getId().getMostSignificantBits())
.setTenantIdLSB(getTenantId().getId().getLeastSignificantBits())
@@ -306,13 +320,13 @@ class DefaultTbContext implements TbContext {
}
@Override
- public TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) {
- return newMsg(queueName, type, originator, null, metaData, data);
+ public TbMsg newMsg(QueueId queueId, String type, EntityId originator, TbMsgMetaData metaData, String data) {
+ return newMsg(queueId, type, originator, null, metaData, data);
}
@Override
- public TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) {
- return TbMsg.newMsg(queueName, type, originator, customerId, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId());
+ public TbMsg newMsg(QueueId queueId, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) {
+ return TbMsg.newMsg(queueId, type, originator, customerId, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId());
}
@Override
@@ -326,20 +340,17 @@ class DefaultTbContext implements TbContext {
public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) {
RuleChainId ruleChainId = null;
- String queueName = ServiceQueue.MAIN;
+ QueueId queueId = null;
if (device.getDeviceProfileId() != null) {
DeviceProfile deviceProfile = mainCtx.getDeviceProfileCache().find(device.getDeviceProfileId());
if (deviceProfile == null) {
log.warn("[{}] Device profile is null!", device.getDeviceProfileId());
- ruleChainId = null;
- queueName = ServiceQueue.MAIN;
} else {
ruleChainId = deviceProfile.getDefaultRuleChainId();
- String defaultQueueName = deviceProfile.getDefaultQueueName();
- queueName = defaultQueueName != null ? defaultQueueName : ServiceQueue.MAIN;
+ queueId = deviceProfile.getDefaultQueueId();
}
}
- return entityActionMsg(device, device.getId(), ruleNodeId, DataConstants.ENTITY_CREATED, queueName, ruleChainId);
+ return entityActionMsg(device, device.getId(), ruleNodeId, DataConstants.ENTITY_CREATED, queueId, ruleChainId);
}
public TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId) {
@@ -348,21 +359,18 @@ class DefaultTbContext implements TbContext {
public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, String action) {
RuleChainId ruleChainId = null;
- String queueName = ServiceQueue.MAIN;
+ QueueId queueId = null;
if (EntityType.DEVICE.equals(alarm.getOriginator().getEntityType())) {
DeviceId deviceId = new DeviceId(alarm.getOriginator().getId());
DeviceProfile deviceProfile = mainCtx.getDeviceProfileCache().get(getTenantId(), deviceId);
if (deviceProfile == null) {
log.warn("[{}] Device profile is null!", deviceId);
- ruleChainId = null;
- queueName = ServiceQueue.MAIN;
} else {
ruleChainId = deviceProfile.getDefaultRuleChainId();
- String defaultQueueName = deviceProfile.getDefaultQueueName();
- queueName = defaultQueueName != null ? defaultQueueName : ServiceQueue.MAIN;
+ queueId = deviceProfile.getDefaultQueueId();
}
}
- return entityActionMsg(alarm, alarm.getId(), ruleNodeId, action, queueName, ruleChainId);
+ return entityActionMsg(alarm, alarm.getId(), ruleNodeId, action, queueId, ruleChainId);
}
@Override
@@ -371,12 +379,12 @@ class DefaultTbContext implements TbContext {
}
public TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action) {
- return entityActionMsg(entity, id, ruleNodeId, action, ServiceQueue.MAIN, null);
+ return entityActionMsg(entity, id, ruleNodeId, action, null, null);
}
- public TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action, String queueName, RuleChainId ruleChainId) {
+ public TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action, QueueId queueId, RuleChainId ruleChainId) {
try {
- return TbMsg.newMsg(queueName, action, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity)), ruleChainId, null);
+ return TbMsg.newMsg(queueId, action, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity)), ruleChainId, null);
} catch (JsonProcessingException | IllegalArgumentException e) {
throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " " + action + " msg: " + e);
}
@@ -548,6 +556,11 @@ class DefaultTbContext implements TbContext {
return mainCtx.getEdgeEventService();
}
+ @Override
+ public QueueService getQueueService() {
+ return mainCtx.getQueueService();
+ }
+
@Override
public EventLoopGroup getSharedEventLoop() {
return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup();
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
index b4911650f3..add500f058 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
@@ -293,7 +293,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor ruleNodeRelations = nodeRoutes.get(originatorNodeId);
if (ruleNodeRelations == null) { // When unchecked, this will cause NullPointerException when rule node doesn't exist anymore
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
index 2c7faf5226..3d3bf2bbbb 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
+++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
@@ -35,6 +35,7 @@ import org.thingsboard.server.actors.stats.StatsActor;
import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
+import org.thingsboard.server.queue.util.AfterStartUp;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@@ -113,8 +114,7 @@ public class DefaultActorService extends TbApplicationEventListener isolatedTenantId = systemContext.getServiceInfoProvider().getIsolatedTenant();
-
TenantProfile tenantProfile = systemContext.getTenantProfileCache().get(tenant.getTenantProfileId());
isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE);
isRuleEngine = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE);
if (isRuleEngine) {
try {
- if (isolatedTenantId.map(id -> id.equals(tenantId)).orElseGet(() -> !tenantProfile.isIsolatedTbRuleEngine())) {
- if (getApiUsageState().isReExecEnabled()) {
- log.debug("[{}] Going to init rule chains", tenantId);
- initRuleChains();
- } else {
- log.info("[{}] Skip init of the rule chains due to API limits", tenantId);
- }
+ if (getApiUsageState().isReExecEnabled()) {
+ log.debug("[{}] Going to init rule chains", tenantId);
+ initRuleChains();
} else {
- isRuleEngine = false;
+ log.info("[{}] Skip init of the rule chains due to API limits", tenantId);
}
} catch (Exception e) {
cantFindTenant = true;
@@ -133,7 +125,7 @@ public class TenantActor extends RuleChainManagerActor {
switch (msg.getMsgType()) {
case PARTITION_CHANGE_MSG:
PartitionChangeMsg partitionChangeMsg = (PartitionChangeMsg) msg;
- ServiceType serviceType = partitionChangeMsg.getServiceQueueKey().getServiceType();
+ ServiceType serviceType = partitionChangeMsg.getServiceType();
if (ServiceType.TB_RULE_ENGINE.equals(serviceType)) {
//To Rule Chain Actors
broadcast(msg);
diff --git a/application/src/main/java/org/thingsboard/server/config/JwtSettings.java b/application/src/main/java/org/thingsboard/server/config/JwtSettings.java
index 31dbfc68e8..95e510612a 100644
--- a/application/src/main/java/org/thingsboard/server/config/JwtSettings.java
+++ b/application/src/main/java/org/thingsboard/server/config/JwtSettings.java
@@ -15,12 +15,14 @@
*/
package org.thingsboard.server.config;
+import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.security.model.JwtToken;
-@Configuration
+@Component
@ConfigurationProperties(prefix = "security.jwt")
+@Data
public class JwtSettings {
/**
* {@link JwtToken} will expire after this time.
@@ -42,34 +44,4 @@ public class JwtSettings {
*/
private Integer refreshTokenExpTime;
- public Integer getRefreshTokenExpTime() {
- return refreshTokenExpTime;
- }
-
- public void setRefreshTokenExpTime(Integer refreshTokenExpTime) {
- this.refreshTokenExpTime = refreshTokenExpTime;
- }
-
- public Integer getTokenExpirationTime() {
- return tokenExpirationTime;
- }
-
- public void setTokenExpirationTime(Integer tokenExpirationTime) {
- this.tokenExpirationTime = tokenExpirationTime;
- }
-
- public String getTokenIssuer() {
- return tokenIssuer;
- }
- public void setTokenIssuer(String tokenIssuer) {
- this.tokenIssuer = tokenIssuer;
- }
-
- public String getTokenSigningKey() {
- return tokenSigningKey;
- }
-
- public void setTokenSigningKey(String tokenSigningKey) {
- this.tokenSigningKey = tokenSigningKey;
- }
-}
\ No newline at end of file
+}
diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
index 3e9597a72d..823bbf35e3 100644
--- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
@@ -42,6 +42,7 @@ import org.springframework.web.filter.CorsFilter;
import org.thingsboard.server.dao.audit.AuditLogLevelFilter;
import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
+import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.auth.jwt.JwtAuthenticationProvider;
import org.thingsboard.server.service.security.auth.jwt.JwtTokenAuthenticationProcessingFilter;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenAuthenticationProvider;
@@ -61,6 +62,7 @@ import java.util.List;
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
@Order(SecurityProperties.BASIC_AUTH_ORDER)
+@TbCoreComponent
public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapter {
public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization";
@@ -241,8 +243,4 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
}
}
- @Bean
- public AuditLogLevelFilter auditLogLevelFilter(@Autowired AuditLogLevelProperties auditLogLevelProperties) {
- return new AuditLogLevelFilter(auditLogLevelProperties.getMask());
- }
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/AuthController.java b/application/src/main/java/org/thingsboard/server/controller/AuthController.java
index 4887bc205d..72be55a135 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java
@@ -82,7 +82,7 @@ public class AuthController extends BaseController {
@ApiOperation(value = "Get current User (getUser)",
notes = "Get the information about the User which credentials are used to perform this REST API call.")
- @PreAuthorize("isAuthenticated()")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/auth/user", method = RequestMethod.GET)
public @ResponseBody
User getUser() throws ThingsboardException {
@@ -96,7 +96,7 @@ public class AuthController extends BaseController {
@ApiOperation(value = "Logout (logout)",
notes = "Special API call to record the 'logout' of the user to the Audit Logs. Since platform uses [JWT](https://jwt.io/), the actual logout is the procedure of clearing the [JWT](https://jwt.io/) token on the client side. ")
- @PreAuthorize("isAuthenticated()")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/auth/logout", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void logout(HttpServletRequest request) throws ThingsboardException {
@@ -105,7 +105,7 @@ public class AuthController extends BaseController {
@ApiOperation(value = "Change password for current User (changePassword)",
notes = "Change the password for the User which credentials are used to perform this REST API call. Be aware that previously generated [JWT](https://jwt.io/) tokens will be still valid until they expire.")
- @PreAuthorize("isAuthenticated()")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/auth/changePassword", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public ObjectNode changePassword(
diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
index 1daefb7a39..050745844b 100644
--- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
@@ -23,9 +23,11 @@ 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.support.DefaultMessageSourceResolvable;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Customer;
@@ -69,6 +71,7 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.OtaPackageId;
+import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.RpcId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
@@ -84,6 +87,7 @@ import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType;
+import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.rpc.Rpc;
import org.thingsboard.server.common.data.rule.RuleChain;
@@ -108,6 +112,7 @@ import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
import org.thingsboard.server.dao.oauth2.OAuth2Service;
import org.thingsboard.server.dao.ota.OtaPackageService;
+import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rpc.RpcService;
import org.thingsboard.server.dao.rule.RuleChainService;
@@ -144,9 +149,11 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
+import java.util.stream.Collectors;
import static org.thingsboard.server.controller.ControllerConstants.DEFAULT_PAGE_SIZE;
import static org.thingsboard.server.controller.ControllerConstants.INCORRECT_TENANT_ID;
+import static org.thingsboard.server.controller.UserController.YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION;
import static org.thingsboard.server.dao.service.Validator.validateId;
@Slf4j
@@ -271,6 +278,9 @@ public abstract class BaseController {
@Autowired
protected EntityActionService entityActionService;
+ @Autowired
+ protected QueueService queueService;
+
@Value("${server.log_controller_error_stack_trace}")
@Getter
private boolean logControllerErrorStackTrace;
@@ -279,11 +289,37 @@ public abstract class BaseController {
@Getter
protected boolean edgesEnabled;
+ @ExceptionHandler(Exception.class)
+ public void handleControllerException(Exception e, HttpServletResponse response) {
+ ThingsboardException thingsboardException = handleException(e);
+ if (thingsboardException.getErrorCode() == ThingsboardErrorCode.GENERAL && thingsboardException.getCause() instanceof Exception
+ && StringUtils.equals(thingsboardException.getCause().getMessage(), thingsboardException.getMessage())) {
+ e = (Exception) thingsboardException.getCause();
+ } else {
+ e = thingsboardException;
+ }
+ errorResponseHandler.handle(e, response);
+ }
+
@ExceptionHandler(ThingsboardException.class)
public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) {
errorResponseHandler.handle(ex, response);
}
+ /**
+ * @deprecated Exceptions that are not of {@link ThingsboardException} type
+ * are now caught and mapped to {@link ThingsboardException} by
+ * {@link ExceptionHandler} {@link BaseController#handleControllerException(Exception, HttpServletResponse)}
+ * which basically acts like the following boilerplate:
+ * {@code
+ * try {
+ * someExceptionThrowingMethod();
+ * } catch (Exception e) {
+ * throw handleException(e);
+ * }
+ * }
+ * */
+ @Deprecated
ThingsboardException handleException(Exception exception) {
return handleException(exception, true);
}
@@ -310,6 +346,18 @@ public abstract class BaseController {
}
}
+ /**
+ * Handles validation error for controller method arguments annotated with @{@link javax.validation.Valid}
+ * */
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public void handleValidationError(MethodArgumentNotValidException e, HttpServletResponse response) {
+ String errorMessage = "Validation error: " + e.getBindingResult().getAllErrors().stream()
+ .map(DefaultMessageSourceResolvable::getDefaultMessage)
+ .collect(Collectors.joining(", "));
+ ThingsboardException thingsboardException = new ThingsboardException(errorMessage, ThingsboardErrorCode.BAD_REQUEST_PARAMS);
+ handleThingsboardException(thingsboardException, response);
+ }
+
T checkNotNull(T reference) throws ThingsboardException {
return checkNotNull(reference, "Requested item wasn't found!");
}
@@ -520,6 +568,9 @@ public abstract class BaseController {
case OTA_PACKAGE:
checkOtaPackageId(new OtaPackageId(entityId.getId()), operation);
return;
+ case QUEUE:
+ checkQueueId(new QueueId(entityId.getId()), operation);
+ return;
default:
throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType());
}
@@ -811,7 +862,22 @@ public abstract class BaseController {
}
}
- @SuppressWarnings("unchecked")
+ protected Queue checkQueueId(QueueId queueId, Operation operation) throws ThingsboardException {
+ validateId(queueId, "Incorrect queueId " + queueId);
+ Queue queue = queueService.findQueueById(getCurrentUser().getTenantId(), queueId);
+ checkNotNull(queue);
+ accessControlService.checkPermission(getCurrentUser(), Resource.QUEUE, operation, queueId, queue);
+ TenantId tenantId = getTenantId();
+ if (queue.getTenantId().isNullUid() && !tenantId.isNullUid()) {
+ TenantProfile tenantProfile = tenantProfileCache.get(tenantId);
+ if (tenantProfile.isIsolatedTbRuleEngine()) {
+ throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
+ ThingsboardErrorCode.PERMISSION_DENIED);
+ }
+ }
+ return queue;
+ }
+
protected I emptyId(EntityType entityType) {
return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
index 2fc4b7c748..4ebcde7d36 100644
--- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
@@ -32,22 +32,16 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.Customer;
-import org.thingsboard.server.common.data.EntityType;
-import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
-import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
-import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.customer.TbCustomerService;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
-import java.util.List;
-
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_SORT_PROPERTY_ALLOWABLE_VALUES;
@@ -149,7 +143,7 @@ public class CustomerController extends BaseController {
@RequestMapping(value = "/customer", method = RequestMethod.POST)
@ResponseBody
public Customer saveCustomer(@ApiParam(value = "A JSON value representing the customer.") @RequestBody Customer customer) throws ThingsboardException {
- customer.setTenantId(getCurrentUser().getTenantId());
+ customer.setTenantId(getTenantId());
checkEntity(customer.getId(), customer, Resource.CUSTOMER);
return tbCustomerService.save(customer, getCurrentUser());
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
index 49c33045a5..47acdc64a4 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
@@ -22,6 +22,7 @@ import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.Example;
import io.swagger.annotations.ExampleProperty;
+import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@@ -38,15 +39,11 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
-import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HomeDashboard;
import org.thingsboard.server.common.data.HomeDashboardInfo;
-import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
-import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
-import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
@@ -55,6 +52,7 @@ import org.thingsboard.server.common.data.id.TenantId;
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.dashboard.TbDashboardService;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
@@ -62,6 +60,7 @@ import org.thingsboard.server.service.security.permission.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.UUID;
import java.util.stream.Collectors;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID;
@@ -90,9 +89,11 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI
@RestController
@TbCoreComponent
+@RequiredArgsConstructor
@RequestMapping("/api")
public class DashboardController extends BaseController {
+ private final TbDashboardService tbDashboardService;
public static final String DASHBOARD_ID = "dashboardId";
private static final String HOME_DASHBOARD_ID = "homeDashboardId";
@@ -180,28 +181,9 @@ public class DashboardController extends BaseController {
public Dashboard saveDashboard(
@ApiParam(value = "A JSON value representing the dashboard.")
@RequestBody Dashboard dashboard) throws ThingsboardException {
- try {
- dashboard.setTenantId(getCurrentUser().getTenantId());
-
- checkEntity(dashboard.getId(), dashboard, Resource.DASHBOARD);
-
- Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard));
-
- logEntityAction(savedDashboard.getId(), savedDashboard,
- null,
- dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
-
- if (dashboard.getId() != null) {
- sendEntityNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), EdgeEventActionType.UPDATED);
- }
-
- return savedDashboard;
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.DASHBOARD), dashboard,
- null, dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
-
- throw handleException(e);
- }
+ dashboard.setTenantId(getTenantId());
+ checkEntity(dashboard.getId(), dashboard, Resource.DASHBOARD);
+ return tbDashboardService.save(dashboard, getCurrentUser());
}
@ApiOperation(value = "Delete the Dashboard (deleteDashboard)",
@@ -213,28 +195,9 @@ public class DashboardController extends BaseController {
@ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
- try {
- DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
- Dashboard dashboard = checkDashboardId(dashboardId, Operation.DELETE);
-
- List relatedEdgeIds = findRelatedEdgeIds(getTenantId(), dashboardId);
-
- dashboardService.deleteDashboard(getCurrentUser().getTenantId(), dashboardId);
-
- logEntityAction(dashboardId, dashboard,
- null,
- ActionType.DELETED, null, strDashboardId);
-
- sendDeleteNotificationMsg(getTenantId(), dashboardId, relatedEdgeIds);
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.DASHBOARD),
- null,
- null,
- ActionType.DELETED, e, strDashboardId);
-
- throw handleException(e);
- }
+ DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
+ Dashboard dashboard = checkDashboardId(dashboardId, Operation.DELETE);
+ tbDashboardService.delete(dashboard, getCurrentUser());
}
@ApiOperation(value = "Assign the Dashboard (assignDashboardToCustomer)",
@@ -251,30 +214,13 @@ public class DashboardController extends BaseController {
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter(CUSTOMER_ID, strCustomerId);
checkParameter(DASHBOARD_ID, strDashboardId);
- try {
- CustomerId customerId = new CustomerId(toUUID(strCustomerId));
- Customer customer = checkCustomerId(customerId, Operation.READ);
-
- DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
- checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
-
- Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
- logEntityAction(dashboardId, savedDashboard,
- customerId,
- ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, strCustomerId, customer.getName());
+ CustomerId customerId = new CustomerId(toUUID(strCustomerId));
+ Customer customer = checkCustomerId(customerId, Operation.READ);
- sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
-
- return savedDashboard;
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.DASHBOARD), null,
- null,
- ActionType.ASSIGNED_TO_CUSTOMER, e, strDashboardId, strCustomerId);
-
- throw handleException(e);
- }
+ DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
+ checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
+ return tbDashboardService.assignDashboardToCustomer(dashboardId, customer, getCurrentUser());
}
@ApiOperation(value = "Unassign the Dashboard (unassignDashboardFromCustomer)",
@@ -291,29 +237,11 @@ public class DashboardController extends BaseController {
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
checkParameter(DASHBOARD_ID, strDashboardId);
- try {
- CustomerId customerId = new CustomerId(toUUID(strCustomerId));
- Customer customer = checkCustomerId(customerId, Operation.READ);
- DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
- Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
-
- Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
-
- logEntityAction(dashboardId, dashboard,
- customerId,
- ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customer.getId().toString(), customer.getName());
-
- sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
-
- return savedDashboard;
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.DASHBOARD), null,
- null,
- ActionType.UNASSIGNED_FROM_CUSTOMER, e, strDashboardId);
-
- throw handleException(e);
- }
+ CustomerId customerId = new CustomerId(toUUID(strCustomerId));
+ Customer customer = checkCustomerId(customerId, Operation.READ);
+ DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
+ Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
+ return tbDashboardService.unassignDashboardFromCustomer(dashboard, customer, getCurrentUser());
}
@ApiOperation(value = "Update the Dashboard Customers (updateDashboardCustomers)",
@@ -331,69 +259,15 @@ public class DashboardController extends BaseController {
@ApiParam(value = "JSON array with the list of customer ids, or empty to remove all customers")
@RequestBody(required = false) String[] strCustomerIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
- try {
- DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
- Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
-
- Set customerIds = new HashSet<>();
- if (strCustomerIds != null) {
- for (String strCustomerId : strCustomerIds) {
- customerIds.add(new CustomerId(toUUID(strCustomerId)));
- }
- }
-
- Set addedCustomerIds = new HashSet<>();
- Set removedCustomerIds = new HashSet<>();
- for (CustomerId customerId : customerIds) {
- if (!dashboard.isAssignedToCustomer(customerId)) {
- addedCustomerIds.add(customerId);
- }
- }
-
- Set assignedCustomers = dashboard.getAssignedCustomers();
- if (assignedCustomers != null) {
- for (ShortCustomerInfo customerInfo : assignedCustomers) {
- if (!customerIds.contains(customerInfo.getCustomerId())) {
- removedCustomerIds.add(customerInfo.getCustomerId());
- }
- }
- }
-
- if (addedCustomerIds.isEmpty() && removedCustomerIds.isEmpty()) {
- return dashboard;
- } else {
- Dashboard savedDashboard = null;
- for (CustomerId customerId : addedCustomerIds) {
- savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
- ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
- logEntityAction(dashboardId, savedDashboard,
- customerId,
- ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
- sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
- }
- for (CustomerId customerId : removedCustomerIds) {
- ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
- savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
- logEntityAction(dashboardId, dashboard,
- customerId,
- ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
- sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
- }
- return savedDashboard;
- }
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.DASHBOARD), null,
- null,
- ActionType.ASSIGNED_TO_CUSTOMER, e, strDashboardId);
-
- throw handleException(e);
- }
+ DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
+ Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
+ Set customerIds = customerIdFromStr(strCustomerIds, dashboard);
+ return tbDashboardService.updateDashboardCustomers(dashboard, customerIds, getCurrentUser());
}
@ApiOperation(value = "Adds the Dashboard Customers (addDashboardCustomers)",
notes = "Adds the list of Customers to the existing list of assignments for the Dashboard. Keeps previous assignments to customers that are not in the provided list. " +
- "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH,
+ "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@@ -405,42 +279,10 @@ public class DashboardController extends BaseController {
@ApiParam(value = "JSON array with the list of customer ids")
@RequestBody String[] strCustomerIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
- try {
- DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
- Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
-
- Set customerIds = new HashSet<>();
- if (strCustomerIds != null) {
- for (String strCustomerId : strCustomerIds) {
- CustomerId customerId = new CustomerId(toUUID(strCustomerId));
- if (!dashboard.isAssignedToCustomer(customerId)) {
- customerIds.add(customerId);
- }
- }
- }
-
- if (customerIds.isEmpty()) {
- return dashboard;
- } else {
- Dashboard savedDashboard = null;
- for (CustomerId customerId : customerIds) {
- savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
- ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
- logEntityAction(dashboardId, savedDashboard,
- customerId,
- ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
- sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
- }
- return savedDashboard;
- }
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.DASHBOARD), null,
- null,
- ActionType.ASSIGNED_TO_CUSTOMER, e, strDashboardId);
-
- throw handleException(e);
- }
+ DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
+ Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
+ Set customerIds = customerIdFromStr(strCustomerIds, dashboard);
+ return tbDashboardService.addDashboardCustomers(dashboard, customerIds, getCurrentUser());
}
@ApiOperation(value = "Remove the Dashboard Customers (removeDashboardCustomers)",
@@ -457,42 +299,10 @@ public class DashboardController extends BaseController {
@ApiParam(value = "JSON array with the list of customer ids")
@RequestBody String[] strCustomerIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
- try {
- DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
- Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
-
- Set customerIds = new HashSet<>();
- if (strCustomerIds != null) {
- for (String strCustomerId : strCustomerIds) {
- CustomerId customerId = new CustomerId(toUUID(strCustomerId));
- if (dashboard.isAssignedToCustomer(customerId)) {
- customerIds.add(customerId);
- }
- }
- }
-
- if (customerIds.isEmpty()) {
- return dashboard;
- } else {
- Dashboard savedDashboard = null;
- for (CustomerId customerId : customerIds) {
- ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
- savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
- logEntityAction(dashboardId, dashboard,
- customerId,
- ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
- sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
- }
- return savedDashboard;
- }
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.DASHBOARD), null,
- null,
- ActionType.UNASSIGNED_FROM_CUSTOMER, e, strDashboardId);
-
- throw handleException(e);
- }
+ DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
+ Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
+ Set customerIds = customerIdFromStr(strCustomerIds, dashboard);
+ return tbDashboardService.removeDashboardCustomers(dashboard, customerIds, getCurrentUser());
}
@ApiOperation(value = "Assign the Dashboard to Public Customer (assignDashboardToPublicCustomer)",
@@ -510,25 +320,9 @@ public class DashboardController extends BaseController {
@ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
- try {
- DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
- Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
- Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId());
- Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, publicCustomer.getId()));
-
- logEntityAction(dashboardId, savedDashboard,
- publicCustomer.getId(),
- ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, publicCustomer.getId().toString(), publicCustomer.getName());
-
- return savedDashboard;
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.DASHBOARD), null,
- null,
- ActionType.ASSIGNED_TO_CUSTOMER, e, strDashboardId);
-
- throw handleException(e);
- }
+ DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
+ checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
+ return tbDashboardService.assignDashboardToPublicCustomer(dashboardId, getCurrentUser());
}
@ApiOperation(value = "Unassign the Dashboard from Public Customer (unassignDashboardFromPublicCustomer)",
@@ -542,26 +336,9 @@ public class DashboardController extends BaseController {
@ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
- try {
- DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
- Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
- Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId());
-
- Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, publicCustomer.getId()));
-
- logEntityAction(dashboardId, dashboard,
- publicCustomer.getId(),
- ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, publicCustomer.getId().toString(), publicCustomer.getName());
-
- return savedDashboard;
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.DASHBOARD), null,
- null,
- ActionType.UNASSIGNED_FROM_CUSTOMER, e, strDashboardId);
-
- throw handleException(e);
- }
+ DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
+ Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
+ return tbDashboardService.unassignDashboardFromPublicCustomer(dashboard, getCurrentUser());
}
@ApiOperation(value = "Get Tenant Dashboards by System Administrator (getTenantDashboards)",
@@ -671,7 +448,7 @@ public class DashboardController extends BaseController {
"If 'homeDashboardId' parameter is not set on the User and Customer levels then checks the same parameter for the Tenant that owns the user. "
+ DASHBOARD_DEFINITION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
produces = MediaType.APPLICATION_JSON_VALUE)
- @PreAuthorize("isAuthenticated()")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/dashboard/home", method = RequestMethod.GET)
@ResponseBody
public HomeDashboard getHomeDashboard() throws ThingsboardException {
@@ -708,7 +485,7 @@ public class DashboardController extends BaseController {
"If 'homeDashboardId' parameter is not set on the User and Customer levels then checks the same parameter for the Tenant that owns the user. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
produces = MediaType.APPLICATION_JSON_VALUE)
- @PreAuthorize("isAuthenticated()")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/dashboard/home/info", method = RequestMethod.GET)
@ResponseBody
public HomeDashboardInfo getHomeDashboardInfo() throws ThingsboardException {
@@ -775,6 +552,7 @@ public class DashboardController extends BaseController {
public void setTenantHomeDashboardInfo(
@ApiParam(value = "A JSON object that represents home dashboard id and other parameters", required = true)
@RequestBody HomeDashboardInfo homeDashboardInfo) throws ThingsboardException {
+
try {
if (homeDashboardInfo.getDashboardId() != null) {
checkDashboardId(homeDashboardInfo.getDashboardId(), Operation.READ);
@@ -847,30 +625,13 @@ public class DashboardController extends BaseController {
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
checkParameter(DASHBOARD_ID, strDashboardId);
- try {
- EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
- Edge edge = checkEdgeId(edgeId, Operation.READ);
- DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
- checkDashboardId(dashboardId, Operation.READ);
-
- Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
-
- logEntityAction(dashboardId, savedDashboard,
- null,
- ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, strEdgeId, edge.getName());
+ EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
+ Edge edge = checkEdgeId(edgeId, Operation.READ);
- sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedDashboard.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE);
-
- return savedDashboard;
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.DASHBOARD), null,
- null,
- ActionType.ASSIGNED_TO_EDGE, e, strDashboardId, strEdgeId);
-
- throw handleException(e);
- }
+ DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
+ checkDashboardId(dashboardId, Operation.READ);
+ return tbDashboardService.asignDashboardToEdge(dashboardId, edge, getCurrentUser());
}
@ApiOperation(value = "Unassign dashboard from edge (unassignDashboardFromEdge)",
@@ -886,37 +647,22 @@ public class DashboardController extends BaseController {
@ResponseBody
public Dashboard unassignDashboardFromEdge(@PathVariable("edgeId") String strEdgeId,
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
- checkParameter("edgeId", strEdgeId);
+ checkParameter(EDGE_ID, strEdgeId);
checkParameter(DASHBOARD_ID, strDashboardId);
- try {
- EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
- Edge edge = checkEdgeId(edgeId, Operation.READ);
- DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
- Dashboard dashboard = checkDashboardId(dashboardId, Operation.READ);
-
- Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
- logEntityAction(dashboardId, dashboard,
- null,
- ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, strEdgeId, edge.getName());
+ EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
+ Edge edge = checkEdgeId(edgeId, Operation.READ);
- sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedDashboard.getId(), EdgeEventActionType.UNASSIGNED_FROM_EDGE);
+ DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
+ Dashboard dashboard = checkDashboardId(dashboardId, Operation.READ);
- return savedDashboard;
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.DASHBOARD), null,
- null,
- ActionType.UNASSIGNED_FROM_EDGE, e, strDashboardId, strEdgeId);
-
- throw handleException(e);
- }
+ return tbDashboardService.unassignDashboardFromEdge(dashboard, edge, getCurrentUser());
}
@ApiOperation(value = "Get Edge Dashboards (getEdgeDashboards)",
- notes = "Returns a page of dashboard info objects assigned to the specified edge. "
- + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ notes = "Returns a page of dashboard info objects assigned to the specified edge. "
+ + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
+ produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/edge/{edgeId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@@ -957,4 +703,17 @@ public class DashboardController extends BaseController {
throw handleException(e);
}
}
+
+ private Set customerIdFromStr(String [] strCustomerIds, Dashboard dashboard) {
+ Set customerIds = new HashSet<>();
+ if (strCustomerIds != null) {
+ for (String strCustomerId : strCustomerIds) {
+ CustomerId customerId = new CustomerId(UUID.fromString(strCustomerId));
+ if (dashboard.isAssignedToCustomer(customerId)) {
+ customerIds.add(customerId);
+ }
+ }
+ }
+ return customerIds;
+ }
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
index f7b3908010..793ea64336 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
@@ -206,7 +206,7 @@ public class DeviceController extends BaseController {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId, Operation.DELETE);
try {
- tbDeviceService.deleteDevice(device, getCurrentUser()).get();
+ tbDeviceService.delete(device, getCurrentUser()).get();
} catch (Exception e) {
throw handleException(e);
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
index 25a35e77fa..c9330c0a77 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -32,21 +33,17 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileInfo;
-import org.thingsboard.server.common.data.EntityType;
-import org.thingsboard.server.common.data.audit.ActionType;
-import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
-import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.deviceProfile.TbDeviceProfileService;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.List;
-import java.util.Objects;
import java.util.UUID;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_DATA;
@@ -70,9 +67,11 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI
@RestController
@TbCoreComponent
@RequestMapping("/api")
+@RequiredArgsConstructor
@Slf4j
public class DeviceProfileController extends BaseController {
+ private final TbDeviceProfileService tbDeviceProfileService;
@Autowired
private TimeseriesService timeseriesService;
@@ -201,44 +200,9 @@ public class DeviceProfileController extends BaseController {
public DeviceProfile saveDeviceProfile(
@ApiParam(value = "A JSON value representing the device profile.")
@RequestBody DeviceProfile deviceProfile) throws ThingsboardException {
- try {
- boolean created = deviceProfile.getId() == null;
- deviceProfile.setTenantId(getTenantId());
-
- checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE);
-
- boolean isFirmwareChanged = false;
- boolean isSoftwareChanged = false;
-
- if (!created) {
- DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId());
- if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) {
- isFirmwareChanged = true;
- }
- if (!Objects.equals(deviceProfile.getSoftwareId(), oldDeviceProfile.getSoftwareId())) {
- isSoftwareChanged = true;
- }
- }
- DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile));
-
- tbClusterService.onDeviceProfileChange(savedDeviceProfile, null);
- tbClusterService.broadcastEntityStateChangeEvent(deviceProfile.getTenantId(), savedDeviceProfile.getId(),
- created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
-
- logEntityAction(savedDeviceProfile.getId(), savedDeviceProfile,
- null,
- created ? ActionType.ADDED : ActionType.UPDATED, null);
-
- otaPackageStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged);
-
- sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(),
- deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
- return savedDeviceProfile;
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile,
- null, deviceProfile.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
- throw handleException(e);
- }
+ deviceProfile.setTenantId(getTenantId());
+ checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE);
+ return tbDeviceProfileService.save(deviceProfile, getCurrentUser());
}
@ApiOperation(value = "Delete device profile (deleteDeviceProfile)",
@@ -252,27 +216,10 @@ public class DeviceProfileController extends BaseController {
@ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException {
checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId);
- try {
- DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
- DeviceProfile deviceProfile = checkDeviceProfileId(deviceProfileId, Operation.DELETE);
- deviceProfileService.deleteDeviceProfile(getTenantId(), deviceProfileId);
-
- tbClusterService.onDeviceProfileDelete(deviceProfile, null);
- tbClusterService.broadcastEntityStateChangeEvent(deviceProfile.getTenantId(), deviceProfile.getId(), ComponentLifecycleEvent.DELETED);
-
- logEntityAction(deviceProfileId, deviceProfile,
- null,
- ActionType.DELETED, null, strDeviceProfileId);
-
- sendEntityNotificationMsg(getTenantId(), deviceProfile.getId(), EdgeEventActionType.DELETED);
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.DEVICE_PROFILE),
- null,
- null,
- ActionType.DELETED, e, strDeviceProfileId);
- throw handleException(e);
- }
- }
+ DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
+ DeviceProfile deviceProfile = checkDeviceProfileId(deviceProfileId, Operation.DELETE);
+ tbDeviceProfileService.delete(deviceProfile, getCurrentUser());
+ }
@ApiOperation(value = "Make Device Profile Default (setDefaultDeviceProfile)",
notes = "Marks device profile as default within a tenant scope." + TENANT_AUTHORITY_PARAGRAPH,
@@ -284,30 +231,10 @@ public class DeviceProfileController extends BaseController {
@ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException {
checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId);
- try {
- DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
- DeviceProfile deviceProfile = checkDeviceProfileId(deviceProfileId, Operation.WRITE);
- DeviceProfile previousDefaultDeviceProfile = deviceProfileService.findDefaultDeviceProfile(getTenantId());
- if (deviceProfileService.setDefaultDeviceProfile(getTenantId(), deviceProfileId)) {
- if (previousDefaultDeviceProfile != null) {
- previousDefaultDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), previousDefaultDeviceProfile.getId());
-
- logEntityAction(previousDefaultDeviceProfile.getId(), previousDefaultDeviceProfile,
- null, ActionType.UPDATED, null);
- }
- deviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfileId);
-
- logEntityAction(deviceProfile.getId(), deviceProfile,
- null, ActionType.UPDATED, null);
- }
- return deviceProfile;
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.DEVICE_PROFILE),
- null,
- null,
- ActionType.UPDATED, e, strDeviceProfileId);
- throw handleException(e);
- }
+ DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
+ DeviceProfile deviceProfile = checkDeviceProfileId(deviceProfileId, Operation.WRITE);
+ DeviceProfile previousDefaultDeviceProfile = deviceProfileService.findDefaultDeviceProfile(getTenantId());
+ return tbDeviceProfileService.setDefaultDeviceProfile(deviceProfile, previousDefaultDeviceProfile, getCurrentUser());
}
@ApiOperation(value = "Get Device Profiles (getDeviceProfiles)",
diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
index 8a01e9ef02..5d9737db23 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
@@ -164,7 +164,7 @@ public class EdgeController extends BaseController {
accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, edge.getId(), edge);
- return tbEdgeService.saveEdge(edge, edgeTemplateRootRuleChain, getCurrentUser());
+ return tbEdgeService.save(edge, edgeTemplateRootRuleChain, getCurrentUser());
}
@ApiOperation(value = "Delete edge (deleteEdge)",
@@ -177,7 +177,7 @@ public class EdgeController extends BaseController {
checkParameter(EDGE_ID, strEdgeId);
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.DELETE);
- tbEdgeService.deleteEdge(edge, getCurrentUser());
+ tbEdgeService.delete(edge, getCurrentUser());
}
@ApiOperation(value = "Get Tenant Edges (getEdges)",
diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
index f678335622..0a873aaa18 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
+import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -27,9 +28,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
-import org.thingsboard.server.common.data.audit.ActionType;
-import org.thingsboard.server.common.data.edge.EdgeEventActionType;
-import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
@@ -38,6 +36,7 @@ import org.thingsboard.server.common.data.relation.EntityRelationInfo;
import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.entityRelation.TbEntityRelationService;
import org.thingsboard.server.service.security.permission.Operation;
import java.util.List;
@@ -52,8 +51,11 @@ import static org.thingsboard.server.controller.ControllerConstants.RELATION_TYP
@RestController
@TbCoreComponent
@RequestMapping("/api")
+@RequiredArgsConstructor
public class EntityRelationController extends BaseController {
+ private final TbEntityRelationService tbEntityRelationService;
+
public static final String TO_TYPE = "toType";
public static final String FROM_ID = "fromId";
public static final String FROM_TYPE = "fromType";
@@ -77,28 +79,14 @@ public class EntityRelationController extends BaseController {
@ResponseStatus(value = HttpStatus.OK)
public void saveRelation(@ApiParam(value = "A JSON value representing the relation.", required = true)
@RequestBody EntityRelation relation) throws ThingsboardException {
- try {
- checkNotNull(relation);
- checkEntityId(relation.getFrom(), Operation.WRITE);
- checkEntityId(relation.getTo(), Operation.WRITE);
- if (relation.getTypeGroup() == null) {
- relation.setTypeGroup(RelationTypeGroup.COMMON);
- }
- relationService.saveRelation(getTenantId(), relation);
-
- logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
- ActionType.RELATION_ADD_OR_UPDATE, null, relation);
- logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
- ActionType.RELATION_ADD_OR_UPDATE, null, relation);
-
- sendRelationNotificationMsg(getTenantId(), relation, EdgeEventActionType.RELATION_ADD_OR_UPDATE);
- } catch (Exception e) {
- logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
- ActionType.RELATION_ADD_OR_UPDATE, e, relation);
- logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
- ActionType.RELATION_ADD_OR_UPDATE, e, relation);
- throw handleException(e);
+ checkNotNull(relation);
+ checkEntityId(relation.getFrom(), Operation.WRITE);
+ checkEntityId(relation.getTo(), Operation.WRITE);
+ if (relation.getTypeGroup() == null) {
+ relation.setTypeGroup(RelationTypeGroup.COMMON);
}
+
+ tbEntityRelationService.save(getTenantId(), getCurrentUser().getCustomerId(), relation, getCurrentUser());
}
@ApiOperation(value = "Delete Relation (deleteRelation)",
@@ -123,24 +111,8 @@ public class EntityRelationController extends BaseController {
checkEntityId(toId, Operation.WRITE);
RelationTypeGroup relationTypeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
EntityRelation relation = new EntityRelation(fromId, toId, strRelationType, relationTypeGroup);
- try {
- Boolean found = relationService.deleteRelation(getTenantId(), fromId, toId, strRelationType, relationTypeGroup);
- if (!found) {
- throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND);
- }
- logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
- ActionType.RELATION_DELETED, null, relation);
- logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
- ActionType.RELATION_DELETED, null, relation);
- sendRelationNotificationMsg(getTenantId(), relation, EdgeEventActionType.RELATION_DELETED);
- } catch (Exception e) {
- logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
- ActionType.RELATION_DELETED, e, relation);
- logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
- ActionType.RELATION_DELETED, e, relation);
- throw handleException(e);
- }
+ tbEntityRelationService.delete(getTenantId(), getCurrentUser().getCustomerId(), relation, getCurrentUser());
}
@ApiOperation(value = "Delete Relations (deleteRelations)",
@@ -155,13 +127,7 @@ public class EntityRelationController extends BaseController {
checkParameter("entityType", strType);
EntityId entityId = EntityIdFactory.getByTypeAndId(strType, strId);
checkEntityId(entityId, Operation.WRITE);
- try {
- relationService.deleteEntityRelations(getTenantId(), entityId);
- logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, null);
- } catch (Exception e) {
- logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, e);
- throw handleException(e);
- }
+ tbEntityRelationService.deleteRelations (getTenantId(), getCurrentUser().getCustomerId(), entityId, getCurrentUser());
}
@ApiOperation(value = "Get Relation (getRelation)",
diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
index 2dc8fe42d6..a7089969c3 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
@@ -15,15 +15,11 @@
*/
package org.thingsboard.server.controller;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
-import com.google.common.util.concurrent.SettableFuture;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -36,45 +32,30 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.Customer;
-import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntitySubtype;
-import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.EntityViewInfo;
-import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
-import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
-import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.data.kv.AttributeKvEntry;
-import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
-import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
-import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.entityView.TbEntityViewService;
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.annotation.Nullable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
import java.util.List;
-import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
-import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION;
@@ -104,14 +85,14 @@ import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
*/
@RestController
@TbCoreComponent
+@RequiredArgsConstructor
@RequestMapping("/api")
@Slf4j
public class EntityViewController extends BaseController {
- public static final String ENTITY_VIEW_ID = "entityViewId";
+ public final TbEntityViewService tbEntityViewService;
- @Autowired
- private TimeseriesService tsService;
+ public static final String ENTITY_VIEW_ID = "entityViewId";
@ApiOperation(value = "Get entity view (getEntityViewById)",
notes = "Fetch the EntityView object based on the provided entity view id. "
@@ -159,237 +140,15 @@ public class EntityViewController extends BaseController {
public EntityView saveEntityView(
@ApiParam(value = "A JSON object representing the entity view.")
@RequestBody EntityView entityView) throws ThingsboardException {
- try {
- entityView.setTenantId(getCurrentUser().getTenantId());
-
- List> futures = new ArrayList<>();
-
- if (entityView.getId() == null) {
- accessControlService
- .checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.CREATE, null, entityView);
- } else {
- EntityView existingEntityView = checkEntityViewId(entityView.getId(), Operation.WRITE);
- if (existingEntityView.getKeys() != null) {
- if (existingEntityView.getKeys().getAttributes() != null) {
- futures.add(deleteAttributesFromEntityView(existingEntityView, DataConstants.CLIENT_SCOPE, existingEntityView.getKeys().getAttributes().getCs(), getCurrentUser()));
- futures.add(deleteAttributesFromEntityView(existingEntityView, DataConstants.SERVER_SCOPE, existingEntityView.getKeys().getAttributes().getCs(), getCurrentUser()));
- futures.add(deleteAttributesFromEntityView(existingEntityView, DataConstants.SHARED_SCOPE, existingEntityView.getKeys().getAttributes().getCs(), getCurrentUser()));
- }
- }
- List tsKeys = existingEntityView.getKeys() != null && existingEntityView.getKeys().getTimeseries() != null ?
- existingEntityView.getKeys().getTimeseries() : Collections.emptyList();
- futures.add(deleteLatestFromEntityView(existingEntityView, tsKeys, getCurrentUser()));
- }
-
- EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView));
- if (savedEntityView.getKeys() != null) {
- if (savedEntityView.getKeys().getAttributes() != null) {
- futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.CLIENT_SCOPE, savedEntityView.getKeys().getAttributes().getCs(), getCurrentUser()));
- futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SERVER_SCOPE, savedEntityView.getKeys().getAttributes().getSs(), getCurrentUser()));
- futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SHARED_SCOPE, savedEntityView.getKeys().getAttributes().getSh(), getCurrentUser()));
- }
- futures.add(copyLatestFromEntityToEntityView(savedEntityView, getCurrentUser()));
- }
- for (ListenableFuture> future : futures) {
- try {
- future.get();
- } catch (InterruptedException | ExecutionException e) {
- throw new RuntimeException("Failed to copy attributes to entity view", e);
- }
- }
-
- logEntityAction(savedEntityView.getId(), savedEntityView, null,
- entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
-
- if (entityView.getId() != null) {
- sendEntityNotificationMsg(savedEntityView.getTenantId(), savedEntityView.getId(), EdgeEventActionType.UPDATED);
- }
-
- return savedEntityView;
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.ENTITY_VIEW), entityView, null,
- entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
- throw handleException(e);
- }
- }
-
- private ListenableFuture deleteLatestFromEntityView(EntityView entityView, List keys, SecurityUser user) {
- EntityViewId entityId = entityView.getId();
- SettableFuture resultFuture = SettableFuture.create();
- if (keys != null && !keys.isEmpty()) {
- tsSubService.deleteLatest(entityView.getTenantId(), entityId, keys, new FutureCallback() {
- @Override
- public void onSuccess(@Nullable Void tmp) {
- try {
- logTimeseriesDeleted(user, entityId, keys, null);
- } catch (ThingsboardException e) {
- log.error("Failed to log timeseries delete", e);
- }
- resultFuture.set(tmp);
- }
-
- @Override
- public void onFailure(Throwable t) {
- try {
- logTimeseriesDeleted(user, entityId, keys, t);
- } catch (ThingsboardException e) {
- log.error("Failed to log timeseries delete", e);
- }
- resultFuture.setException(t);
- }
- });
- } else {
- tsSubService.deleteAllLatest(entityView.getTenantId(), entityId, new FutureCallback>() {
- @Override
- public void onSuccess(@Nullable Collection keys) {
- try {
- logTimeseriesDeleted(user, entityId, new ArrayList<>(keys), null);
- } catch (ThingsboardException e) {
- log.error("Failed to log timeseries delete", e);
- }
- resultFuture.set(null);
- }
-
- @Override
- public void onFailure(Throwable t) {
- try {
- logTimeseriesDeleted(user, entityId, Collections.emptyList(), t);
- } catch (ThingsboardException e) {
- log.error("Failed to log timeseries delete", e);
- }
- resultFuture.setException(t);
- }
- });
- }
- return resultFuture;
- }
-
- private ListenableFuture deleteAttributesFromEntityView(EntityView entityView, String scope, List keys, SecurityUser user) {
- EntityViewId entityId = entityView.getId();
- SettableFuture resultFuture = SettableFuture.create();
- if (keys != null && !keys.isEmpty()) {
- tsSubService.deleteAndNotify(entityView.getTenantId(), entityId, scope, keys, new FutureCallback() {
- @Override
- public void onSuccess(@Nullable Void tmp) {
- try {
- logAttributesDeleted(user, entityId, scope, keys, null);
- } catch (ThingsboardException e) {
- log.error("Failed to log attribute delete", e);
- }
- resultFuture.set(tmp);
- }
-
- @Override
- public void onFailure(Throwable t) {
- try {
- logAttributesDeleted(user, entityId, scope, keys, t);
- } catch (ThingsboardException e) {
- log.error("Failed to log attribute delete", e);
- }
- resultFuture.setException(t);
- }
- });
- } else {
- resultFuture.set(null);
- }
- return resultFuture;
- }
-
- private ListenableFuture> copyLatestFromEntityToEntityView(EntityView entityView, SecurityUser user) {
- EntityViewId entityId = entityView.getId();
- List keys = entityView.getKeys() != null && entityView.getKeys().getTimeseries() != null ?
- entityView.getKeys().getTimeseries() : Collections.emptyList();
- long startTs = entityView.getStartTimeMs();
- long endTs = entityView.getEndTimeMs() == 0 ? Long.MAX_VALUE : entityView.getEndTimeMs();
- ListenableFuture> keysFuture;
- if (keys.isEmpty()) {
- keysFuture = Futures.transform(tsService.findAllLatest(user.getTenantId(),
- entityView.getEntityId()), latest -> latest.stream().map(TsKvEntry::getKey).collect(Collectors.toList()), MoreExecutors.directExecutor());
- } else {
- keysFuture = Futures.immediateFuture(keys);
- }
- ListenableFuture> latestFuture = Futures.transformAsync(keysFuture, fetchKeys -> {
- List queries = fetchKeys.stream().filter(key -> !isBlank(key)).map(key -> new BaseReadTsKvQuery(key, startTs, endTs, 1, "DESC")).collect(Collectors.toList());
- if (!queries.isEmpty()) {
- return tsService.findAll(user.getTenantId(), entityView.getEntityId(), queries);
- } else {
- return Futures.immediateFuture(null);
- }
- }, MoreExecutors.directExecutor());
- return Futures.transform(latestFuture, latestValues -> {
- if (latestValues != null && !latestValues.isEmpty()) {
- tsSubService.saveLatestAndNotify(entityView.getTenantId(), entityId, latestValues, new FutureCallback() {
- @Override
- public void onSuccess(@Nullable Void tmp) {
- }
-
- @Override
- public void onFailure(Throwable t) {
- }
- });
- }
- return null;
- }, MoreExecutors.directExecutor());
- }
-
- private ListenableFuture> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection keys, SecurityUser user) throws ThingsboardException {
- EntityViewId entityId = entityView.getId();
- if (keys != null && !keys.isEmpty()) {
- ListenableFuture> getAttrFuture = attributesService.find(getTenantId(), entityView.getEntityId(), scope, keys);
- return Futures.transform(getAttrFuture, attributeKvEntries -> {
- List attributes;
- if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) {
- attributes =
- attributeKvEntries.stream()
- .filter(attributeKvEntry -> {
- long startTime = entityView.getStartTimeMs();
- long endTime = entityView.getEndTimeMs();
- long lastUpdateTs = attributeKvEntry.getLastUpdateTs();
- return startTime == 0 && endTime == 0 ||
- (endTime == 0 && startTime < lastUpdateTs) ||
- (startTime == 0 && endTime > lastUpdateTs)
- ? true : startTime < lastUpdateTs && endTime > lastUpdateTs;
- }).collect(Collectors.toList());
- tsSubService.saveAndNotify(entityView.getTenantId(), entityId, scope, attributes, new FutureCallback() {
- @Override
- public void onSuccess(@Nullable Void tmp) {
- try {
- logAttributesUpdated(user, entityId, scope, attributes, null);
- } catch (ThingsboardException e) {
- log.error("Failed to log attribute updates", e);
- }
- }
-
- @Override
- public void onFailure(Throwable t) {
- try {
- logAttributesUpdated(user, entityId, scope, attributes, t);
- } catch (ThingsboardException e) {
- log.error("Failed to log attribute updates", e);
- }
- }
- });
- }
- return null;
- }, MoreExecutors.directExecutor());
+ entityView.setTenantId(getCurrentUser().getTenantId());
+ EntityView existingEntityView = null;
+ if (entityView.getId() == null) {
+ accessControlService
+ .checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.CREATE, null, entityView);
} else {
- return Futures.immediateFuture(null);
+ existingEntityView = checkEntityViewId(entityView.getId(), Operation.WRITE);
}
- }
-
- private void logAttributesUpdated(SecurityUser user, EntityId entityId, String scope, List attributes, Throwable e) throws ThingsboardException {
- logEntityAction(user, entityId, null, null, ActionType.ATTRIBUTES_UPDATED, toException(e),
- scope, attributes);
- }
-
- private void logAttributesDeleted(SecurityUser user, EntityId entityId, String scope, List keys, Throwable e) throws ThingsboardException {
- logEntityAction(user, entityId, null, null, ActionType.ATTRIBUTES_DELETED, toException(e),
- scope, keys);
- }
-
- private void logTimeseriesDeleted(SecurityUser user, EntityId entityId, List keys, Throwable e) throws ThingsboardException {
- logEntityAction(user, entityId, null, null, ActionType.TIMESERIES_DELETED, toException(e),
- keys);
+ return tbEntityViewService.save(entityView, existingEntityView, getCurrentUser());
}
@ApiOperation(value = "Delete entity view (deleteEntityView)",
@@ -402,24 +161,9 @@ public class EntityViewController extends BaseController {
@ApiParam(value = ENTITY_VIEW_ID_PARAM_DESCRIPTION)
@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
- try {
- EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
- EntityView entityView = checkEntityViewId(entityViewId, Operation.DELETE);
-
- List relatedEdgeIds = findRelatedEdgeIds(getTenantId(), entityViewId);
-
- entityViewService.deleteEntityView(getTenantId(), entityViewId);
- logEntityAction(entityViewId, entityView, entityView.getCustomerId(),
- ActionType.DELETED, null, strEntityViewId);
-
- sendDeleteNotificationMsg(getTenantId(), entityViewId, relatedEdgeIds);
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.ENTITY_VIEW),
- null,
- null,
- ActionType.DELETED, e, strEntityViewId);
- throw handleException(e);
- }
+ EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
+ EntityView entityView = checkEntityViewId(entityViewId, Operation.DELETE);
+ tbEntityViewService.delete(entityView, getCurrentUser());
}
@ApiOperation(value = "Get Entity View by name (getTenantEntityView)",
@@ -451,28 +195,14 @@ public class EntityViewController extends BaseController {
@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(CUSTOMER_ID, strCustomerId);
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
- try {
- CustomerId customerId = new CustomerId(toUUID(strCustomerId));
- Customer customer = checkCustomerId(customerId, Operation.READ);
-
- EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
- checkEntityViewId(entityViewId, Operation.ASSIGN_TO_CUSTOMER);
- EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(getTenantId(), entityViewId, customerId));
- logEntityAction(entityViewId, savedEntityView,
- savedEntityView.getCustomerId(),
- ActionType.ASSIGNED_TO_CUSTOMER, null, strEntityViewId, strCustomerId, customer.getName());
+ CustomerId customerId = new CustomerId(toUUID(strCustomerId));
+ Customer customer = checkCustomerId(customerId, Operation.READ);
- sendEntityAssignToCustomerNotificationMsg(savedEntityView.getTenantId(), savedEntityView.getId(),
- customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
+ EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
+ checkEntityViewId(entityViewId, Operation.ASSIGN_TO_CUSTOMER);
- return savedEntityView;
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
- null,
- ActionType.ASSIGNED_TO_CUSTOMER, e, strEntityViewId, strCustomerId);
- throw handleException(e);
- }
+ return tbEntityViewService.assignEntityViewToCustomer(getTenantId(), entityViewId, customer, getCurrentUser());
}
@ApiOperation(value = "Unassign Entity View from customer (unassignEntityViewFromCustomer)",
@@ -484,28 +214,15 @@ public class EntityViewController extends BaseController {
@ApiParam(value = ENTITY_VIEW_ID_PARAM_DESCRIPTION)
@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
- try {
- EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
- EntityView entityView = checkEntityViewId(entityViewId, Operation.UNASSIGN_FROM_CUSTOMER);
- if (entityView.getCustomerId() == null || entityView.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
- throw new IncorrectParameterException("Entity View isn't assigned to any customer!");
- }
- Customer customer = checkCustomerId(entityView.getCustomerId(), Operation.READ);
- EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromCustomer(getTenantId(), entityViewId));
- logEntityAction(entityViewId, entityView,
- entityView.getCustomerId(),
- ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEntityViewId, customer.getId().toString(), customer.getName());
+ EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
+ EntityView entityView = checkEntityViewId(entityViewId, Operation.UNASSIGN_FROM_CUSTOMER);
+ if (entityView.getCustomerId() == null || entityView.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
+ throw new IncorrectParameterException("Entity View isn't assigned to any customer!");
+ }
- sendEntityAssignToCustomerNotificationMsg(savedEntityView.getTenantId(), savedEntityView.getId(),
- customer.getId(), EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
+ Customer customer = checkCustomerId(entityView.getCustomerId(), Operation.READ);
- return savedEntityView;
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
- null,
- ActionType.UNASSIGNED_FROM_CUSTOMER, e, strEntityViewId);
- throw handleException(e);
- }
+ return tbEntityViewService.unassignEntityViewFromCustomer(getTenantId(), entityViewId, customer, getCurrentUser());
}
@ApiOperation(value = "Get Customer Entity Views (getCustomerEntityViews)",
@@ -705,23 +422,13 @@ public class EntityViewController extends BaseController {
@ApiParam(value = ENTITY_VIEW_ID_PARAM_DESCRIPTION)
@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
- try {
- EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
- EntityView entityView = checkEntityViewId(entityViewId, Operation.ASSIGN_TO_CUSTOMER);
- Customer publicCustomer = customerService.findOrCreatePublicCustomer(entityView.getTenantId());
- EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(getCurrentUser().getTenantId(), entityViewId, publicCustomer.getId()));
+ EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
+ checkEntityViewId(entityViewId, Operation.ASSIGN_TO_CUSTOMER);
- logEntityAction(entityViewId, savedEntityView,
- savedEntityView.getCustomerId(),
- ActionType.ASSIGNED_TO_CUSTOMER, null, strEntityViewId, publicCustomer.getId().toString(), publicCustomer.getName());
+ Customer publicCustomer = customerService.findOrCreatePublicCustomer(getTenantId());
- return savedEntityView;
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
- null,
- ActionType.ASSIGNED_TO_CUSTOMER, e, strEntityViewId);
- throw handleException(e);
- }
+ return tbEntityViewService.assignEntityViewToPublicCustomer(getTenantId(), getCurrentUser().getCustomerId(),
+ publicCustomer, entityViewId, getCurrentUser());
}
@ApiOperation(value = "Assign entity view to edge (assignEntityViewToEdge)",
@@ -738,27 +445,14 @@ public class EntityViewController extends BaseController {
@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
- try {
- EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
- Edge edge = checkEdgeId(edgeId, Operation.READ);
- EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
- checkEntityViewId(entityViewId, Operation.READ);
+ EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
+ Edge edge = checkEdgeId(edgeId, Operation.READ);
- EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToEdge(getTenantId(), entityViewId, edgeId));
- logEntityAction(entityViewId, savedEntityView,
- savedEntityView.getCustomerId(),
- ActionType.ASSIGNED_TO_EDGE, null, strEntityViewId, strEdgeId, edge.getName());
+ EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
+ checkEntityViewId(entityViewId, Operation.READ);
- sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedEntityView.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE);
-
- return savedEntityView;
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
- null,
- ActionType.ASSIGNED_TO_EDGE, e, strEntityViewId, strEdgeId);
- throw handleException(e);
- }
+ return tbEntityViewService.assignEntityViewToEdge(getTenantId(), getCurrentUser().getCustomerId(), entityViewId, edge, getCurrentUser());
}
@ApiOperation(value = "Unassign entity view from edge (unassignEntityViewFromEdge)",
@@ -775,27 +469,14 @@ public class EntityViewController extends BaseController {
@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
- try {
- EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
- Edge edge = checkEdgeId(edgeId, Operation.READ);
- EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
- EntityView entityView = checkEntityViewId(entityViewId, Operation.READ);
-
- EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(getTenantId(), entityViewId, edgeId));
- logEntityAction(entityViewId, entityView,
- entityView.getCustomerId(),
- ActionType.UNASSIGNED_FROM_EDGE, null, strEntityViewId, strEdgeId, edge.getName());
+ EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
+ Edge edge = checkEdgeId(edgeId, Operation.READ);
- sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedEntityView.getId(), EdgeEventActionType.UNASSIGNED_FROM_EDGE);
+ EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
+ EntityView entityView = checkEntityViewId(entityViewId, Operation.READ);
- return savedEntityView;
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
- null,
- ActionType.UNASSIGNED_FROM_EDGE, e, strEntityViewId, strEdgeId);
- throw handleException(e);
- }
+ return tbEntityViewService.unassignEntityViewFromEdge(getTenantId(), entityView.getCustomerId(), entityView, edge, getCurrentUser());
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
diff --git a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java
index 281a13a6a0..ba3845127d 100644
--- a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java
@@ -17,8 +17,8 @@ package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
@@ -32,11 +32,9 @@ import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
-import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
-import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.OtaPackageId;
@@ -45,11 +43,10 @@ import org.thingsboard.server.common.data.ota.OtaPackageType;
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.otaPackageController.TbOtaPackageService;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
-import java.nio.ByteBuffer;
-
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_ID_PARAM_DESCRIPTION;
@@ -73,8 +70,11 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI
@RestController
@TbCoreComponent
@RequestMapping("/api")
+@RequiredArgsConstructor
public class OtaPackageController extends BaseController {
+ private final TbOtaPackageService tbOtaPackageService;
+
public static final String OTA_PACKAGE_ID = "otaPackageId";
public static final String CHECKSUM_ALGORITHM = "checksumAlgorithm";
@@ -154,19 +154,12 @@ public class OtaPackageController extends BaseController {
@ResponseBody
public OtaPackageInfo saveOtaPackageInfo(@ApiParam(value = "A JSON value representing the OTA Package.")
@RequestBody SaveOtaPackageInfoRequest otaPackageInfo) throws ThingsboardException {
- boolean created = otaPackageInfo.getId() == null;
- try {
- otaPackageInfo.setTenantId(getTenantId());
- checkEntity(otaPackageInfo.getId(), otaPackageInfo, Resource.OTA_PACKAGE);
- OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(new OtaPackageInfo(otaPackageInfo), otaPackageInfo.isUsesUrl());
- logEntityAction(savedOtaPackageInfo.getId(), savedOtaPackageInfo,
- null, created ? ActionType.ADDED : ActionType.UPDATED, null);
- return savedOtaPackageInfo;
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.OTA_PACKAGE), otaPackageInfo,
- null, created ? ActionType.ADDED : ActionType.UPDATED, e);
- throw handleException(e);
- }
+ otaPackageInfo.setTenantId(getTenantId());
+ checkEntity(otaPackageInfo.getId(), otaPackageInfo, Resource.OTA_PACKAGE);
+
+ return tbOtaPackageService.save(otaPackageInfo, getCurrentUser());
+
+
}
@ApiOperation(value = "Save OTA Package data (saveOtaPackageData)",
@@ -186,38 +179,14 @@ public class OtaPackageController extends BaseController {
@RequestPart MultipartFile file) throws ThingsboardException {
checkParameter(OTA_PACKAGE_ID, strOtaPackageId);
checkParameter(CHECKSUM_ALGORITHM, checksumAlgorithmStr);
+ OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId));
+ OtaPackageInfo otaPackageInfo = checkOtaPackageInfoId(otaPackageId, Operation.READ);
try {
- OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId));
- OtaPackageInfo info = checkOtaPackageInfoId(otaPackageId, Operation.READ);
-
- OtaPackage otaPackage = new OtaPackage(otaPackageId);
- otaPackage.setCreatedTime(info.getCreatedTime());
- otaPackage.setTenantId(getTenantId());
- otaPackage.setDeviceProfileId(info.getDeviceProfileId());
- otaPackage.setType(info.getType());
- otaPackage.setTitle(info.getTitle());
- otaPackage.setVersion(info.getVersion());
- otaPackage.setTag(info.getTag());
- otaPackage.setAdditionalInfo(info.getAdditionalInfo());
-
ChecksumAlgorithm checksumAlgorithm = ChecksumAlgorithm.valueOf(checksumAlgorithmStr.toUpperCase());
-
- byte[] bytes = file.getBytes();
- if (StringUtils.isEmpty(checksum)) {
- checksum = otaPackageService.generateChecksum(checksumAlgorithm, ByteBuffer.wrap(bytes));
- }
-
- otaPackage.setChecksumAlgorithm(checksumAlgorithm);
- otaPackage.setChecksum(checksum);
- otaPackage.setFileName(file.getOriginalFilename());
- otaPackage.setContentType(file.getContentType());
- otaPackage.setData(ByteBuffer.wrap(bytes));
- otaPackage.setDataSize((long) bytes.length);
- OtaPackageInfo savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage);
- logEntityAction(savedOtaPackage.getId(), savedOtaPackage, null, ActionType.UPDATED, null);
- return savedOtaPackage;
+ byte[] data = file.getBytes();
+ return tbOtaPackageService.saveOtaPackageData(otaPackageInfo, checksum, checksumAlgorithm,
+ data, file.getOriginalFilename(), file.getContentType(), getCurrentUser());
} catch (Exception e) {
- logEntityAction(emptyId(EntityType.OTA_PACKAGE), null, null, ActionType.UPDATED, e, strOtaPackageId);
throw handleException(e);
}
}
@@ -289,15 +258,10 @@ public class OtaPackageController extends BaseController {
public void deleteOtaPackage(@ApiParam(value = OTA_PACKAGE_ID_PARAM_DESCRIPTION)
@PathVariable("otaPackageId") String strOtaPackageId) throws ThingsboardException {
checkParameter(OTA_PACKAGE_ID, strOtaPackageId);
- try {
- OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId));
- OtaPackageInfo info = checkOtaPackageInfoId(otaPackageId, Operation.DELETE);
- otaPackageService.deleteOtaPackage(getTenantId(), otaPackageId);
- logEntityAction(otaPackageId, info, null, ActionType.DELETED, null, strOtaPackageId);
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.OTA_PACKAGE), null, null, ActionType.DELETED, e, strOtaPackageId);
- throw handleException(e);
- }
+ OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId));
+ OtaPackageInfo otaPackageInfo = checkOtaPackageInfoId(otaPackageId, Operation.DELETE);
+
+ tbOtaPackageService.delete(otaPackageInfo, getCurrentUser());
}
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/QueueController.java b/application/src/main/java/org/thingsboard/server/controller/QueueController.java
index 33cf7b5a0f..39a971e6ed 100644
--- a/application/src/main/java/org/thingsboard/server/controller/QueueController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/QueueController.java
@@ -15,26 +15,27 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
-import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.QueueId;
+import org.thingsboard.server.common.data.page.PageData;
+import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.msg.queue.ServiceType;
-import org.thingsboard.server.queue.QueueService;
import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.queue.TbQueueService;
+import org.thingsboard.server.service.security.permission.Operation;
+import org.thingsboard.server.service.security.permission.Resource;
-import java.util.Set;
-
-import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES;
-import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SERVICE_TYPE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
+import java.util.UUID;
@RestController
@TbCoreComponent
@@ -42,18 +43,82 @@ import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHO
@RequiredArgsConstructor
public class QueueController extends BaseController {
- private final QueueService queueService;
+ private final TbQueueService tbQueueService;
- @ApiOperation(value = "Get queue names (getTenantQueuesByServiceType)",
- notes = "Returns a set of unique queue names based on service type. " + TENANT_AUTHORITY_PARAGRAPH)
- @PreAuthorize("hasAuthority('TENANT_ADMIN')")
- @RequestMapping(value = "/tenant/queues", params = {"serviceType"}, produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
- @ResponseBody()
- public Set getTenantQueuesByServiceType(@ApiParam(value = QUEUE_SERVICE_TYPE_DESCRIPTION, allowableValues = QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES)
- @RequestParam String serviceType) throws ThingsboardException {
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
+ @RequestMapping(value = "/queues", params = {"serviceType", "pageSize", "page"}, method = RequestMethod.GET)
+ @ResponseBody
+ public PageData getTenantQueuesByServiceType(@RequestParam String serviceType,
+ @RequestParam int pageSize,
+ @RequestParam int page,
+ @RequestParam(required = false) String textSearch,
+ @RequestParam(required = false) String sortProperty,
+ @RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("serviceType", serviceType);
try {
- return queueService.getQueuesByServiceType(ServiceType.valueOf(serviceType));
+ PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
+ ServiceType type = ServiceType.valueOf(serviceType);
+ switch (type) {
+ case TB_RULE_ENGINE:
+ return queueService.findQueuesByTenantId(getTenantId(), pageLink);
+ default:
+ return new PageData<>();
+ }
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
+ @RequestMapping(value = "/queues/{queueId}", method = RequestMethod.GET)
+ @ResponseBody
+ public Queue getQueueById(@PathVariable("queueId") String queueIdStr) throws ThingsboardException {
+ checkParameter("queueId", queueIdStr);
+ try {
+ QueueId queueId = new QueueId(UUID.fromString(queueIdStr));
+ checkQueueId(queueId, Operation.READ);
+ return checkNotNull(queueService.findQueueById(getTenantId(), queueId));
+ } catch (
+ Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
+ @RequestMapping(value = "/queues", params = {"serviceType"}, method = RequestMethod.POST)
+ @ResponseBody
+ public Queue saveQueue(@RequestBody Queue queue,
+ @RequestParam String serviceType) throws ThingsboardException {
+ checkParameter("serviceType", serviceType);
+ try {
+ queue.setTenantId(getCurrentUser().getTenantId());
+
+ checkEntity(queue.getId(), queue, Resource.QUEUE);
+
+ ServiceType type = ServiceType.valueOf(serviceType);
+ switch (type) {
+ case TB_RULE_ENGINE:
+ queue.setTenantId(getTenantId());
+ Queue savedQueue = tbQueueService.saveQueue(queue);
+ checkNotNull(savedQueue);
+ return savedQueue;
+ default:
+ return null;
+ }
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
+ @RequestMapping(value = "/queues/{queueId}", method = RequestMethod.DELETE)
+ @ResponseBody
+ public void deleteQueue(@PathVariable("queueId") String queueIdStr) throws ThingsboardException {
+ checkParameter("queueId", queueIdStr);
+ try {
+ QueueId queueId = new QueueId(toUUID(queueIdStr));
+ checkQueueId(queueId, Operation.DELETE);
+ tbQueueService.deleteQueue(getTenantId(), queueId);
} catch (Exception e) {
throw handleException(e);
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
index 6479b838b2..190d68e79e 100644
--- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
@@ -40,12 +40,9 @@ import org.thingsboard.rule.engine.api.ScriptEngine;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
import org.thingsboard.server.common.data.DataConstants;
-import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.StringUtils;
-import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
-import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
@@ -62,14 +59,11 @@ import org.thingsboard.server.common.data.rule.RuleChainImportResult;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage;
import org.thingsboard.server.common.data.rule.RuleChainType;
-import org.thingsboard.server.common.data.rule.RuleChainUpdateResult;
-import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.queue.util.TbCoreComponent;
-import org.thingsboard.server.service.install.InstallScripts;
import org.thingsboard.server.service.rule.TbRuleChainService;
import org.thingsboard.server.service.script.JsInvokeService;
import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
@@ -77,15 +71,11 @@ import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.TreeSet;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
import static org.thingsboard.server.controller.ControllerConstants.EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION;
@@ -146,9 +136,6 @@ public class RuleChainController extends BaseController {
@Autowired
protected TbRuleChainService tbRuleChainService;
- @Autowired
- private InstallScripts installScripts;
-
@Autowired
private EventService eventService;
@@ -246,38 +233,11 @@ public class RuleChainController extends BaseController {
public RuleChain saveRuleChain(
@ApiParam(value = "A JSON value representing the rule chain.")
@RequestBody RuleChain ruleChain) throws ThingsboardException {
- try {
- boolean created = ruleChain.getId() == null;
- ruleChain.setTenantId(getCurrentUser().getTenantId());
-
- checkEntity(ruleChain.getId(), ruleChain, Resource.RULE_CHAIN);
-
- RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain));
-
- if (RuleChainType.CORE.equals(savedRuleChain.getType())) {
- tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), savedRuleChain.getId(),
- created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
- }
-
- logEntityAction(savedRuleChain.getId(), savedRuleChain,
- null,
- created ? ActionType.ADDED : ActionType.UPDATED, null);
- if (RuleChainType.EDGE.equals(savedRuleChain.getType())) {
- if (!created) {
- sendEntityNotificationMsg(savedRuleChain.getTenantId(), savedRuleChain.getId(), EdgeEventActionType.UPDATED);
- }
- }
-
- return savedRuleChain;
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.RULE_CHAIN), ruleChain,
- null, ruleChain.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
-
- throw handleException(e);
- }
- }
+ ruleChain.setTenantId(getCurrentUser().getTenantId());
+ checkEntity(ruleChain.getId(), ruleChain, Resource.RULE_CHAIN);
+ return tbRuleChainService.save(ruleChain, getCurrentUser());
+ }
@ApiOperation(value = "Create Default Rule Chain",
notes = "Create rule chain from template, based on the specified name in the request. " +
@@ -288,24 +248,11 @@ public class RuleChainController extends BaseController {
public RuleChain saveRuleChain(
@ApiParam(value = "A JSON value representing the request.")
@RequestBody DefaultRuleChainCreateRequest request) throws ThingsboardException {
- try {
- checkNotNull(request);
- checkParameter(request.getName(), "name");
-
- RuleChain savedRuleChain = installScripts.createDefaultRuleChain(getCurrentUser().getTenantId(), request.getName());
- tbClusterService.broadcastEntityStateChangeEvent(savedRuleChain.getTenantId(), savedRuleChain.getId(), ComponentLifecycleEvent.CREATED);
-
- logEntityAction(savedRuleChain.getId(), savedRuleChain, null, ActionType.ADDED, null);
-
- return savedRuleChain;
- } catch (Exception e) {
- RuleChain ruleChain = new RuleChain();
- ruleChain.setName(request.getName());
- logEntityAction(emptyId(EntityType.RULE_CHAIN), ruleChain, null, ActionType.ADDED, e);
- throw handleException(e);
- }
- }
+ checkNotNull(request);
+ checkParameter(request.getName(), "name");
+ return tbRuleChainService.saveDefaultByName(getTenantId(), request, getCurrentUser());
+ }
@ApiOperation(value = "Set Root Rule Chain (setRootRuleChain)",
notes = "Makes the rule chain to be root rule chain. Updates previous root rule chain as well. " + TENANT_AUTHORITY_PARAGRAPH)
@@ -316,38 +263,10 @@ public class RuleChainController extends BaseController {
@ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
- try {
- RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
- RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
- TenantId tenantId = getCurrentUser().getTenantId();
- RuleChain previousRootRuleChain = ruleChainService.getRootTenantRuleChain(tenantId);
- if (ruleChainService.setRootRuleChain(getTenantId(), ruleChainId)) {
- if (previousRootRuleChain != null) {
- previousRootRuleChain = ruleChainService.findRuleChainById(getTenantId(), previousRootRuleChain.getId());
-
- tbClusterService.broadcastEntityStateChangeEvent(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(),
- ComponentLifecycleEvent.UPDATED);
+ RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
+ RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
+ return tbRuleChainService.setRootRuleChain(getTenantId(),ruleChain, getCurrentUser());
- logEntityAction(previousRootRuleChain.getId(), previousRootRuleChain,
- null, ActionType.UPDATED, null);
- }
- ruleChain = ruleChainService.findRuleChainById(getTenantId(), ruleChainId);
-
- tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), ruleChain.getId(),
- ComponentLifecycleEvent.UPDATED);
-
- logEntityAction(ruleChain.getId(), ruleChain,
- null, ActionType.UPDATED, null);
-
- }
- return ruleChain;
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.RULE_CHAIN),
- null,
- null,
- ActionType.UPDATED, e, strRuleChainId);
- throw handleException(e);
- }
}
@ApiOperation(value = "Update Rule Chain Metadata",
@@ -361,59 +280,19 @@ public class RuleChainController extends BaseController {
@ApiParam(value = "Update related rule nodes.")
@RequestParam(value = "updateRelated", required = false, defaultValue = "true") boolean updateRelated
) throws ThingsboardException {
- try {
- TenantId tenantId = getTenantId();
- if (debugPerTenantEnabled) {
- ConcurrentMap debugPerTenantLimits = actorContext.getDebugPerTenantLimits();
- DebugTbRateLimits debugTbRateLimits = debugPerTenantLimits.getOrDefault(tenantId, null);
- if (debugTbRateLimits != null) {
- debugPerTenantLimits.remove(tenantId, debugTbRateLimits);
- }
- }
-
- RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE);
- RuleChainUpdateResult result = ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData);
- checkNotNull(result.isSuccess() ? true : null);
-
- List updatedRuleChains;
- if (updateRelated && result.isSuccess()) {
- updatedRuleChains = tbRuleChainService.updateRelatedRuleChains(tenantId, ruleChainMetaData.getRuleChainId(), result);
- } else {
- updatedRuleChains = Collections.emptyList();
- }
-
- RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId()));
-
- if (RuleChainType.CORE.equals(ruleChain.getType())) {
- tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
- updatedRuleChains.forEach(updatedRuleChain -> {
- tbClusterService.broadcastEntityStateChangeEvent(updatedRuleChain.getTenantId(), updatedRuleChain.getId(), ComponentLifecycleEvent.UPDATED);
- });
+ TenantId tenantId = getTenantId();
+ if (debugPerTenantEnabled) {
+ ConcurrentMap debugPerTenantLimits = actorContext.getDebugPerTenantLimits();
+ DebugTbRateLimits debugTbRateLimits = debugPerTenantLimits.getOrDefault(tenantId, null);
+ if (debugTbRateLimits != null) {
+ debugPerTenantLimits.remove(tenantId, debugTbRateLimits);
}
-
- logEntityAction(ruleChain.getId(), ruleChain, null, ActionType.UPDATED, null, ruleChainMetaData);
- for (RuleChain updatedRuleChain : updatedRuleChains) {
- RuleChainMetaData updatedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, updatedRuleChain.getId()));
- logEntityAction(updatedRuleChain.getId(), updatedRuleChain, null, ActionType.UPDATED, null, updatedRuleChainMetaData);
- }
-
- if (RuleChainType.EDGE.equals(ruleChain.getType())) {
- sendEntityNotificationMsg(ruleChain.getTenantId(), ruleChain.getId(), EdgeEventActionType.UPDATED);
- updatedRuleChains.forEach(updatedRuleChain -> {
- sendEntityNotificationMsg(updatedRuleChain.getTenantId(), updatedRuleChain.getId(), EdgeEventActionType.UPDATED);
- });
- }
-
- return savedRuleChainMetaData;
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.RULE_CHAIN), null,
- null, ActionType.UPDATED, e, ruleChainMetaData);
-
- throw handleException(e);
}
- }
+ RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE);
+ return tbRuleChainService.saveRuleChainMetaData(tenantId, ruleChain, ruleChainMetaData, updateRelated,
+ getCurrentUser());
+ }
@ApiOperation(value = "Get Rule Chains (getRuleChains)",
notes = "Returns a page of Rule Chains owned by tenant. " + RULE_CHAIN_DESCRIPTION + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
@@ -456,46 +335,11 @@ public class RuleChainController extends BaseController {
@ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
- try {
- RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
- RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.DELETE);
-
- List referencingRuleNodes = ruleChainService.getReferencingRuleChainNodes(getTenantId(), ruleChainId);
- Set referencingRuleChainIds = referencingRuleNodes.stream().map(RuleNode::getRuleChainId).collect(Collectors.toSet());
-
- List relatedEdgeIds = null;
- if (RuleChainType.EDGE.equals(ruleChain.getType())) {
- relatedEdgeIds = findRelatedEdgeIds(getTenantId(), ruleChainId);
- }
-
- ruleChainService.deleteRuleChainById(getTenantId(), ruleChainId);
-
- referencingRuleChainIds.remove(ruleChain.getId());
-
- if (RuleChainType.CORE.equals(ruleChain.getType())) {
- referencingRuleChainIds.forEach(referencingRuleChainId ->
- tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
-
- tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
- }
-
- logEntityAction(ruleChainId, ruleChain,
- null,
- ActionType.DELETED, null, strRuleChainId);
-
- if (RuleChainType.EDGE.equals(ruleChain.getType())) {
- sendDeleteNotificationMsg(ruleChain.getTenantId(), ruleChain.getId(), relatedEdgeIds);
- }
-
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.RULE_CHAIN),
- null,
- null,
- ActionType.DELETED, e, strRuleChainId);
- throw handleException(e);
- }
- }
+ RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
+ RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.DELETE);
+ tbRuleChainService.delete(ruleChain, getCurrentUser());
+ }
@ApiOperation(value = "Get latest input message (getLatestRuleNodeDebugInput)",
notes = "Gets the input message from the debug events for specified Rule Chain Id. " +
@@ -681,31 +525,14 @@ public class RuleChainController extends BaseController {
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
checkParameter(RULE_CHAIN_ID, strRuleChainId);
- try {
- EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
- Edge edge = checkEdgeId(edgeId, Operation.WRITE);
-
- RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
- checkRuleChain(ruleChainId, Operation.READ);
-
- RuleChain savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId));
-
- logEntityAction(ruleChainId, savedRuleChain,
- null,
- ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, strEdgeId, edge.getName());
-
- sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedRuleChain.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE);
+ EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
+ Edge edge = checkEdgeId(edgeId, Operation.WRITE);
- return savedRuleChain;
- } catch (Exception e) {
+ RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
+ RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.READ);
- logEntityAction(emptyId(EntityType.RULE_CHAIN), null,
- null,
- ActionType.ASSIGNED_TO_EDGE, e, strRuleChainId, strEdgeId);
-
- throw handleException(e);
- }
- }
+ return tbRuleChainService.assignRuleChainToEdge(getTenantId(), ruleChain, edge, getCurrentUser());
+ }
@ApiOperation(value = "Unassign rule chain from edge (unassignRuleChainFromEdge)",
notes = "Clears assignment of the rule chain to the edge. " +
@@ -721,30 +548,13 @@ public class RuleChainController extends BaseController {
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
checkParameter(RULE_CHAIN_ID, strRuleChainId);
- try {
- EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
- Edge edge = checkEdgeId(edgeId, Operation.WRITE);
- RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
- RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.READ);
-
- RuleChain savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId, false));
-
- logEntityAction(ruleChainId, ruleChain,
- null,
- ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, strEdgeId, edge.getName());
-
- sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedRuleChain.getId(), EdgeEventActionType.UNASSIGNED_FROM_EDGE);
+ EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
+ Edge edge = checkEdgeId(edgeId, Operation.WRITE);
+ RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
+ RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.READ);
- return savedRuleChain;
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.RULE_CHAIN), null,
- null,
- ActionType.UNASSIGNED_FROM_EDGE, e, strRuleChainId, strEdgeId);
-
- throw handleException(e);
- }
- }
+ return tbRuleChainService.unassignRuleChainFromEdge(getTenantId(), ruleChain, edge, getCurrentUser());
+ }
@ApiOperation(value = "Get Edge Rule Chains (getEdgeRuleChains)",
notes = "Returns a page of Rule Chains assigned to the specified edge. " + RULE_CHAIN_DESCRIPTION + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
@@ -785,18 +595,9 @@ public class RuleChainController extends BaseController {
public RuleChain setEdgeTemplateRootRuleChain(@ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
- try {
- RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
- RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
- ruleChainService.setEdgeTemplateRootRuleChain(getTenantId(), ruleChainId);
- return ruleChain;
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.RULE_CHAIN),
- null,
- null,
- ActionType.UPDATED, e, strRuleChainId);
- throw handleException(e);
- }
+ RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
+ RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
+ return tbRuleChainService.setEdgeTemplateRootRuleChain(getTenantId(), ruleChain, getCurrentUser());
}
@ApiOperation(value = "Set Auto Assign To Edge Rule Chain (setAutoAssignToEdgeRuleChain)",
@@ -808,19 +609,10 @@ public class RuleChainController extends BaseController {
public RuleChain setAutoAssignToEdgeRuleChain(@ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
- try {
- RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
- RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
- ruleChainService.setAutoAssignToEdgeRuleChain(getTenantId(), ruleChainId);
- return ruleChain;
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.RULE_CHAIN),
- null,
- null,
- ActionType.UPDATED, e, strRuleChainId);
- throw handleException(e);
- }
- }
+ RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
+ RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
+ return tbRuleChainService.setAutoAssignToEdgeRuleChain(getTenantId(), ruleChain, getCurrentUser());
+ }
@ApiOperation(value = "Unset Auto Assign To Edge Rule Chain (unsetAutoAssignToEdgeRuleChain)",
notes = "Removes the rule chain from the list of rule chains that are going to be automatically assigned for any new edge that will be created. " +
@@ -831,19 +623,11 @@ public class RuleChainController extends BaseController {
public RuleChain unsetAutoAssignToEdgeRuleChain(@ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
- try {
- RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
- RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
- ruleChainService.unsetAutoAssignToEdgeRuleChain(getTenantId(), ruleChainId);
- return ruleChain;
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.RULE_CHAIN),
- null,
- null,
- ActionType.UPDATED, e, strRuleChainId);
- throw handleException(e);
- }
- }
+ RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
+ RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
+
+ return tbRuleChainService.unsetAutoAssignToEdgeRuleChain(getTenantId(), ruleChain, getCurrentUser());
+ }
// TODO: @voba refactor this - add new config to edge rule chain to set it as auto-assign
@ApiOperation(value = "Get Auto Assign To Edge Rule Chains (getAutoAssignToEdgeRuleChains)",
@@ -865,5 +649,4 @@ public class RuleChainController extends BaseController {
throw handleException(e);
}
}
-
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
index 24373f54a4..120449c137 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders;
@@ -30,10 +31,8 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
-import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo;
-import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TbResourceId;
import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
@@ -41,6 +40,7 @@ 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.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.resource.TbResourceService;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
@@ -68,8 +68,11 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI
@RestController
@TbCoreComponent
@RequestMapping("/api")
+@RequiredArgsConstructor
public class TbResourceController extends BaseController {
+ private final TbResourceService tbResourceService;
+
public static final String RESOURCE_ID = "resourceId";
@ApiOperation(value = "Download Resource (downloadResource)", notes = "Download Resource based on the provided Resource Id." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@@ -144,20 +147,9 @@ public class TbResourceController extends BaseController {
@ResponseBody
public TbResource saveResource(@ApiParam(value = "A JSON value representing the Resource.")
@RequestBody TbResource resource) throws ThingsboardException {
- boolean created = resource.getId() == null;
- try {
- resource.setTenantId(getTenantId());
- checkEntity(resource.getId(), resource, Resource.TB_RESOURCE);
- TbResource savedResource = checkNotNull(resourceService.saveResource(resource));
- tbClusterService.onResourceChange(savedResource, null);
- logEntityAction(savedResource.getId(), savedResource,
- null, created ? ActionType.ADDED : ActionType.UPDATED, null);
- return savedResource;
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.TB_RESOURCE), resource,
- null, created ? ActionType.ADDED : ActionType.UPDATED, e);
- throw handleException(e);
- }
+ resource.setTenantId(getTenantId());
+ checkEntity(resource.getId(), resource, Resource.TB_RESOURCE);
+ return tbResourceService.save(resource, getCurrentUser());
}
@ApiOperation(value = "Get Resource Infos (getResources)",
@@ -242,16 +234,8 @@ public class TbResourceController extends BaseController {
public void deleteResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
@PathVariable("resourceId") String strResourceId) throws ThingsboardException {
checkParameter(RESOURCE_ID, strResourceId);
- try {
- TbResourceId resourceId = new TbResourceId(toUUID(strResourceId));
- TbResource tbResource = checkResourceId(resourceId, Operation.DELETE);
- resourceService.deleteResource(getTenantId(), resourceId);
- tbClusterService.onResourceDeleted(tbResource, null);
- logEntityAction(resourceId, tbResource, null, ActionType.DELETED, null, strResourceId);
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.TB_RESOURCE), null, null, ActionType.DELETED, e, strResourceId);
- throw handleException(e);
- }
+ TbResourceId resourceId = new TbResourceId(toUUID(strResourceId));
+ TbResource tbResource = checkResourceId(resourceId, Operation.DELETE);
+ tbResourceService.delete(tbResource, getCurrentUser());
}
-
}
\ No newline at end of file
diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantController.java b/application/src/main/java/org/thingsboard/server/controller/TenantController.java
index 6d6e99cbba..5e18f1e3a5 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TenantController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TenantController.java
@@ -18,7 +18,6 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
-import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
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 2512a46c1d..40956d0fcc 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -37,6 +38,7 @@ import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.tenant_profile.TbTenantProfileService;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
@@ -59,10 +61,13 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI
@TbCoreComponent
@RequestMapping("/api")
@Slf4j
+@RequiredArgsConstructor
public class TenantProfileController extends BaseController {
private static final String TENANT_PROFILE_INFO_DESCRIPTION = "Tenant Profile Info is a lightweight object that contains only id and name of the profile. ";
+ private final TbTenantProfileService tbTenantProfileService;
+
@ApiOperation(value = "Get Tenant Profile (getTenantProfileById)",
notes = "Fetch the Tenant Profile object based on the provided Tenant Profile Id. " + SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@@ -171,14 +176,16 @@ public class TenantProfileController extends BaseController {
@RequestBody TenantProfile tenantProfile) throws ThingsboardException {
try {
boolean newTenantProfile = tenantProfile.getId() == null;
+ TenantProfile oldProfile;
if (newTenantProfile) {
accessControlService
.checkPermission(getCurrentUser(), Resource.TENANT_PROFILE, Operation.CREATE);
+ oldProfile = null;
} else {
- checkEntityId(tenantProfile.getId(), Operation.WRITE);
+ oldProfile = checkTenantProfileId(tenantProfile.getId(), Operation.WRITE);
}
- tenantProfile = checkNotNull(tenantProfileService.saveTenantProfile(getTenantId(), tenantProfile));
+ tenantProfile = checkNotNull(tbTenantProfileService.saveTenantProfile(getTenantId(), tenantProfile, oldProfile));
tenantProfileCache.put(tenantProfile);
tbClusterService.onTenantProfileChange(tenantProfile, null);
tbClusterService.broadcastEntityStateChangeEvent(TenantId.SYS_TENANT_ID, tenantProfile.getId(),
diff --git a/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java b/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java
new file mode 100644
index 0000000000..4b34b95f59
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java
@@ -0,0 +1,272 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.controller;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings;
+import org.thingsboard.server.common.data.security.model.mfa.account.AccountTwoFaSettings;
+import org.thingsboard.server.common.data.security.model.mfa.account.TwoFaAccountConfig;
+import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderConfig;
+import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType;
+import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService;
+import org.thingsboard.server.service.security.auth.mfa.config.TwoFaConfigManager;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+import javax.validation.Valid;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE;
+
+@RestController
+@RequestMapping("/api/2fa")
+@TbCoreComponent
+@RequiredArgsConstructor
+public class TwoFaConfigController extends BaseController {
+
+ private final TwoFaConfigManager twoFaConfigManager;
+ private final TwoFactorAuthService twoFactorAuthService;
+
+
+ @ApiOperation(value = "Get account 2FA settings (getAccountTwoFaSettings)",
+ notes = "Get user's account 2FA configuration. Configuration contains configs for different 2FA providers." + NEW_LINE +
+ "Example:\n" +
+ "```\n{\n \"configs\": {\n" +
+ " \"EMAIL\": {\n \"providerType\": \"EMAIL\",\n \"useByDefault\": true,\n \"email\": \"tenant@thingsboard.org\"\n },\n" +
+ " \"TOTP\": {\n \"providerType\": \"TOTP\",\n \"useByDefault\": false,\n \"authUrl\": \"otpauth://totp/TB%202FA:tenant@thingsboard.org?issuer=TB+2FA&secret=P6Z2TLYTASOGP6LCJZAD24ETT5DACNNX\"\n },\n" +
+ " \"SMS\": {\n \"providerType\": \"SMS\",\n \"useByDefault\": false,\n \"phoneNumber\": \"+380501253652\"\n }\n" +
+ " }\n}\n```" +
+ ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER)
+ @GetMapping("/account/settings")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
+ public AccountTwoFaSettings getAccountTwoFaSettings() throws ThingsboardException {
+ SecurityUser user = getCurrentUser();
+ return twoFaConfigManager.getAccountTwoFaSettings(user.getTenantId(), user.getId()).orElse(null);
+ }
+
+
+ @ApiOperation(value = "Generate 2FA account config (generateTwoFaAccountConfig)",
+ notes = "Generate new 2FA account config template for specified provider type. " + NEW_LINE +
+ "For TOTP, this will return a corresponding account config template " +
+ "with a generated OTP auth URL (with new random secret key for each API call) that can be then " +
+ "converted to a QR code to scan with an authenticator app. Example:\n" +
+ "```\n{\n" +
+ " \"providerType\": \"TOTP\",\n" +
+ " \"useByDefault\": false,\n" +
+ " \"authUrl\": \"otpauth://totp/TB%202FA:tenant@thingsboard.org?issuer=TB+2FA&secret=PNJDNWJVAK4ZTUYT7RFGPQLXA7XGU7PX\"\n" +
+ "}\n```" + NEW_LINE +
+ "For EMAIL, the generated config will contain email from user's account:\n" +
+ "```\n{\n" +
+ " \"providerType\": \"EMAIL\",\n" +
+ " \"useByDefault\": false,\n" +
+ " \"email\": \"tenant@thingsboard.org\"\n" +
+ "}\n```" + NEW_LINE +
+ "For SMS 2FA this method will just return a config with empty/default values as there is nothing to generate/preset:\n" +
+ "```\n{\n" +
+ " \"providerType\": \"SMS\",\n" +
+ " \"useByDefault\": false,\n" +
+ " \"phoneNumber\": null\n" +
+ "}\n```" + NEW_LINE +
+ "Will throw an error (Bad Request) if the provider is not configured for usage. " +
+ ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER)
+ @PostMapping("/account/config/generate")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
+ public TwoFaAccountConfig generateTwoFaAccountConfig(@ApiParam(value = "2FA provider type to generate new account config for", defaultValue = "TOTP", required = true)
+ @RequestParam TwoFaProviderType providerType) throws Exception {
+ SecurityUser user = getCurrentUser();
+ return twoFactorAuthService.generateNewAccountConfig(user, providerType);
+ }
+
+ @ApiOperation(value = "Submit 2FA account config (submitTwoFaAccountConfig)",
+ notes = "Submit 2FA account config to prepare for a future verification. " +
+ "Basically, this method will send a verification code for a given account config, if this has " +
+ "sense for a chosen 2FA provider. This code is needed to then verify and save the account config." + NEW_LINE +
+ "Example of EMAIL 2FA account config:\n" +
+ "```\n{\n" +
+ " \"providerType\": \"EMAIL\",\n" +
+ " \"useByDefault\": true,\n" +
+ " \"email\": \"separate-email-for-2fa@thingsboard.org\"\n" +
+ "}\n```" + NEW_LINE +
+ "Example of SMS 2FA account config:\n" +
+ "```\n{\n" +
+ " \"providerType\": \"SMS\",\n" +
+ " \"useByDefault\": false,\n" +
+ " \"phoneNumber\": \"+38012312321\"\n" +
+ "}\n```" + NEW_LINE +
+ "For TOTP this method does nothing." + NEW_LINE +
+ "Will throw an error (Bad Request) if submitted account config is not valid, " +
+ "or if the provider is not configured for usage. " +
+ ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER)
+ @PostMapping("/account/config/submit")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
+ public void submitTwoFaAccountConfig(@Valid @RequestBody TwoFaAccountConfig accountConfig) throws Exception {
+ SecurityUser user = getCurrentUser();
+ twoFactorAuthService.prepareVerificationCode(user, accountConfig, false);
+ }
+
+ @ApiOperation(value = "Verify and save 2FA account config (verifyAndSaveTwoFaAccountConfig)",
+ notes = "Checks the verification code for submitted config, and if it is correct, saves the provided account config. " + NEW_LINE +
+ "Returns whole account's 2FA settings object.\n" +
+ "Will throw an error (Bad Request) if the provider is not configured for usage. " +
+ ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER)
+ @PostMapping("/account/config")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
+ public AccountTwoFaSettings verifyAndSaveTwoFaAccountConfig(@Valid @RequestBody TwoFaAccountConfig accountConfig,
+ @RequestParam(required = false) String verificationCode) throws Exception {
+ SecurityUser user = getCurrentUser();
+ if (twoFaConfigManager.getTwoFaAccountConfig(user.getTenantId(), user.getId(), accountConfig.getProviderType()).isPresent()) {
+ throw new IllegalArgumentException("2FA provider is already configured");
+ }
+
+ boolean verificationSuccess;
+ if (accountConfig.getProviderType() != TwoFaProviderType.BACKUP_CODE) {
+ verificationSuccess = twoFactorAuthService.checkVerificationCode(user, verificationCode, accountConfig, false);
+ } else {
+ verificationSuccess = true;
+ }
+ if (verificationSuccess) {
+ return twoFaConfigManager.saveTwoFaAccountConfig(user.getTenantId(), user.getId(), accountConfig);
+ } else {
+ throw new IllegalArgumentException("Verification code is incorrect");
+ }
+ }
+
+ @ApiOperation(value = "Update 2FA account config (updateTwoFaAccountConfig)", notes =
+ "Update config for a given provider type. \n" +
+ "Update request example:\n" +
+ "```\n{\n \"useByDefault\": true\n}\n```\n" +
+ "Returns whole account's 2FA settings object.\n" +
+ ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER)
+ @PutMapping("/account/config")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
+ public AccountTwoFaSettings updateTwoFaAccountConfig(@RequestParam TwoFaProviderType providerType,
+ @RequestBody TwoFaAccountConfigUpdateRequest updateRequest) throws ThingsboardException {
+ SecurityUser user = getCurrentUser();
+
+ TwoFaAccountConfig accountConfig = twoFaConfigManager.getTwoFaAccountConfig(user.getTenantId(), user.getId(), providerType)
+ .orElseThrow(() -> new IllegalArgumentException("Config for " + providerType + " 2FA provider not found"));
+ accountConfig.setUseByDefault(updateRequest.isUseByDefault());
+ return twoFaConfigManager.saveTwoFaAccountConfig(user.getTenantId(), user.getId(), accountConfig);
+ }
+
+ @ApiOperation(value = "Delete 2FA account config (deleteTwoFaAccountConfig)", notes =
+ "Delete 2FA config for a given 2FA provider type. \n" +
+ "Returns whole account's 2FA settings object.\n" +
+ ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER)
+ @DeleteMapping("/account/config")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
+ public AccountTwoFaSettings deleteTwoFaAccountConfig(@RequestParam TwoFaProviderType providerType) throws ThingsboardException {
+ SecurityUser user = getCurrentUser();
+ return twoFaConfigManager.deleteTwoFaAccountConfig(user.getTenantId(), user.getId(), providerType);
+ }
+
+
+ @ApiOperation(value = "Get available 2FA providers (getAvailableTwoFaProviders)", notes =
+ "Get the list of provider types available for user to use (the ones configured by tenant or sysadmin).\n" +
+ "Example of response:\n" +
+ "```\n[\n \"TOTP\",\n \"EMAIL\",\n \"SMS\"\n]\n```" +
+ ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER
+ )
+ @GetMapping("/providers")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
+ public List getAvailableTwoFaProviders() throws ThingsboardException {
+ return twoFaConfigManager.getPlatformTwoFaSettings(getTenantId(), true)
+ .map(PlatformTwoFaSettings::getProviders).orElse(Collections.emptyList()).stream()
+ .map(TwoFaProviderConfig::getProviderType)
+ .collect(Collectors.toList());
+ }
+
+
+ @ApiOperation(value = "Get platform 2FA settings (getPlatformTwoFaSettings)",
+ notes = "Get platform settings for 2FA. The settings are described for savePlatformTwoFaSettings API method. " +
+ "If 2FA is not configured, then an empty response will be returned." +
+ ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
+ @GetMapping("/settings")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
+ public PlatformTwoFaSettings getPlatformTwoFaSettings() throws ThingsboardException {
+ return twoFaConfigManager.getPlatformTwoFaSettings(getTenantId(), false).orElse(null);
+ }
+
+ @ApiOperation(value = "Save platform 2FA settings (savePlatformTwoFaSettings)",
+ notes = "Save 2FA settings for platform. The settings have following properties:\n" +
+ "- `providers` - the list of 2FA providers' configs. Users will only be allowed to use 2FA providers from this list. \n\n" +
+ "- `minVerificationCodeSendPeriod` - minimal period in seconds to wait after verification code send request to send next request. \n" +
+ "- `verificationCodeCheckRateLimit` - rate limit configuration for verification code checking.\n" +
+ "The format is standard: 'amountOfRequests:periodInSeconds'. The value of '1:60' would limit verification " +
+ "code checking requests to one per minute.\n" +
+ "- `maxVerificationFailuresBeforeUserLockout` - maximum number of verification failures before a user gets disabled.\n" +
+ "- `totalAllowedTimeForVerification` - total amount of time in seconds allotted for verification. " +
+ "Basically, this property sets a lifetime for pre-verification token. If not set, default value of 30 minutes is used.\n" + NEW_LINE +
+ "TOTP 2FA provider config has following settings:\n" +
+ "- `issuerName` - issuer name that will be displayed in an authenticator app near a username. Must not be blank.\n\n" +
+ "For SMS 2FA provider:\n" +
+ "- `smsVerificationMessageTemplate` - verification message template. Available template variables " +
+ "are ${code} and ${userEmail}. It must not be blank and must contain verification code variable.\n" +
+ "- `verificationCodeLifetime` - verification code lifetime in seconds. Required to be positive.\n\n" +
+ "For EMAIL provider type:\n" +
+ "- `verificationCodeLifetime` - the same as for SMS." + NEW_LINE +
+ "Example of the settings:\n" +
+ "```\n{\n" +
+ " \"providers\": [\n" +
+ " {\n" +
+ " \"providerType\": \"TOTP\",\n" +
+ " \"issuerName\": \"TB\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"providerType\": \"EMAIL\",\n" +
+ " \"verificationCodeLifetime\": 60\n" +
+ " },\n" +
+ " {\n" +
+ " \"providerType\": \"SMS\",\n" +
+ " \"verificationCodeLifetime\": 60,\n" +
+ " \"smsVerificationMessageTemplate\": \"Here is your verification code: ${code}\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"minVerificationCodeSendPeriod\": 60,\n" +
+ " \"verificationCodeCheckRateLimit\": \"3:900\",\n" +
+ " \"maxVerificationFailuresBeforeUserLockout\": 10,\n" +
+ " \"totalAllowedTimeForVerification\": 600\n" +
+ "}\n```" +
+ ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
+ @PostMapping("/settings")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
+ public PlatformTwoFaSettings savePlatformTwoFaSettings(@ApiParam(value = "Settings value", required = true)
+ @RequestBody PlatformTwoFaSettings twoFaSettings) throws ThingsboardException {
+ return twoFaConfigManager.savePlatformTwoFaSettings(getTenantId(), twoFaSettings);
+ }
+
+
+ @Data
+ public static class TwoFaAccountConfigUpdateRequest {
+ private boolean useByDefault;
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthController.java b/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthController.java
new file mode 100644
index 0000000000..003b4ab450
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthController.java
@@ -0,0 +1,152 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.controller;
+
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.thingsboard.server.common.data.StringUtils;
+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.security.model.mfa.PlatformTwoFaSettings;
+import org.thingsboard.server.common.data.security.model.mfa.account.EmailTwoFaAccountConfig;
+import org.thingsboard.server.common.data.security.model.mfa.account.SmsTwoFaAccountConfig;
+import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType;
+import org.thingsboard.server.dao.user.UserService;
+import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService;
+import org.thingsboard.server.service.security.auth.mfa.config.TwoFaConfigManager;
+import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails;
+import org.thingsboard.server.service.security.model.JwtTokenPair;
+import org.thingsboard.server.service.security.model.SecurityUser;
+import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
+import org.thingsboard.server.service.security.system.SystemSecurityService;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE;
+
+@RestController
+@RequestMapping("/api/auth/2fa")
+@TbCoreComponent
+@RequiredArgsConstructor
+public class TwoFactorAuthController extends BaseController {
+
+ private final TwoFactorAuthService twoFactorAuthService;
+ private final TwoFaConfigManager twoFaConfigManager;
+ private final JwtTokenFactory tokenFactory;
+ private final SystemSecurityService systemSecurityService;
+ private final UserService userService;
+
+
+ @ApiOperation(value = "Request 2FA verification code (requestTwoFaVerificationCode)",
+ notes = "Request 2FA verification code." + NEW_LINE +
+ "To make a request to this endpoint, you need an access token with the scope of PRE_VERIFICATION_TOKEN, " +
+ "which is issued on username/password auth if 2FA is enabled." + NEW_LINE +
+ "The API method is rate limited (using rate limit config from TwoFactorAuthSettings). " +
+ "Will return a Bad Request error if provider is not configured for usage, " +
+ "and Too Many Requests error if rate limits are exceeded.")
+ @PostMapping("/verification/send")
+ @PreAuthorize("hasAuthority('PRE_VERIFICATION_TOKEN')")
+ public void requestTwoFaVerificationCode(@RequestParam TwoFaProviderType providerType) throws Exception {
+ SecurityUser user = getCurrentUser();
+ twoFactorAuthService.prepareVerificationCode(user, providerType, true);
+ }
+
+ @ApiOperation(value = "Check 2FA verification code (checkTwoFaVerificationCode)",
+ notes = "Checks 2FA verification code, and if it is correct the method returns a regular access and refresh token pair." + NEW_LINE +
+ "The API method is rate limited (using rate limit config from TwoFactorAuthSettings), and also will block a user " +
+ "after X unsuccessful verification attempts if such behavior is configured (in TwoFactorAuthSettings)." + NEW_LINE +
+ "Will return a Bad Request error if provider is not configured for usage, " +
+ "and Too Many Requests error if rate limits are exceeded.")
+ @PostMapping("/verification/check")
+ @PreAuthorize("hasAuthority('PRE_VERIFICATION_TOKEN')")
+ public JwtTokenPair checkTwoFaVerificationCode(@RequestParam TwoFaProviderType providerType,
+ @RequestParam String verificationCode, HttpServletRequest servletRequest) throws Exception {
+ SecurityUser user = getCurrentUser();
+ boolean verificationSuccess = twoFactorAuthService.checkVerificationCode(user, providerType, verificationCode, true);
+ if (verificationSuccess) {
+ systemSecurityService.logLoginAction(user, new RestAuthenticationDetails(servletRequest), ActionType.LOGIN, null);
+ user = new SecurityUser(userService.findUserById(user.getTenantId(), user.getId()), true, user.getUserPrincipal());
+ return tokenFactory.createTokenPair(user);
+ } else {
+ ThingsboardException error = new ThingsboardException("Verification code is incorrect", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
+ systemSecurityService.logLoginAction(user, new RestAuthenticationDetails(servletRequest), ActionType.LOGIN, error);
+ throw error;
+ }
+ }
+
+
+ @ApiOperation(value = "Get available 2FA providers (getAvailableTwoFaProviders)", notes =
+ "Get the list of 2FA provider infos available for user to use. Example:\n" +
+ "```\n[\n" +
+ " {\n \"type\": \"EMAIL\",\n \"default\": true,\n \"contact\": \"ab*****ko@gmail.com\"\n },\n" +
+ " {\n \"type\": \"TOTP\",\n \"default\": false,\n \"contact\": null\n },\n" +
+ " {\n \"type\": \"SMS\",\n \"default\": false,\n \"contact\": \"+38********12\"\n }\n" +
+ "]\n```")
+ @GetMapping("/providers")
+ @PreAuthorize("hasAuthority('PRE_VERIFICATION_TOKEN')")
+ public List getAvailableTwoFaProviders() throws ThingsboardException {
+ SecurityUser user = getCurrentUser();
+ Optional platformTwoFaSettings = twoFaConfigManager.getPlatformTwoFaSettings(user.getTenantId(), true);
+ return twoFaConfigManager.getAccountTwoFaSettings(user.getTenantId(), user.getId())
+ .map(settings -> settings.getConfigs().values()).orElse(Collections.emptyList())
+ .stream().map(config -> {
+ String contact = null;
+ switch (config.getProviderType()) {
+ case SMS:
+ String phoneNumber = ((SmsTwoFaAccountConfig) config).getPhoneNumber();
+ contact = StringUtils.obfuscate(phoneNumber, 2, '*', phoneNumber.indexOf('+') + 1, phoneNumber.length());
+ break;
+ case EMAIL:
+ String email = ((EmailTwoFaAccountConfig) config).getEmail();
+ contact = StringUtils.obfuscate(email, 2, '*', 0, email.indexOf('@'));
+ break;
+ }
+ return TwoFaProviderInfo.builder()
+ .type(config.getProviderType())
+ .isDefault(config.isUseByDefault())
+ .contact(contact)
+ .minVerificationCodeSendPeriod(platformTwoFaSettings.get().getMinVerificationCodeSendPeriod())
+ .build();
+ })
+ .collect(Collectors.toList());
+ }
+
+ @Data
+ @AllArgsConstructor
+ @Builder
+ public static class TwoFaProviderInfo {
+ private TwoFaProviderType type;
+ private boolean isDefault;
+ private String contact;
+ private Integer minVerificationCodeSendPeriod;
+ }
+
+}
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 250f5392b5..d6fda64d31 100644
--- a/application/src/main/java/org/thingsboard/server/controller/UserController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java
@@ -33,14 +33,10 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.rule.engine.api.MailService;
-import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
-import org.thingsboard.server.common.data.audit.ActionType;
-import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
-import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
@@ -50,6 +46,7 @@ import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.common.data.security.event.UserAuthDataChangedEvent;
import org.thingsboard.server.common.data.security.model.JwtToken;
import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.user.TbUserService;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
import org.thingsboard.server.service.security.model.JwtTokenPair;
import org.thingsboard.server.service.security.model.SecurityUser;
@@ -60,7 +57,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.List;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
@@ -102,6 +98,7 @@ public class UserController extends BaseController {
private final RefreshTokenRepository refreshTokenRepository;
private final SystemSecurityService systemSecurityService;
private final ApplicationEventPublisher eventPublisher;
+ private final TbUserService tbUserService;
@ApiOperation(value = "Get User (getUserById)",
notes = "Fetch the User object based on the provided User Id. " +
@@ -188,47 +185,12 @@ public class UserController extends BaseController {
@RequestBody User user,
@ApiParam(value = "Send activation email (or use activation link)", defaultValue = "true")
@RequestParam(required = false, defaultValue = "true") boolean sendActivationMail, HttpServletRequest request) throws ThingsboardException {
- try {
-
- if (Authority.TENANT_ADMIN.equals(getCurrentUser().getAuthority())) {
- user.setTenantId(getCurrentUser().getTenantId());
- }
-
- checkEntity(user.getId(), user, Resource.USER);
-
- boolean sendEmail = user.getId() == null && sendActivationMail;
- User savedUser = checkNotNull(userService.saveUser(user));
- if (sendEmail) {
- SecurityUser authUser = getCurrentUser();
- UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), savedUser.getId());
- String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request);
- String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl,
- userCredentials.getActivateToken());
- String email = savedUser.getEmail();
- try {
- mailService.sendActivationEmail(activateUrl, email);
- } catch (ThingsboardException e) {
- userService.deleteUser(authUser.getTenantId(), savedUser.getId());
- throw e;
- }
- }
-
- logEntityAction(savedUser.getId(), savedUser,
- savedUser.getCustomerId(),
- user.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
-
- sendEntityNotificationMsg(getTenantId(), savedUser.getId(),
- user.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
-
- return savedUser;
- } catch (Exception e) {
-
- logEntityAction(emptyId(EntityType.USER), user,
- null, user.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
-
- throw handleException(e);
+ if (Authority.TENANT_ADMIN.equals(getCurrentUser().getAuthority())) {
+ user.setTenantId(getCurrentUser().getTenantId());
}
- }
+ checkEntity(user.getId(), user, Resource.USER);
+ return tbUserService.save(getTenantId(), getCurrentUser().getCustomerId(), user, sendActivationMail, request, getCurrentUser());
+ }
@ApiOperation(value = "Send or re-send the activation email",
notes = "Force send the activation email to the user. Useful to resend the email if user has accidentally deleted it. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@@ -298,31 +260,12 @@ public class UserController extends BaseController {
@ApiParam(value = USER_ID_PARAM_DESCRIPTION)
@PathVariable(USER_ID) String strUserId) throws ThingsboardException {
checkParameter(USER_ID, strUserId);
- try {
- UserId userId = new UserId(toUUID(strUserId));
- User user = checkUserId(userId, Operation.DELETE);
-
- if (user.getAuthority() == Authority.SYS_ADMIN && getCurrentUser().getId().equals(userId)) {
- throw new ThingsboardException("Sysadmin is not allowed to delete himself", ThingsboardErrorCode.PERMISSION_DENIED);
- }
-
- List relatedEdgeIds = findRelatedEdgeIds(getTenantId(), userId);
-
- userService.deleteUser(getCurrentUser().getTenantId(), userId);
-
- logEntityAction(userId, user,
- user.getCustomerId(),
- ActionType.DELETED, null, strUserId);
-
- sendDeleteNotificationMsg(getTenantId(), userId, relatedEdgeIds);
-
- } catch (Exception e) {
- logEntityAction(emptyId(EntityType.USER),
- null,
- null,
- ActionType.DELETED, e, strUserId);
- throw handleException(e);
+ UserId userId = new UserId(toUUID(strUserId));
+ User user = checkUserId(userId, Operation.DELETE);
+ if (user.getAuthority() == Authority.SYS_ADMIN && getCurrentUser().getId().equals(userId)) {
+ throw new ThingsboardException("Sysadmin is not allowed to delete himself", ThingsboardErrorCode.PERMISSION_DENIED);
}
+ tbUserService.delete(getTenantId(), getCurrentUser().getCustomerId(), user, getCurrentUser());
}
@ApiOperation(value = "Get Users (getUsers)",
diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
index 7ec3e2e9aa..4a69b203ed 100644
--- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
+import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
@@ -27,7 +28,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
-import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
@@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.widgetsBundle.TbWidgetsBundleService;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
@@ -57,8 +58,11 @@ import static org.thingsboard.server.controller.ControllerConstants.WIDGET_BUNDL
@RestController
@TbCoreComponent
@RequestMapping("/api")
+@RequiredArgsConstructor
public class WidgetsBundleController extends BaseController {
+ private final TbWidgetsBundleService tbWidgetsBundleService;
+
private static final String WIDGET_BUNDLE_DESCRIPTION = "Widget Bundle represents a group(bundle) of widgets. Widgets are grouped into bundle by type or use case. ";
@ApiOperation(value = "Get Widget Bundle (getWidgetsBundleById)",
@@ -93,7 +97,7 @@ public class WidgetsBundleController extends BaseController {
public WidgetsBundle saveWidgetsBundle(
@ApiParam(value = "A JSON value representing the Widget Bundle.", required = true)
@RequestBody WidgetsBundle widgetsBundle) throws ThingsboardException {
- try {
+
if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) {
widgetsBundle.setTenantId(TenantId.SYS_TENANT_ID);
} else {
@@ -101,15 +105,8 @@ public class WidgetsBundleController extends BaseController {
}
checkEntity(widgetsBundle.getId(), widgetsBundle, Resource.WIDGETS_BUNDLE);
- WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
-
- sendEntityNotificationMsg(getTenantId(), savedWidgetsBundle.getId(),
- widgetsBundle.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
- return checkNotNull(savedWidgetsBundle);
- } catch (Exception e) {
- throw handleException(e);
- }
+ return tbWidgetsBundleService.save(widgetsBundle, getCurrentUser());
}
@ApiOperation(value = "Delete widgets bundle (deleteWidgetsBundle)",
@@ -121,16 +118,9 @@ public class WidgetsBundleController extends BaseController {
@ApiParam(value = WIDGET_BUNDLE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException {
checkParameter("widgetsBundleId", strWidgetsBundleId);
- try {
- WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
- checkWidgetsBundleId(widgetsBundleId, Operation.DELETE);
- widgetsBundleService.deleteWidgetsBundle(getTenantId(), widgetsBundleId);
-
- sendEntityNotificationMsg(getTenantId(), widgetsBundleId, EdgeEventActionType.DELETED);
-
- } catch (Exception e) {
- throw handleException(e);
- }
+ WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
+ WidgetsBundle widgetsBundle = checkWidgetsBundleId(widgetsBundleId, Operation.DELETE);
+ tbWidgetsBundleService.delete(widgetsBundle, getCurrentUser());
}
@ApiOperation(value = "Get Widget Bundles (getWidgetsBundles)",
diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallConfiguration.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallConfiguration.java
index a1f1fcf0e3..4338f4aa44 100644
--- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallConfiguration.java
@@ -19,6 +19,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.thingsboard.server.dao.audit.AuditLogLevelFilter;
+import org.thingsboard.server.dao.audit.AuditLogLevelProperties;
import java.util.HashMap;
@@ -28,6 +29,8 @@ public class ThingsboardInstallConfiguration {
@Bean
public AuditLogLevelFilter emptyAuditLogLevelFilter() {
- return new AuditLogLevelFilter(new HashMap<>());
+ var props = new AuditLogLevelProperties();
+ props.setMask(new HashMap<>());
+ return new AuditLogLevelFilter(props);
}
}
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 ebab403149..40e15c7b0e 100644
--- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
+++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
@@ -219,6 +219,8 @@ public class ThingsboardInstallService {
databaseEntitiesUpgradeService.upgradeDatabase("3.3.3");
case "3.3.4":
log.info("Upgrading ThingsBoard from version 3.3.4 to 3.4.0 ...");
+ databaseEntitiesUpgradeService.upgradeDatabase("3.3.4");
+ dataUpdateService.updateData("3.3.4");
log.info("Updating system data...");
systemDataLoaderService.updateSystemWidgets();
break;
@@ -257,6 +259,7 @@ public class ThingsboardInstallService {
systemDataLoaderService.createAdminSettings();
systemDataLoaderService.loadSystemWidgets();
systemDataLoaderService.createOAuth2Templates();
+ systemDataLoaderService.createQueues();
// systemDataLoaderService.loadSystemPlugins();
// systemDataLoaderService.loadSystemRules();
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java
index 038dcadfc8..fd55e9b321 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java
@@ -76,7 +76,7 @@ public class EdgeBulkImportService extends AbstractBulkImportService {
@Override
protected Edge saveEntity(SecurityUser user, Edge entity, Map fields) {
RuleChain edgeTemplateRootRuleChain = ruleChainService.getEdgeTemplateRootRuleChain(user.getTenantId());
- return tbEdgeService.saveEdge(entity, edgeTemplateRootRuleChain, user);
+ return tbEdgeService.save(entity, edgeTemplateRootRuleChain, user);
}
@Override
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java
index a72f41c887..769d988507 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java
@@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
+import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
@@ -51,7 +52,6 @@ import org.thingsboard.server.common.data.kv.AttributeKey;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
-import org.thingsboard.server.common.msg.queue.ServiceQueue;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.common.transport.util.JsonUtils;
@@ -134,24 +134,23 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor {
return metaData;
}
- private Pair getDefaultQueueNameAndRuleChainId(TenantId tenantId, EntityId entityId) {
+ private Pair getDefaultQueueNameAndRuleChainId(TenantId tenantId, EntityId entityId) {
if (EntityType.DEVICE.equals(entityId.getEntityType())) {
DeviceProfile deviceProfile = deviceProfileCache.get(tenantId, new DeviceId(entityId.getId()));
RuleChainId ruleChainId;
- String queueName;
+ QueueId queueId;
if (deviceProfile == null) {
log.warn("[{}] Device profile is null!", entityId);
ruleChainId = null;
- queueName = ServiceQueue.MAIN;
+ queueId = null;
} else {
ruleChainId = deviceProfile.getDefaultRuleChainId();
- String defaultQueueName = deviceProfile.getDefaultQueueName();
- queueName = defaultQueueName != null ? defaultQueueName : ServiceQueue.MAIN;
+ queueId = deviceProfile.getDefaultQueueId();
}
- return new ImmutablePair<>(queueName, ruleChainId);
+ return new ImmutablePair<>(queueId, ruleChainId);
} else {
- return new ImmutablePair<>(ServiceQueue.MAIN, null);
+ return new ImmutablePair<>(null, null);
}
}
@@ -160,10 +159,10 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor {
for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) {
JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList());
metaData.putValue("ts", tsKv.getTs() + "");
- Pair defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
- String queueName = defaultQueueAndRuleChain.getKey();
+ Pair defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
+ QueueId queueId = defaultQueueAndRuleChain.getKey();
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
- TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
+ TbMsg tbMsg = TbMsg.newMsg(queueId, SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override
public void onSuccess(TbQueueMsgMetadata metadata) {
@@ -183,10 +182,10 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor {
private ListenableFuture processPostAttributes(TenantId tenantId, CustomerId customerId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) {
SettableFuture futureToSet = SettableFuture.create();
JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
- Pair defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
- String queueName = defaultQueueAndRuleChain.getKey();
+ Pair defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
+ QueueId queueId = defaultQueueAndRuleChain.getKey();
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
- TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
+ TbMsg tbMsg = TbMsg.newMsg(queueId, SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override
public void onSuccess(TbQueueMsgMetadata metadata) {
@@ -210,10 +209,10 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor {
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List keys) {
- Pair defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
- String queueName = defaultQueueAndRuleChain.getKey();
+ Pair defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
+ QueueId queueId = defaultQueueAndRuleChain.getKey();
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
- TbMsg tbMsg = TbMsg.newMsg(queueName, DataConstants.ATTRIBUTES_UPDATED, entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
+ TbMsg tbMsg = TbMsg.newMsg(queueId, DataConstants.ATTRIBUTES_UPDATED, entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override
public void onSuccess(TbQueueMsgMetadata metadata) {
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java
index e2ccc1f3d2..395ebdeaac 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java
@@ -15,7 +15,6 @@
*/
package org.thingsboard.server.service.entitiy;
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.Getter;
@@ -42,20 +41,35 @@ import org.thingsboard.server.common.data.page.PageDataIterableByTenantIdEntityI
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetService;
+import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.customer.CustomerService;
+import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.ClaimDevicesService;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
+import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeService;
+import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
+import org.thingsboard.server.dao.ota.OtaPackageService;
+import org.thingsboard.server.dao.queue.QueueService;
+import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.tenant.TenantService;
+import org.thingsboard.server.dao.user.UserService;
+import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.service.action.EntityActionService;
import org.thingsboard.server.service.edge.EdgeNotificationService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
+import org.thingsboard.server.service.install.InstallScripts;
+import org.thingsboard.server.service.ota.OtaPackageStateService;
+import org.thingsboard.server.service.resource.TbResourceService;
+import org.thingsboard.server.service.rule.TbRuleChainService;
+import org.thingsboard.server.service.security.permission.AccessControlService;
+import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import javax.mail.MessagingException;
import java.util.ArrayList;
@@ -69,8 +83,6 @@ public abstract class AbstractTbEntityService {
protected static final int DEFAULT_PAGE_SIZE = 1000;
- private static final ObjectMapper json = new ObjectMapper();
-
@Value("${server.log_controller_error_stack_trace}")
@Getter
private boolean logControllerErrorStackTrace;
@@ -105,7 +117,39 @@ public abstract class AbstractTbEntityService {
@Autowired
protected RuleChainService ruleChainService;
@Autowired
+ protected TbRuleChainService tbRuleChainService;
+ @Autowired
protected EdgeNotificationService edgeNotificationService;
+ @Autowired
+ protected QueueService queueService;
+ @Autowired
+ protected DashboardService dashboardService;
+ @Autowired
+ protected EntityViewService entityViewService;
+ @Autowired
+ protected TelemetrySubscriptionService tsSubService;
+ @Autowired
+ protected AttributesService attributesService;
+ @Autowired
+ protected AccessControlService accessControlService;
+ @Autowired
+ protected DeviceProfileService deviceProfileService;
+ @Autowired
+ protected TbClusterService tbClusterService;
+ @Autowired
+ protected OtaPackageStateService otaPackageStateService;
+ @Autowired
+ protected RelationService relationService;
+ @Autowired
+ protected OtaPackageService otaPackageService;
+ @Autowired
+ protected InstallScripts installScripts;
+ @Autowired
+ protected UserService userService;
+ @Autowired
+ protected TbResourceService resourceService;
+ @Autowired
+ protected WidgetsBundleService widgetsBundleService;
protected ListenableFuture removeAlarmsByEntityId(TenantId tenantId, EntityId entityId) {
ListenableFuture> alarmsFuture =
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java
index c18e175fe2..c7e4f2cd0b 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java
@@ -22,9 +22,9 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
import org.thingsboard.server.cluster.TbClusterService;
-import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.alarm.Alarm;
@@ -36,8 +36,12 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
@@ -73,9 +77,33 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
List relatedEdgeIds,
SecurityUser user, Object... additionalInfo) {
logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo);
- sendDeleteNotificationMsg(tenantId, entityId, entity, relatedEdgeIds);
+ sendDeleteNotificationMsg(tenantId, entityId, entity, relatedEdgeIds);
+ }
+
+ public void notifyDeleteAlarm(TenantId tenantId, Alarm alarm, EntityId originatorId,
+ CustomerId customerId,
+ List relatedEdgeIds,
+ SecurityUser user,
+ String body, Object... additionalInfo) {
+ logEntityAction(tenantId, originatorId, alarm, customerId, ActionType.DELETED, user, additionalInfo);
+ sendAlarmDeleteNotificationMsg(tenantId, alarm, relatedEdgeIds, body);
+ }
+
+ @Override
+ public void notifyDeleteRuleChain(TenantId tenantId, RuleChain ruleChain,
+ List relatedEdgeIds, SecurityUser user) {
+ RuleChainId ruleChainId = ruleChain.getId();
+ logEntityAction(tenantId, ruleChainId, ruleChain, null, ActionType.DELETED, user, null, ruleChainId.toString());
+ if (RuleChainType.EDGE.equals(ruleChain.getType())) {
+ sendDeleteNotificationMsg(tenantId, ruleChainId, relatedEdgeIds, null);
+ }
}
+ @Override
+ public void notifySendMsgToEdgeService(TenantId tenantId, I entityId, EdgeEventActionType edgeEventActionType) {
+ sendEntityNotificationMsg(tenantId, entityId, edgeEventActionType);
+ }
+
@Override
public void notifyAssignOrUnassignEntityToCustomer(TenantId tenantId, I entityId,
CustomerId customerId, E entity,
@@ -94,10 +122,9 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
public void notifyAssignOrUnassignEntityToEdge(TenantId tenantId, I entityId,
CustomerId customerId, EdgeId edgeId,
E entity, ActionType actionType,
- EdgeEventActionType edgeActionType,
SecurityUser user, Object... additionalInfo) {
logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo);
- sendEntityAssignToEdgeNotificationMsg(tenantId, edgeId, entityId, edgeActionType);
+ sendEntityAssignToEdgeNotificationMsg(tenantId, edgeId, entityId, edgeTypeByActionType(actionType));
}
@Override
@@ -126,7 +153,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
gatewayNotificationsService.onDeviceDeleted(device);
tbClusterService.onDeviceDeleted(device, null);
- notifyDeleteEntity(tenantId, deviceId, device, customerId, ActionType.DELETED, relatedEdgeIds, user, false, additionalInfo);
+ notifyDeleteEntity(tenantId, deviceId, device, customerId, ActionType.DELETED, relatedEdgeIds, user, additionalInfo);
}
@Override
@@ -145,9 +172,9 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
}
@Override
- public void notifyCreateOrUpdateEntity(TenantId tenantId, I entityId, E entity, CustomerId customerId, ActionType actionType, SecurityUser user, Object... additionalInfo) {
+ public void notifyCreateOrUpdateEntity(TenantId tenantId, I entityId, E entity, CustomerId customerId, ActionType actionType, SecurityUser user, Object... additionalInfo) {
logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo);
- if (actionType == ActionType.UPDATED) {
+ if (actionType == ActionType.UPDATED) {
sendEntityNotificationMsg(tenantId, entityId, EdgeEventActionType.UPDATED);
}
}
@@ -191,20 +218,38 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
@Override
public void notifyCreateOrUpdateAlarm(Alarm alarm, ActionType actionType, SecurityUser user, Object... additionalInfo) {
logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarm, alarm.getCustomerId(), actionType, user, additionalInfo);
- sendEntityNotificationMsg(alarm.getTenantId(), alarm.getId(), edgeTypeByActionType (actionType));
+ sendEntityNotificationMsg(alarm.getTenantId(), alarm.getId(), edgeTypeByActionType(actionType));
}
@Override
- public void notifyDeleteAlarm(Alarm alarm, SecurityUser user, List relatedEdgeIds) {
- logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarm, alarm.getCustomerId(), ActionType.ALARM_DELETE, user, null);
- sendAlarmDeleteNotificationMsg(alarm, relatedEdgeIds);
+ public void notifyCreateOrUpdateOrDelete(TenantId tenantId, CustomerId customerId,
+ I entityId, E entity, SecurityUser user,
+ ActionType actionType, boolean sendNotifyMsgToEdge, Exception e,
+ Object... additionalInfo) {
+ notifyEntity(tenantId, entityId, entity, customerId, actionType, user, e, additionalInfo);
+ if (sendNotifyMsgToEdge) {
+ sendEntityNotificationMsg(tenantId, entityId, edgeTypeByActionType(actionType));
+ }
}
@Override
- public void notifyDeleteCustomer(Customer customer, SecurityUser user, List edgeIds) {
- logEntityAction(customer.getTenantId(), customer.getId(), customer, customer.getId(), ActionType.DELETED, user, null);
- sendDeleteNotificationMsg(customer.getTenantId(), customer.getId(), customer, edgeIds);
- tbClusterService.broadcastEntityStateChangeEvent(customer.getTenantId(), customer.getId(), ComponentLifecycleEvent.DELETED);
+ public void notifyCreateOrUpdateOrDeleteRelation(TenantId tenantId, CustomerId customerId,
+ EntityRelation relation, SecurityUser user,
+ ActionType actionType, Exception e,
+ Object... additionalInfo) {
+ notifyEntity(tenantId, relation.getFrom(), null, customerId, actionType, user, e, additionalInfo);
+ notifyEntity(tenantId, relation.getTo(), null, customerId, actionType, user, e, additionalInfo);
+ if (e == null) {
+ try {
+ if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
+ !relation.getTo().getEntityType().equals(EntityType.EDGE)) {
+ sendNotificationMsgToEdgeService(tenantId, null, null, json.writeValueAsString(relation),
+ EdgeEventType.RELATION, edgeTypeByActionType(actionType));
+ }
+ } catch (Exception e1) {
+ log.warn("Failed to push relation to core: {}", relation, e1);
+ }
+ }
}
private void logEntityAction(TenantId tenantId, I entityId, E entity, CustomerId customerId,
@@ -233,19 +278,20 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
}
}
- protected void sendDeleteNotificationMsg(TenantId tenantId, I entityId, E entity, List edgeIds) {
+ protected void sendAlarmDeleteNotificationMsg(TenantId tenantId, Alarm alarm, List edgeIds, String body) {
try {
- sendDeleteNotificationMsg(tenantId, entityId, edgeIds, null);
+ sendDeleteNotificationMsg(tenantId, alarm.getId(), edgeIds, body);
} catch (Exception e) {
- log.warn("Failed to push delete " + entity.getClass().getName() + " msg to core: {}", entity, e);
+ log.warn("Failed to push delete msg to core: {}", alarm, e);
}
}
- protected void sendAlarmDeleteNotificationMsg(Alarm alarm, List relatedEdgeIds) {
+ protected void sendDeleteNotificationMsg(TenantId tenantId, I entityId, E entity,
+ List edgeIds) {
try {
- sendDeleteNotificationMsg(alarm.getTenantId(), alarm.getId(), relatedEdgeIds, json.writeValueAsString(alarm));
+ sendDeleteNotificationMsg(tenantId, entityId, edgeIds, null);
} catch (Exception e) {
- log.warn("Failed to push delete alarm msg to core: {}", alarm, e);
+ log.warn("Failed to push delete msg to core: {}", entity, e);
}
}
@@ -289,7 +335,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
return null;
}
- private EdgeEventActionType edgeTypeByActionType (ActionType actionType) {
+ public static EdgeEventActionType edgeTypeByActionType(ActionType actionType) {
switch (actionType) {
case ADDED:
return EdgeEventActionType.ADDED;
@@ -301,9 +347,16 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
return EdgeEventActionType.ALARM_CLEAR;
case DELETED:
return EdgeEventActionType.DELETED;
+ case RELATION_ADD_OR_UPDATE:
+ return EdgeEventActionType.RELATION_ADD_OR_UPDATE;
+ case RELATION_DELETED:
+ return EdgeEventActionType.RELATION_DELETED;
+ case ASSIGNED_TO_EDGE:
+ return EdgeEventActionType.ASSIGNED_TO_EDGE;
+ case UNASSIGNED_FROM_EDGE:
+ return EdgeEventActionType.UNASSIGNED_FROM_EDGE;
default:
return null;
}
}
-
}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/SimpleTbEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/SimpleTbEntityService.java
index 84f5658015..ce22e8d787 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/SimpleTbEntityService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/SimpleTbEntityService.java
@@ -20,6 +20,8 @@ import org.thingsboard.server.service.security.model.SecurityUser;
public interface SimpleTbEntityService {
- T save(T entity, SecurityUser user) throws ThingsboardException;
+ T save(T entity, SecurityUser user) throws ThingsboardException;
+
+ void delete (T entity, SecurityUser user) throws ThingsboardException;
}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java
index 43c0cc4c91..c1131761dc 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java
@@ -15,7 +15,6 @@
*/
package org.thingsboard.server.service.entitiy;
-import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.Tenant;
@@ -29,6 +28,8 @@ import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.service.security.model.SecurityUser;
@@ -44,9 +45,19 @@ public interface TbNotificationEntityService {
CustomerId customerId, ActionType actionType,
SecurityUser user, Object... additionalInfo);
- void notifyDeleteEntity(TenantId tenantId, I entityId, E entity, CustomerId customerId,
- ActionType actionType, List relatedEdgeIds, SecurityUser user,
- Object... additionalInfo);
+ void notifyDeleteEntity(TenantId tenantId, I entityId, E entity,
+ CustomerId customerId, ActionType actionType,
+ List relatedEdgeIds,
+ SecurityUser user, Object... additionalInfo);
+
+ void notifyDeleteAlarm(TenantId tenantId, Alarm alarm, EntityId originatorId,
+ CustomerId customerId, List relatedEdgeIds,
+ SecurityUser user, String body, Object... additionalInfo);
+
+ void notifyDeleteRuleChain(TenantId tenantId, RuleChain ruleChain,
+ List relatedEdgeIds, SecurityUser user);
+
+ void notifySendMsgToEdgeService(TenantId tenantId, I entityId, EdgeEventActionType edgeEventActionType);
void notifyAssignOrUnassignEntityToCustomer(TenantId tenantId, I entityId,
CustomerId customerId, E entity,
@@ -58,11 +69,11 @@ public interface TbNotificationEntityService {
void notifyAssignOrUnassignEntityToEdge(TenantId tenantId, I entityId,
CustomerId customerId, EdgeId edgeId,
E entity, ActionType actionType,
- EdgeEventActionType edgeActionType,
SecurityUser user, Object... additionalInfo);
void notifyCreateOruUpdateTenant(Tenant tenant, ComponentLifecycleEvent event);
+
void notifyDeleteTenant(Tenant tenant);
void notifyCreateOrUpdateDevice(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device,
@@ -77,11 +88,18 @@ public interface TbNotificationEntityService {
void notifyAssignDeviceToTenant(TenantId tenantId, TenantId newTenantId, DeviceId deviceId, CustomerId customerId,
Device device, Tenant tenant, SecurityUser user, Object... additionalInfo);
- void notifyEdge(TenantId tenantId, EdgeId edgeId, CustomerId customerId, Edge edge, ActionType actionType, SecurityUser user, Object... additionalInfo);
+ void notifyEdge(TenantId tenantId, EdgeId edgeId, CustomerId customerId, Edge edge, ActionType actionType,
+ SecurityUser user, Object... additionalInfo);
void notifyCreateOrUpdateAlarm(Alarm alarm, ActionType actionType, SecurityUser user, Object... additionalInfo);
- void notifyDeleteAlarm(Alarm alarm, SecurityUser user, List relatedEdgeIds);
+ void notifyCreateOrUpdateOrDelete(TenantId tenantId, CustomerId customerId,
+ I entityId, E entity, SecurityUser user,
+ ActionType actionType, boolean sendNotifyMsgToEdge, Exception e,
+ Object... additionalInfo);
- void notifyDeleteCustomer(Customer customer, SecurityUser user, List relatedEdgeIds);
+ void notifyCreateOrUpdateOrDeleteRelation(TenantId tenantId, CustomerId customerId,
+ EntityRelation relation, SecurityUser user,
+ ActionType actionType, Exception e,
+ Object... additionalInfo);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java
index 1fb19e3c41..4930f54a5d 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.entitiy.alarm;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
+import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
@@ -77,8 +78,13 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
@Override
public Boolean delete(Alarm alarm, SecurityUser user) throws ThingsboardException {
- List relatedEdgeIds = findRelatedEdgeIds(alarm.getTenantId(), alarm.getOriginator());
- notificationEntityService.notifyDeleteAlarm(alarm, user, relatedEdgeIds);
- return alarmService.deleteAlarm(alarm.getTenantId(), alarm.getId()).isSuccessful();
+ try {
+ List relatedEdgeIds = findRelatedEdgeIds(user.getTenantId(), alarm.getOriginator());
+ notificationEntityService.notifyDeleteAlarm(user.getTenantId(), alarm, alarm.getOriginator(), user.getCustomerId(),
+ relatedEdgeIds, user, JacksonUtil.OBJECT_MAPPER.writeValueAsString(alarm));
+ return alarmService.deleteAlarm(user.getTenantId(), alarm.getId()).isSuccessful();
+ } catch (Exception e) {
+ throw handleException(e);
+ }
}
-}
+}
\ No newline at end of file
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java
index f73e44bc69..7a6d7f6a81 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java
@@ -16,12 +16,12 @@
package org.thingsboard.server.service.entitiy.alarm;
import org.thingsboard.server.common.data.alarm.Alarm;
-import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.exception.ThingsboardException;
-import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
import org.thingsboard.server.service.security.model.SecurityUser;
-public interface TbAlarmService extends SimpleTbEntityService {
+public interface TbAlarmService {
+
+ Alarm save(Alarm entity, SecurityUser user) throws ThingsboardException;
void ack(Alarm alarm, SecurityUser user) throws ThingsboardException;
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java
index 3f4ce83318..492464f147 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java
@@ -39,6 +39,7 @@ import java.util.List;
@TbCoreComponent
@AllArgsConstructor
public class DefaultTbAssetService extends AbstractTbEntityService implements TbAssetService {
+
@Override
public Asset save(Asset asset, SecurityUser user) throws ThingsboardException {
ActionType actionType = asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
@@ -60,12 +61,13 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb
try {
List relatedEdgeIds = findRelatedEdgeIds(tenantId, assetId);
assetService.deleteAsset(tenantId, assetId);
- notificationEntityService.notifyDeleteEntity(tenantId, assetId, asset, asset.getCustomerId(), ActionType.DELETED, relatedEdgeIds, user, false, asset.toString());
+ notificationEntityService.notifyDeleteEntity(tenantId, assetId, asset, asset.getCustomerId(), ActionType.DELETED,
+ relatedEdgeIds, user, assetId.toString());
return removeAlarmsByEntityId(tenantId, assetId);
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.ASSET), null, null,
- ActionType.DELETED, user, e, asset.toString());
+ ActionType.DELETED, user, e, assetId.toString());
throw handleException(e);
}
}
@@ -133,7 +135,7 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb
try {
Asset savedAsset = checkNotNull(assetService.assignAssetToEdge(tenantId, assetId, edgeId));
notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, assetId, savedAsset.getCustomerId(),
- edgeId, savedAsset, actionType, EdgeEventActionType.ASSIGNED_TO_EDGE, user, assetId.toString(), edgeId.toString(), edge.getName());
+ edgeId, savedAsset, actionType, user, assetId.toString(), edgeId.toString(), edge.getName());
return savedAsset;
} catch (Exception e) {
@@ -152,7 +154,7 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb
Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(tenantId, assetId, edgeId));
notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, assetId, asset.getCustomerId(),
- edgeId, asset, actionType, EdgeEventActionType.UNASSIGNED_FROM_EDGE, user, assetId.toString(), edgeId.toString(), edge.getName());
+ edgeId, asset, actionType, user, assetId.toString(), edgeId.toString(), edge.getName());
return savedAsset;
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.ASSET), null, null,
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/TbAssetService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/TbAssetService.java
index 3bc6dadf3d..3ffb03cb7b 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/TbAssetService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/TbAssetService.java
@@ -22,10 +22,11 @@ import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
import org.thingsboard.server.service.security.model.SecurityUser;
-public interface TbAssetService extends SimpleTbEntityService {
+public interface TbAssetService {
+
+ Asset save(Asset asset, SecurityUser user) throws ThingsboardException;
ListenableFuture delete(Asset asset, SecurityUser user) throws ThingsboardException;
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java
index 452c71b51a..d35cb06a85 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java
@@ -21,8 +21,10 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import org.thingsboard.server.service.security.model.SecurityUser;
@@ -38,9 +40,10 @@ public class DefaultTbCustomerService extends AbstractTbEntityService implements
public Customer save(Customer customer, SecurityUser user) throws ThingsboardException {
ActionType actionType = customer.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
TenantId tenantId = customer.getTenantId();
+ CustomerId customerId = customer.getId();
try {
Customer savedCustomer = checkNotNull(customerService.saveCustomer(customer));
- notificationEntityService.notifyCreateOrUpdateEntity(tenantId, savedCustomer.getId(), savedCustomer, savedCustomer.getId(), actionType, user);
+ notificationEntityService.notifyCreateOrUpdateEntity(tenantId, savedCustomer.getId(), savedCustomer, customerId, actionType, user);
return savedCustomer;
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.CUSTOMER), customer, null, actionType, user, e);
@@ -48,15 +51,20 @@ public class DefaultTbCustomerService extends AbstractTbEntityService implements
}
}
+
@Override
public void delete(Customer customer, SecurityUser user) throws ThingsboardException {
TenantId tenantId = customer.getTenantId();
+ CustomerId customerId = customer.getId();
try {
List relatedEdgeIds = findRelatedEdgeIds(tenantId, customer.getId());
- customerService.deleteCustomer(tenantId, customer.getId());
- notificationEntityService.notifyDeleteCustomer(customer, user, relatedEdgeIds);
+ customerService.deleteCustomer(tenantId, customerId);
+ notificationEntityService.notifyDeleteEntity(tenantId, customer.getId(), customer, customerId,
+ ActionType.DELETED, relatedEdgeIds, user, customerId.toString());
+ tbClusterService.broadcastEntityStateChangeEvent(tenantId, customer.getId(), ComponentLifecycleEvent.DELETED);
} catch (Exception e) {
- notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.CUSTOMER), null, null, ActionType.DELETED, user, e);
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.CUSTOMER), null, null,
+ ActionType.DELETED, user, e, customer.getId().toString());
throw handleException(e);
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/customer/TbCustomerService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/customer/TbCustomerService.java
index f34d796db1..870de10efc 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/customer/TbCustomerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/customer/TbCustomerService.java
@@ -16,12 +16,8 @@
package org.thingsboard.server.service.entitiy.customer;
import org.thingsboard.server.common.data.Customer;
-import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
-import org.thingsboard.server.service.security.model.SecurityUser;
public interface TbCustomerService extends SimpleTbEntityService {
- void delete(Customer customer, SecurityUser user) throws ThingsboardException;
-
}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java
new file mode 100644
index 0000000000..f7110c4da7
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java
@@ -0,0 +1,275 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.dashboard;
+
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.Dashboard;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.ShortCustomerInfo;
+import org.thingsboard.server.common.data.audit.ActionType;
+import org.thingsboard.server.common.data.edge.Edge;
+import org.thingsboard.server.common.data.edge.EdgeEventActionType;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.DashboardId;
+import org.thingsboard.server.common.data.id.EdgeId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@Service
+@TbCoreComponent
+@AllArgsConstructor
+public class DefaultTbDashboardService extends AbstractTbEntityService implements TbDashboardService {
+
+ @Override
+ public Dashboard save(Dashboard dashboard, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
+ TenantId tenantId = dashboard.getTenantId();
+ try {
+ Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard));
+ notificationEntityService.notifyCreateOrUpdateEntity(tenantId, savedDashboard.getId(), savedDashboard,
+ null, actionType, user);
+ return savedDashboard;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DASHBOARD), dashboard, null, actionType, user, e);
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void delete(Dashboard dashboard, SecurityUser user) throws ThingsboardException {
+ DashboardId dashboardId = dashboard.getId();
+ TenantId tenantId = dashboard.getTenantId();
+ try {
+ List relatedEdgeIds = findRelatedEdgeIds(tenantId, dashboardId);
+ dashboardService.deleteDashboard(tenantId, dashboardId);
+ notificationEntityService.notifyDeleteEntity(tenantId, dashboardId, dashboard, null,
+ ActionType.DELETED, relatedEdgeIds, user, dashboardId.toString());
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DASHBOARD), null, null,
+ ActionType.DELETED, user, e, dashboardId.toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public Dashboard assignDashboardToCustomer(DashboardId dashboardId, Customer customer, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = ActionType.ASSIGNED_TO_CUSTOMER;
+ CustomerId customerId = customer.getId();
+ try {
+ Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(user.getTenantId(), dashboardId, customerId));
+ notificationEntityService.notifyAssignOrUnassignEntityToCustomer(user.getTenantId(), dashboardId, customerId, savedDashboard,
+ actionType, EdgeEventActionType.ASSIGNED_TO_CUSTOMER, user, true, customerId.toString(), customer.getName());
+ return savedDashboard;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(user.getTenantId(), emptyId(EntityType.DASHBOARD), null, null,
+ actionType, user, e, dashboardId.toString(), customerId.toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public Dashboard assignDashboardToPublicCustomer(DashboardId dashboardId, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = ActionType.ASSIGNED_TO_CUSTOMER;
+ try {
+ Customer publicCustomer = customerService.findOrCreatePublicCustomer(user.getTenantId());
+ Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(user.getTenantId(), dashboardId, publicCustomer.getId()));
+ notificationEntityService.notifyAssignOrUnassignEntityToCustomer(user.getTenantId(), dashboardId, user.getCustomerId(), savedDashboard,
+ actionType, null, user, false, dashboardId.toString(),
+ publicCustomer.getId().toString(), publicCustomer.getName());
+ return savedDashboard;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(user.getTenantId(), emptyId(EntityType.DASHBOARD), null, null,
+ actionType, user, e, dashboardId.toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public Dashboard unassignDashboardFromPublicCustomer(Dashboard dashboard, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = ActionType.UNASSIGNED_FROM_CUSTOMER;
+ try {
+ Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId());
+ Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(user.getTenantId(), dashboard.getId(), publicCustomer.getId()));
+ notificationEntityService.notifyAssignOrUnassignEntityToCustomer(user.getTenantId(), dashboard.getId(), user.getCustomerId(), dashboard,
+ actionType, null, user, false, dashboard.getId().toString(),
+ publicCustomer.getId().toString(), publicCustomer.getName());
+ return savedDashboard;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(user.getTenantId(), emptyId(EntityType.DASHBOARD), null, null,
+ actionType, user, e, dashboard.getId().toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public Dashboard updateDashboardCustomers(Dashboard dashboard, Set customerIds, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = ActionType.ASSIGNED_TO_CUSTOMER;
+ TenantId tenantId = user.getTenantId();
+ try {
+ Set addedCustomerIds = new HashSet<>();
+ Set removedCustomerIds = new HashSet<>();
+ for (CustomerId customerId : customerIds) {
+ if (!dashboard.isAssignedToCustomer(customerId)) {
+ addedCustomerIds.add(customerId);
+ }
+ }
+
+ Set assignedCustomers = dashboard.getAssignedCustomers();
+ if (assignedCustomers != null) {
+ for (ShortCustomerInfo customerInfo : assignedCustomers) {
+ if (!customerIds.contains(customerInfo.getCustomerId())) {
+ removedCustomerIds.add(customerInfo.getCustomerId());
+ }
+ }
+ }
+
+ if (addedCustomerIds.isEmpty() && removedCustomerIds.isEmpty()) {
+ return dashboard;
+ } else {
+ Dashboard savedDashboard = null;
+ for (CustomerId customerId : addedCustomerIds) {
+ savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboard.getId(), customerId));
+ ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
+ notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, savedDashboard.getId(), customerId, savedDashboard,
+ actionType, EdgeEventActionType.ASSIGNED_TO_CUSTOMER, user, true, customerInfo.getTitle());
+ }
+ for (CustomerId customerId : removedCustomerIds) {
+ ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
+ savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboard.getId(), customerId));
+ notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, savedDashboard.getId(), customerId, savedDashboard,
+ ActionType.UNASSIGNED_FROM_CUSTOMER, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER, user, true, customerInfo.getTitle());
+ }
+ return savedDashboard;
+ }
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DASHBOARD), null, null,
+ actionType, user, e, dashboard.getId().toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public Dashboard addDashboardCustomers(Dashboard dashboard, Set customerIds, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = ActionType.ASSIGNED_TO_CUSTOMER;
+ TenantId tenantId = user.getTenantId();
+ try {
+ if (customerIds.isEmpty()) {
+ return dashboard;
+ } else {
+ Dashboard savedDashboard = null;
+ for (CustomerId customerId : customerIds) {
+ savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboard.getId(), customerId));
+ ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
+ notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, savedDashboard.getId(), customerId, savedDashboard,
+ actionType, EdgeEventActionType.ASSIGNED_TO_CUSTOMER, user, true, customerInfo.getTitle());
+ }
+ return savedDashboard;
+ }
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DASHBOARD), null, null,
+ actionType, user, e, dashboard.getId().toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public Dashboard removeDashboardCustomers(Dashboard dashboard, Set customerIds, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = ActionType.UNASSIGNED_FROM_CUSTOMER;
+ TenantId tenantId = user.getTenantId();
+ try {
+ if (customerIds.isEmpty()) {
+ return dashboard;
+ } else {
+ Dashboard savedDashboard = null;
+ for (CustomerId customerId : customerIds) {
+ ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
+ savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboard.getId(), customerId));
+ notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, savedDashboard.getId(), customerId, savedDashboard,
+ actionType, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER, user, true, customerInfo.getTitle());
+ }
+ return savedDashboard;
+ }
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DASHBOARD), null, null,
+ actionType, user, e, dashboard.getId().toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public Dashboard asignDashboardToEdge(DashboardId dashboardId, Edge edge, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = ActionType.ASSIGNED_TO_EDGE;
+ TenantId tenantId = user.getTenantId();
+ EdgeId edgeId = edge.getId();
+ try {
+ Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(tenantId, dashboardId, edgeId));
+ notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, dashboardId, user.getCustomerId(),
+ edgeId, savedDashboard, actionType, user, dashboardId.toString(),
+ edgeId.toString(), edge.getName());
+ return savedDashboard;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DEVICE), null, null,
+ actionType, user, e, dashboardId.toString(), edgeId.toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public Dashboard unassignDashboardFromEdge(Dashboard dashboard, Edge edge, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = ActionType.UNASSIGNED_FROM_EDGE;
+ TenantId tenantId = dashboard.getTenantId();
+ DashboardId dashboardId = dashboard.getId();
+ EdgeId edgeId = edge.getId();
+ try {
+ Dashboard savedDevice = checkNotNull(dashboardService.unassignDashboardFromEdge(tenantId, dashboardId, edgeId));
+
+ notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, dashboardId, user.getCustomerId(),
+ edgeId, dashboard, actionType, user, dashboardId.toString(),
+ edgeId.toString(), edge.getName());
+ return savedDevice;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DASHBOARD), null, null,
+ actionType, user, e, dashboardId.toString(), edgeId.toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public Dashboard unassignDashboardFromCustomer(Dashboard dashboard, Customer customer, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = ActionType.UNASSIGNED_FROM_CUSTOMER;
+ TenantId tenantId = dashboard.getTenantId();
+ try {
+ Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboard.getId(), customer.getId()));
+ notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboard.getId(), customer.getId(), savedDashboard,
+ actionType, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER, user, true, customer.getId().toString(), customer.getName());
+ return savedDashboard;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DASHBOARD), null, null,
+ actionType, user, e, dashboard.getId().toString());
+ throw handleException(e);
+ }
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/TbDashboardService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/TbDashboardService.java
new file mode 100644
index 0000000000..84dba247ff
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/TbDashboardService.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.dashboard;
+
+import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.Dashboard;
+import org.thingsboard.server.common.data.edge.Edge;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.DashboardId;
+import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+import java.util.Set;
+
+public interface TbDashboardService extends SimpleTbEntityService {
+
+ Dashboard assignDashboardToCustomer(DashboardId dashboardId, Customer customer, SecurityUser user) throws ThingsboardException;
+
+ Dashboard assignDashboardToPublicCustomer(DashboardId dashboardId, SecurityUser user) throws ThingsboardException;
+
+ Dashboard unassignDashboardFromPublicCustomer(Dashboard dashboard, SecurityUser user) throws ThingsboardException;
+
+ Dashboard updateDashboardCustomers(Dashboard dashboard, Set customerIds, SecurityUser user) throws ThingsboardException;
+
+ Dashboard addDashboardCustomers(Dashboard dashboard, Set customerIds, SecurityUser user) throws ThingsboardException;
+
+ Dashboard removeDashboardCustomers(Dashboard dashboard, Set customerIds, SecurityUser user) throws ThingsboardException;
+
+ Dashboard asignDashboardToEdge(DashboardId dashboardId, Edge edge, SecurityUser user) throws ThingsboardException;
+
+ Dashboard unassignDashboardFromEdge(Dashboard dashboard, Edge edge, SecurityUser user) throws ThingsboardException;
+
+ Dashboard unassignDashboardFromCustomer(Dashboard dashboard, Customer customer, SecurityUser user) throws ThingsboardException;
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java
index 1bed17f941..d74610a462 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java
@@ -80,7 +80,7 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T
}
@Override
- public ListenableFuture deleteDevice(Device device, SecurityUser user) throws ThingsboardException {
+ public ListenableFuture delete(Device device, SecurityUser user) throws ThingsboardException {
TenantId tenantId = device.getTenantId();
DeviceId deviceId = device.getId();
try {
@@ -248,7 +248,7 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T
try {
Device savedDevice = checkNotNull(deviceService.assignDeviceToEdge(tenantId, deviceId, edgeId));
notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, deviceId, savedDevice.getCustomerId(),
- edgeId, savedDevice, actionType, EdgeEventActionType.ASSIGNED_TO_EDGE, user, deviceId.toString(), edgeId.toString(), edge.getName());
+ edgeId, savedDevice, actionType, user, deviceId.toString(), edgeId.toString(), edge.getName());
return savedDevice;
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DEVICE), null, null,
@@ -267,7 +267,7 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T
Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(tenantId, deviceId, edgeId));
notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, deviceId, device.getCustomerId(),
- edgeId, device, actionType, EdgeEventActionType.UNASSIGNED_FROM_EDGE, user, deviceId.toString(), edgeId.toString(), edge.getName());
+ edgeId, device, actionType, user, deviceId.toString(), edgeId.toString(), edge.getName());
return savedDevice;
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DEVICE), null, null,
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/device/TbDeviceService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/device/TbDeviceService.java
index 16fd0c7448..74e0bcfbe0 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/device/TbDeviceService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/device/TbDeviceService.java
@@ -35,7 +35,7 @@ public interface TbDeviceService {
Device saveDeviceWithCredentials(TenantId tenantId, Device device, DeviceCredentials deviceCredentials, SecurityUser user) throws ThingsboardException;
- ListenableFuture deleteDevice(Device device, SecurityUser user) throws ThingsboardException;
+ ListenableFuture delete(Device device, SecurityUser user) throws ThingsboardException;
Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, Customer customer, SecurityUser user) throws ThingsboardException;
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/deviceProfile/DefaultTbDeviceProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/deviceProfile/DefaultTbDeviceProfileService.java
new file mode 100644
index 0000000000..ca73b0cb83
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/deviceProfile/DefaultTbDeviceProfileService.java
@@ -0,0 +1,111 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.deviceProfile;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.audit.ActionType;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.DeviceProfileId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+import java.util.Objects;
+
+@Service
+@TbCoreComponent
+@AllArgsConstructor
+@Slf4j
+public class DefaultTbDeviceProfileService extends AbstractTbEntityService implements TbDeviceProfileService {
+ @Override
+ public DeviceProfile save(DeviceProfile deviceProfile, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = deviceProfile.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
+ TenantId tenantId = deviceProfile.getTenantId();
+ try {
+ boolean isFirmwareChanged = false;
+ boolean isSoftwareChanged = false;
+
+ if (actionType.equals(ActionType.UPDATED)) {
+ DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfile.getId());
+ if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) {
+ isFirmwareChanged = true;
+ }
+ if (!Objects.equals(deviceProfile.getSoftwareId(), oldDeviceProfile.getSoftwareId())) {
+ isSoftwareChanged = true;
+ }
+ }
+ DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile));
+
+ tbClusterService.onDeviceProfileChange(savedDeviceProfile, null);
+ tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedDeviceProfile.getId(),
+ actionType.equals(ActionType.ADDED) ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
+
+ otaPackageStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged);
+
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedDeviceProfile.getId(), savedDeviceProfile, user, actionType, true, null);
+ return savedDeviceProfile;
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.DEVICE_PROFILE), deviceProfile, user, actionType, false, e);
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void delete(DeviceProfile deviceProfile, SecurityUser user) throws ThingsboardException {
+ DeviceProfileId deviceProfileId = deviceProfile.getId();
+ TenantId tenantId = deviceProfile.getTenantId();
+ try {
+ deviceProfileService.deleteDeviceProfile(tenantId, deviceProfileId);
+
+ tbClusterService.onDeviceProfileDelete(deviceProfile, null);
+ tbClusterService.broadcastEntityStateChangeEvent(tenantId, deviceProfileId, ComponentLifecycleEvent.DELETED);
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, deviceProfileId, deviceProfile, user, ActionType.DELETED, true, null, deviceProfileId.toString());
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.DEVICE_PROFILE), null, user, ActionType.DELETED, false, e, deviceProfileId.toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public DeviceProfile setDefaultDeviceProfile(DeviceProfile deviceProfile, DeviceProfile previousDefaultDeviceProfile, SecurityUser user) throws ThingsboardException {
+ TenantId tenantId = deviceProfile.getTenantId();
+ try {
+
+ if (deviceProfileService.setDefaultDeviceProfile(tenantId, deviceProfile.getId())) {
+ if (previousDefaultDeviceProfile != null) {
+ previousDefaultDeviceProfile = deviceProfileService.findDeviceProfileById(tenantId, previousDefaultDeviceProfile.getId());
+ notificationEntityService.notifyEntity(tenantId, previousDefaultDeviceProfile.getId(), previousDefaultDeviceProfile, null,
+ ActionType.UPDATED, user, null);
+ }
+ deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfile.getId());
+
+ notificationEntityService.notifyEntity(tenantId, deviceProfile.getId(), deviceProfile, null,
+ ActionType.UPDATED, user, null);
+ }
+ return deviceProfile;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DEVICE_PROFILE), null, null,
+ ActionType.UPDATED, user, e, deviceProfile.getId().toString());
+ throw handleException(e);
+ }
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/deviceProfile/TbDeviceProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/deviceProfile/TbDeviceProfileService.java
new file mode 100644
index 0000000000..28b07e7d13
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/deviceProfile/TbDeviceProfileService.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.deviceProfile;
+
+import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+public interface TbDeviceProfileService extends SimpleTbEntityService {
+
+ DeviceProfile setDefaultDeviceProfile(DeviceProfile deviceProfile, DeviceProfile previousDefaultDeviceProfile, SecurityUser user) throws ThingsboardException;
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java
index 93487ac5cd..dca6869cb9 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java
@@ -39,7 +39,7 @@ import org.thingsboard.server.service.security.model.SecurityUser;
public class DefaultTbEdgeService extends AbstractTbEntityService implements TbEdgeService {
@Override
- public Edge saveEdge(Edge edge, RuleChain edgeTemplateRootRuleChain, SecurityUser user) throws ThingsboardException {
+ public Edge save(Edge edge, RuleChain edgeTemplateRootRuleChain, SecurityUser user) throws ThingsboardException {
ActionType actionType = edge.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
TenantId tenantId = edge.getTenantId();
try {
@@ -62,16 +62,16 @@ public class DefaultTbEdgeService extends AbstractTbEntityService implements TbE
}
@Override
- public void deleteEdge(Edge edge, SecurityUser user) throws ThingsboardException {
+ public void delete(Edge edge, SecurityUser user) throws ThingsboardException {
ActionType actionType = ActionType.DELETED;
EdgeId edgeId = edge.getId();
TenantId tenantId = edge.getTenantId();
try {
edgeService.deleteEdge(tenantId, edgeId);
notificationEntityService.notifyEdge(tenantId, edgeId, edge.getCustomerId(), edge, actionType, user, edgeId.toString());
-
} catch (Exception e) {
- notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.EDGE), edge, null, actionType, user, e);
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.EDGE), edge, null, actionType,
+ user, e, edgeId.toString());
throw handleException(e);
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/edge/TbEdgeService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/edge/TbEdgeService.java
index 5ed27fbbb3..3b4fc28283 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/edge/TbEdgeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/edge/TbEdgeService.java
@@ -25,9 +25,9 @@ import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.service.security.model.SecurityUser;
public interface TbEdgeService {
- Edge saveEdge(Edge edge, RuleChain edgeTemplateRootRuleChain, SecurityUser user) throws ThingsboardException;
+ Edge save(Edge edge, RuleChain edgeTemplateRootRuleChain, SecurityUser user) throws ThingsboardException;
- void deleteEdge(Edge edge, SecurityUser user) throws ThingsboardException;
+ void delete(Edge edge, SecurityUser user) throws ThingsboardException;
Edge assignEdgeToCustomer(TenantId tenantId, EdgeId edgeId, Customer customer, SecurityUser user) throws ThingsboardException;
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entityRelation/DefaultTbEntityRelationService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entityRelation/DefaultTbEntityRelationService.java
new file mode 100644
index 0000000000..ea895683a1
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entityRelation/DefaultTbEntityRelationService.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.entityRelation;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+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.CustomerId;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+@Service
+@TbCoreComponent
+@AllArgsConstructor
+@Slf4j
+public class DefaultTbEntityRelationService extends AbstractTbEntityService implements TbEntityRelationService {
+ @Override
+ public void save(TenantId tenantId, CustomerId customerId, EntityRelation relation, SecurityUser user) throws ThingsboardException {
+ try {
+ relationService.saveRelation(tenantId, relation);
+ notificationEntityService.notifyCreateOrUpdateOrDeleteRelation (tenantId, customerId,
+ relation, user, ActionType.RELATION_ADD_OR_UPDATE, null, relation);
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDeleteRelation (tenantId, customerId,
+ relation, user, ActionType.RELATION_ADD_OR_UPDATE, e, relation);
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void delete(TenantId tenantId, CustomerId customerId, EntityRelation relation, SecurityUser user) throws ThingsboardException {
+ try {
+ Boolean found = relationService.deleteRelation(tenantId, relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup());
+ if (!found) {
+ throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND);
+ }
+ notificationEntityService.notifyCreateOrUpdateOrDeleteRelation (tenantId, customerId,
+ relation, user, ActionType.RELATION_DELETED, null, relation);
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDeleteRelation (tenantId, customerId,
+ relation, user, ActionType.RELATION_DELETED, e, relation);
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void deleteRelations(TenantId tenantId, CustomerId customerId, EntityId entityId, SecurityUser user) throws ThingsboardException {
+ try {
+ relationService.deleteEntityRelations(tenantId, entityId);
+ notificationEntityService.notifyEntity(tenantId, entityId, null, customerId, ActionType.RELATIONS_DELETED, user, null);
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, entityId, null, customerId, ActionType.RELATIONS_DELETED, user, e);
+ throw handleException(e);
+ }
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entityRelation/TbEntityRelationService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entityRelation/TbEntityRelationService.java
new file mode 100644
index 0000000000..52ad888dc8
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entityRelation/TbEntityRelationService.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.entityRelation;
+
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+public interface TbEntityRelationService {
+
+ void save(TenantId tenantId, CustomerId customerId, EntityRelation entity, SecurityUser user) throws ThingsboardException;
+
+ void delete (TenantId tenantId, CustomerId customerId, EntityRelation entity, SecurityUser user) throws ThingsboardException;
+
+ void deleteRelations (TenantId tenantId, CustomerId customerId, EntityId entityId, SecurityUser user) throws ThingsboardException;
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entityView/DefaultTbEntityViewService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entityView/DefaultTbEntityViewService.java
new file mode 100644
index 0000000000..878c6d32bb
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entityView/DefaultTbEntityViewService.java
@@ -0,0 +1,388 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.entityView;
+
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.SettableFuture;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.DataConstants;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.EntityView;
+import org.thingsboard.server.common.data.audit.ActionType;
+import org.thingsboard.server.common.data.edge.Edge;
+import org.thingsboard.server.common.data.edge.EdgeEventActionType;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.EdgeId;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.EntityViewId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.kv.AttributeKvEntry;
+import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
+import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
+import org.thingsboard.server.common.data.kv.TsKvEntry;
+import org.thingsboard.server.dao.timeseries.TimeseriesService;
+import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+
+import static org.apache.commons.lang3.StringUtils.isBlank;
+
+@Service
+@TbCoreComponent
+@AllArgsConstructor
+@Slf4j
+public class DefaultTbEntityViewService extends AbstractTbEntityService implements TbEntityViewService {
+
+ private final TimeseriesService tsService;
+
+ @Override
+ public EntityView save(EntityView entityView, EntityView existingEntityView, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
+ try {
+ List> futures = new ArrayList<>();
+ if (existingEntityView != null) {
+ if (existingEntityView.getKeys() != null && existingEntityView.getKeys().getAttributes() != null) {
+ futures.add(deleteAttributesFromEntityView(existingEntityView, DataConstants.CLIENT_SCOPE, existingEntityView.getKeys().getAttributes().getCs(), user));
+ futures.add(deleteAttributesFromEntityView(existingEntityView, DataConstants.SERVER_SCOPE, existingEntityView.getKeys().getAttributes().getCs(), user));
+ futures.add(deleteAttributesFromEntityView(existingEntityView, DataConstants.SHARED_SCOPE, existingEntityView.getKeys().getAttributes().getCs(), user));
+ }
+ List tsKeys = existingEntityView.getKeys() != null && existingEntityView.getKeys().getTimeseries() != null ?
+ existingEntityView.getKeys().getTimeseries() : Collections.emptyList();
+ futures.add(deleteLatestFromEntityView(existingEntityView, tsKeys, user));
+ }
+ EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView));
+ if (savedEntityView.getKeys() != null) {
+ if (savedEntityView.getKeys().getAttributes() != null) {
+ futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.CLIENT_SCOPE, savedEntityView.getKeys().getAttributes().getCs(), user));
+ futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SERVER_SCOPE, savedEntityView.getKeys().getAttributes().getSs(), user));
+ futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SHARED_SCOPE, savedEntityView.getKeys().getAttributes().getSh(), user));
+ }
+ futures.add(copyLatestFromEntityToEntityView(savedEntityView, user));
+ }
+ for (ListenableFuture> future : futures) {
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException("Failed to copy attributes to entity view", e);
+ }
+ }
+
+ notificationEntityService.notifyCreateOrUpdateEntity(savedEntityView.getTenantId(), savedEntityView.getId(), savedEntityView,
+ null, actionType, user);
+
+ return savedEntityView;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(user.getTenantId(), emptyId(EntityType.ENTITY_VIEW), entityView, null, actionType, user, e);
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void delete(EntityView entityView, SecurityUser user) throws ThingsboardException {
+ TenantId tenantId = entityView.getTenantId();
+ EntityViewId entityViewId = entityView.getId();
+ try {
+ List relatedEdgeIds = findRelatedEdgeIds(tenantId, entityViewId);
+ entityViewService.deleteEntityView(tenantId, entityViewId);
+ notificationEntityService.notifyDeleteEntity(tenantId, entityViewId, entityView, entityView.getCustomerId(), ActionType.DELETED,
+ relatedEdgeIds, user, entityViewId.toString());
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.ENTITY_VIEW), null, null,
+ ActionType.DELETED, user, e, entityViewId.toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public EntityView assignEntityViewToCustomer(TenantId tenantId, EntityViewId entityViewId, Customer customer, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = ActionType.ASSIGNED_TO_CUSTOMER;
+ CustomerId customerId = customer.getId();
+ try {
+ EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(tenantId, entityViewId, customerId));
+ notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, entityViewId, customerId, savedEntityView,
+ actionType, EdgeEventActionType.ASSIGNED_TO_CUSTOMER, user, true, customerId.toString(), customer.getName());
+ return savedEntityView;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.ENTITY_VIEW), null, null,
+ actionType, user, e, entityViewId.toString(), customerId.toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public EntityView assignEntityViewToPublicCustomer(TenantId tenantId, CustomerId customerId, Customer publicCustomer,
+ EntityViewId entityViewId, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = ActionType.ASSIGNED_TO_CUSTOMER;
+ try {
+ EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(tenantId,
+ entityViewId, publicCustomer.getId()));
+ notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, entityViewId, customerId, savedEntityView,
+ actionType, null, user, false, savedEntityView.getEntityId().toString(),
+ publicCustomer.getId().toString(), publicCustomer.getName());
+ return savedEntityView;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.ENTITY_VIEW), null, null,
+ actionType, user, e, entityViewId.toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public EntityView assignEntityViewToEdge(TenantId tenantId, CustomerId customerId, EntityViewId entityViewId, Edge edge, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = ActionType.ASSIGNED_TO_EDGE;
+ EdgeId edgeId = edge.getId();
+ EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToEdge(tenantId, entityViewId, edgeId));
+ try {
+ notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, entityViewId, customerId,
+ edgeId, savedEntityView, actionType, user, savedEntityView.getEntityId().toString(),
+ edgeId.toString(), edge.getName());
+ return savedEntityView;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DEVICE), null, null,
+ actionType, user, e, entityViewId.toString(), edgeId.toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public EntityView unassignEntityViewFromEdge(TenantId tenantId, CustomerId customerId, EntityView entityView,
+ Edge edge, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = ActionType.UNASSIGNED_FROM_EDGE;
+ EntityViewId entityViewId = entityView.getId();
+ EdgeId edgeId = edge.getId();
+ try {
+ EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(tenantId, entityViewId, edgeId));
+ notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, entityViewId, customerId,
+ edgeId, entityView, actionType, user, entityViewId.toString(),
+ edgeId.toString(), edge.getName());
+ return savedEntityView;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.ENTITY_VIEW), null, null,
+ actionType, user, e, entityViewId.toString(), edgeId.toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public EntityView unassignEntityViewFromCustomer(TenantId tenantId, EntityViewId entityViewId, Customer customer, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = ActionType.UNASSIGNED_FROM_CUSTOMER;
+ try {
+ EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromCustomer(tenantId, entityViewId));
+ notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, entityViewId, customer.getId(), savedEntityView,
+ actionType, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER, user, true, customer.getId().toString(), customer.getName());
+ return savedEntityView;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.ENTITY_VIEW), null, null,
+ actionType, user, e, entityViewId.toString());
+ throw handleException(e);
+ }
+ }
+
+ private ListenableFuture> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection keys, SecurityUser user) throws ThingsboardException {
+ EntityViewId entityId = entityView.getId();
+ if (keys != null && !keys.isEmpty()) {
+ ListenableFuture> getAttrFuture = attributesService.find(entityView.getTenantId(), entityView.getEntityId(), scope, keys);
+ return Futures.transform(getAttrFuture, attributeKvEntries -> {
+ List attributes;
+ if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) {
+ attributes =
+ attributeKvEntries.stream()
+ .filter(attributeKvEntry -> {
+ long startTime = entityView.getStartTimeMs();
+ long endTime = entityView.getEndTimeMs();
+ long lastUpdateTs = attributeKvEntry.getLastUpdateTs();
+ return startTime == 0 && endTime == 0 ||
+ (endTime == 0 && startTime < lastUpdateTs) ||
+ (startTime == 0 && endTime > lastUpdateTs)
+ ? true : startTime < lastUpdateTs && endTime > lastUpdateTs;
+ }).collect(Collectors.toList());
+ tsSubService.saveAndNotify(entityView.getTenantId(), entityId, scope, attributes, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable Void tmp) {
+ try {
+ logAttributesUpdated(entityView.getTenantId(), user, entityId, scope, attributes, null);
+ } catch (ThingsboardException e) {
+ log.error("Failed to log attribute updates", e);
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ try {
+ logAttributesUpdated(entityView.getTenantId(), user, entityId, scope, attributes, t);
+ } catch (ThingsboardException e) {
+ log.error("Failed to log attribute updates", e);
+ }
+ }
+ });
+ }
+ return null;
+ }, MoreExecutors.directExecutor());
+ } else {
+ return Futures.immediateFuture(null);
+ }
+ }
+
+ private ListenableFuture> copyLatestFromEntityToEntityView(EntityView entityView, SecurityUser user) {
+ EntityViewId entityId = entityView.getId();
+ List keys = entityView.getKeys() != null && entityView.getKeys().getTimeseries() != null ?
+ entityView.getKeys().getTimeseries() : Collections.emptyList();
+ long startTs = entityView.getStartTimeMs();
+ long endTs = entityView.getEndTimeMs() == 0 ? Long.MAX_VALUE : entityView.getEndTimeMs();
+ ListenableFuture> keysFuture;
+ if (keys.isEmpty()) {
+ keysFuture = Futures.transform(tsService.findAllLatest(user.getTenantId(),
+ entityView.getEntityId()), latest -> latest.stream().map(TsKvEntry::getKey).collect(Collectors.toList()), MoreExecutors.directExecutor());
+ } else {
+ keysFuture = Futures.immediateFuture(keys);
+ }
+ ListenableFuture> latestFuture = Futures.transformAsync(keysFuture, fetchKeys -> {
+ List queries = fetchKeys.stream().filter(key -> !isBlank(key)).map(key -> new BaseReadTsKvQuery(key, startTs, endTs, 1, "DESC")).collect(Collectors.toList());
+ if (!queries.isEmpty()) {
+ return tsService.findAll(user.getTenantId(), entityView.getEntityId(), queries);
+ } else {
+ return Futures.immediateFuture(null);
+ }
+ }, MoreExecutors.directExecutor());
+ return Futures.transform(latestFuture, latestValues -> {
+ if (latestValues != null && !latestValues.isEmpty()) {
+ tsSubService.saveLatestAndNotify(entityView.getTenantId(), entityId, latestValues, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable Void tmp) {
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ }
+ });
+ }
+ return null;
+ }, MoreExecutors.directExecutor());
+ }
+
+ private ListenableFuture deleteAttributesFromEntityView(EntityView entityView, String scope, List keys, SecurityUser user) {
+ EntityViewId entityId = entityView.getId();
+ SettableFuture resultFuture = SettableFuture.create();
+ if (keys != null && !keys.isEmpty()) {
+ tsSubService.deleteAndNotify(entityView.getTenantId(), entityId, scope, keys, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable Void tmp) {
+ try {
+ logAttributesDeleted(entityView.getTenantId(), user, entityId, scope, keys, null);
+ } catch (ThingsboardException e) {
+ log.error("Failed to log attribute delete", e);
+ }
+ resultFuture.set(tmp);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ try {
+ logAttributesDeleted(entityView.getTenantId(), user, entityId, scope, keys, t);
+ } catch (ThingsboardException e) {
+ log.error("Failed to log attribute delete", e);
+ }
+ resultFuture.setException(t);
+ }
+ });
+ } else {
+ resultFuture.set(null);
+ }
+ return resultFuture;
+ }
+
+ private ListenableFuture deleteLatestFromEntityView(EntityView entityView, List keys, SecurityUser user) {
+ EntityViewId entityId = entityView.getId();
+ SettableFuture resultFuture = SettableFuture.create();
+ if (keys != null && !keys.isEmpty()) {
+ tsSubService.deleteLatest(entityView.getTenantId(), entityId, keys, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable Void tmp) {
+ try {
+ logTimeseriesDeleted(entityView.getTenantId(), user, entityId, keys, null);
+ } catch (ThingsboardException e) {
+ log.error("Failed to log timeseries delete", e);
+ }
+ resultFuture.set(tmp);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ try {
+ logTimeseriesDeleted(entityView.getTenantId(),user, entityId, keys, t);
+ } catch (ThingsboardException e) {
+ log.error("Failed to log timeseries delete", e);
+ }
+ resultFuture.setException(t);
+ }
+ });
+ } else {
+ tsSubService.deleteAllLatest(entityView.getTenantId(), entityId, new FutureCallback>() {
+ @Override
+ public void onSuccess(@Nullable Collection keys) {
+ try {
+ logTimeseriesDeleted(entityView.getTenantId(), user, entityId, new ArrayList<>(keys), null);
+ } catch (ThingsboardException e) {
+ log.error("Failed to log timeseries delete", e);
+ }
+ resultFuture.set(null);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ try {
+ logTimeseriesDeleted(entityView.getTenantId(), user, entityId, Collections.emptyList(), t);
+ } catch (ThingsboardException e) {
+ log.error("Failed to log timeseries delete", e);
+ }
+ resultFuture.setException(t);
+ }
+ });
+ }
+ return resultFuture;
+ }
+
+ private void logAttributesUpdated(TenantId tenantId, SecurityUser user, EntityId entityId, String scope, List attributes, Throwable e) throws ThingsboardException {
+ notificationEntityService.notifyEntity(tenantId, entityId, null, null, ActionType.ATTRIBUTES_UPDATED, user, toException(e), scope, attributes);
+ }
+
+ private void logAttributesDeleted(TenantId tenantId, SecurityUser user, EntityId entityId, String scope, List keys, Throwable e) throws ThingsboardException {
+ notificationEntityService.notifyEntity(tenantId, entityId, null, null, ActionType.ATTRIBUTES_DELETED, user, toException(e), scope, keys);
+ }
+
+ private void logTimeseriesDeleted(TenantId tenantId, SecurityUser user, EntityId entityId, List keys, Throwable e) throws ThingsboardException {
+ notificationEntityService.notifyEntity(tenantId, entityId, null, null, ActionType.TIMESERIES_DELETED, user, toException(e), keys);
+ }
+
+ public static Exception toException(Throwable error) {
+ return error != null ? (Exception.class.isInstance(error) ? (Exception) error : new Exception(error)) : null;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entityView/TbEntityViewService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entityView/TbEntityViewService.java
new file mode 100644
index 0000000000..dd5db9391b
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entityView/TbEntityViewService.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.entityView;
+
+import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.EntityView;
+import org.thingsboard.server.common.data.edge.Edge;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.EntityViewId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+public interface TbEntityViewService {
+
+ EntityView save(EntityView entityView, EntityView existingEntityView, SecurityUser user) throws ThingsboardException;
+
+ void delete (EntityView entity, SecurityUser user) throws ThingsboardException;
+
+ EntityView assignEntityViewToCustomer(TenantId tenantId, EntityViewId entityViewId, Customer customer,
+ SecurityUser user) throws ThingsboardException;
+
+ EntityView assignEntityViewToPublicCustomer(TenantId tenantId, CustomerId customerId, Customer publicCustomer,
+ EntityViewId entityViewId, SecurityUser user) throws ThingsboardException;
+
+ EntityView assignEntityViewToEdge(TenantId tenantId, CustomerId customerId, EntityViewId entityViewId, Edge edge,
+ SecurityUser user) throws ThingsboardException;
+
+ EntityView unassignEntityViewFromEdge(TenantId tenantId, CustomerId customerId, EntityView entityView,
+ Edge edge, SecurityUser user) throws ThingsboardException;
+
+ EntityView unassignEntityViewFromCustomer(TenantId tenantId, EntityViewId entityViewId, Customer customer,
+ SecurityUser user) throws ThingsboardException;
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/DefaultTbOtaPackageService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/DefaultTbOtaPackageService.java
new file mode 100644
index 0000000000..555041ed8f
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/DefaultTbOtaPackageService.java
@@ -0,0 +1,107 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.otaPackageController;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.common.data.StringUtils;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.OtaPackage;
+import org.thingsboard.server.common.data.OtaPackageInfo;
+import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
+import org.thingsboard.server.common.data.audit.ActionType;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.OtaPackageId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
+import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+import java.nio.ByteBuffer;
+
+@Service
+@TbCoreComponent
+@AllArgsConstructor
+@Slf4j
+public class DefaultTbOtaPackageService extends AbstractTbEntityService implements TbOtaPackageService {
+ @Override
+ public OtaPackageInfo save(SaveOtaPackageInfoRequest saveOtaPackageInfoRequest, SecurityUser user) throws ThingsboardException {
+ TenantId tenantId = saveOtaPackageInfoRequest.getTenantId();
+ ActionType actionType = saveOtaPackageInfoRequest.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
+ try {
+ OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(new OtaPackageInfo(saveOtaPackageInfoRequest), saveOtaPackageInfoRequest.isUsesUrl());
+ notificationEntityService.notifyEntity(tenantId, savedOtaPackageInfo.getId(), savedOtaPackageInfo, null,
+ actionType, user, null);
+ return savedOtaPackageInfo;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.OTA_PACKAGE), saveOtaPackageInfoRequest, null,
+ actionType, user, e);
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void delete(OtaPackageInfo otaPackageInfo, SecurityUser user) throws ThingsboardException {
+ TenantId tenantId = otaPackageInfo.getTenantId();
+ OtaPackageId otaPackageId = otaPackageInfo.getId();
+ try {
+ otaPackageService.deleteOtaPackage(tenantId, otaPackageId);
+ notificationEntityService.notifyEntity(tenantId, otaPackageId, otaPackageInfo, null,
+ ActionType.DELETED, user, null, otaPackageInfo.getId().toString());
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.OTA_PACKAGE), null, null,
+ ActionType.DELETED, user, e, otaPackageInfo.getId().toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public OtaPackageInfo saveOtaPackageData(OtaPackageInfo otaPackageInfo, String checksum, ChecksumAlgorithm checksumAlgorithm,
+ byte[] data, String filename, String contentType, SecurityUser user) throws ThingsboardException {
+ TenantId tenantId = otaPackageInfo.getTenantId();
+ OtaPackageId otaPackageId = otaPackageInfo.getId();
+ try {
+ if (StringUtils.isEmpty(checksum)) {
+ checksum = otaPackageService.generateChecksum(checksumAlgorithm, ByteBuffer.wrap(data));
+ }
+ OtaPackage otaPackage = new OtaPackage(otaPackageId);
+ otaPackage.setCreatedTime(otaPackageInfo.getCreatedTime());
+ otaPackage.setTenantId(tenantId);
+ otaPackage.setDeviceProfileId(otaPackageInfo.getDeviceProfileId());
+ otaPackage.setType(otaPackageInfo.getType());
+ otaPackage.setTitle(otaPackageInfo.getTitle());
+ otaPackage.setVersion(otaPackageInfo.getVersion());
+ otaPackage.setTag(otaPackageInfo.getTag());
+ otaPackage.setAdditionalInfo(otaPackageInfo.getAdditionalInfo());
+ otaPackage.setChecksumAlgorithm(checksumAlgorithm);
+ otaPackage.setChecksum(checksum);
+ otaPackage.setFileName(filename);
+ otaPackage.setContentType(contentType);
+ otaPackage.setData(ByteBuffer.wrap(data));
+ otaPackage.setDataSize((long) data.length);
+ OtaPackageInfo savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage);
+ notificationEntityService.notifyEntity(tenantId, savedOtaPackage.getId(), savedOtaPackage, null,
+ ActionType.UPDATED, user, null);
+ return savedOtaPackage;
+ } catch (Exception e) {
+ notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.OTA_PACKAGE), null, null,
+ ActionType.UPDATED, user, e, otaPackageId.toString());
+ throw handleException(e);
+ }
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/TbOtaPackageService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/TbOtaPackageService.java
new file mode 100644
index 0000000000..7a7d0e79f7
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/otaPackageController/TbOtaPackageService.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.otaPackageController;
+
+import org.thingsboard.server.common.data.OtaPackageInfo;
+import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+public interface TbOtaPackageService {
+
+ OtaPackageInfo save(SaveOtaPackageInfoRequest saveOtaPackageInfoRequest, SecurityUser user) throws ThingsboardException;
+
+ void delete(OtaPackageInfo otaPackageInfo, SecurityUser user) throws ThingsboardException;
+
+ OtaPackageInfo saveOtaPackageData(OtaPackageInfo otaPackageInfo, String checksum, ChecksumAlgorithm checksumAlgorithm,
+ byte[] data, String filename, String contentType, SecurityUser securityUser) throws ThingsboardException;
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java
new file mode 100644
index 0000000000..6a5778a474
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java
@@ -0,0 +1,269 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.queue;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.cluster.TbClusterService;
+import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.TenantProfile;
+import org.thingsboard.server.common.data.id.QueueId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.common.data.queue.Queue;
+import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration;
+import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
+import org.thingsboard.server.dao.device.DeviceProfileService;
+import org.thingsboard.server.queue.TbQueueAdmin;
+import org.thingsboard.server.queue.scheduler.SchedulerComponent;
+import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@TbCoreComponent
+@AllArgsConstructor
+public class DefaultTbQueueService extends AbstractTbEntityService implements TbQueueService {
+ private static final String MAIN = "Main";
+ private static final long DELETE_DELAY = 30;
+
+ private final TbClusterService tbClusterService;
+ private final TbQueueAdmin tbQueueAdmin;
+ private final DeviceProfileService deviceProfileService;
+ private final SchedulerComponent scheduler;
+
+ @Override
+ public Queue saveQueue(Queue queue) {
+ boolean create = queue.getId() == null;
+ Queue oldQueue;
+
+ if (create) {
+ oldQueue = null;
+ } else {
+ oldQueue = queueService.findQueueById(queue.getTenantId(), queue.getId());
+ }
+
+ //TODO: add checkNotNull
+ Queue savedQueue = queueService.saveQueue(queue);
+
+ if (create) {
+ onQueueCreated(savedQueue);
+ } else {
+ onQueueUpdated(savedQueue, oldQueue);
+ }
+
+ return savedQueue;
+ }
+
+ @Override
+ public void deleteQueue(TenantId tenantId, QueueId queueId) {
+ Queue queue = queueService.findQueueById(tenantId, queueId);
+ queueService.deleteQueue(tenantId, queueId);
+ onQueueDeleted(queue);
+ }
+
+ @Override
+ public void deleteQueueByQueueName(TenantId tenantId, String queueName) {
+ Queue queue = queueService.findQueueByTenantIdAndNameInternal(tenantId, queueName);
+ queueService.deleteQueue(tenantId, queue.getId());
+ onQueueDeleted(queue);
+ }
+
+ private void onQueueCreated(Queue queue) {
+ for (int i = 0; i < queue.getPartitions(); i++) {
+ tbQueueAdmin.createTopicIfNotExists(
+ new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName());
+ }
+
+ tbClusterService.onQueueChange(queue);
+ }
+
+ private void onQueueUpdated(Queue queue, Queue oldQueue) {
+ int oldPartitions = oldQueue.getPartitions();
+ int currentPartitions = queue.getPartitions();
+
+ if (currentPartitions != oldPartitions) {
+ if (currentPartitions > oldPartitions) {
+ log.info("Added [{}] new partitions to [{}] queue", currentPartitions - oldPartitions, queue.getName());
+ for (int i = oldPartitions; i < currentPartitions; i++) {
+ tbQueueAdmin.createTopicIfNotExists(
+ new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName());
+ }
+ tbClusterService.onQueueChange(queue);
+ } else {
+ log.info("Removed [{}] partitions from [{}] queue", oldPartitions - currentPartitions, queue.getName());
+ tbClusterService.onQueueChange(queue);
+
+ scheduler.schedule(() -> {
+ for (int i = currentPartitions; i < oldPartitions; i++) {
+ String fullTopicName = new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName();
+ log.info("Removed partition [{}]", fullTopicName);
+ tbQueueAdmin.deleteTopic(
+ fullTopicName);
+ }
+ }, DELETE_DELAY, TimeUnit.SECONDS);
+ }
+ } else if (!oldQueue.equals(queue)) {
+ tbClusterService.onQueueChange(queue);
+ }
+ }
+
+ private void onQueueDeleted(Queue queue) {
+ tbClusterService.onQueueDelete(queue);
+
+// queueStatsService.deleteQueueStatsByQueueId(tenantId, queueId);
+
+ scheduler.schedule(() -> {
+ for (int i = 0; i < queue.getPartitions(); i++) {
+ String fullTopicName = new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName();
+ log.info("Deleting queue [{}]", fullTopicName);
+ try {
+ tbQueueAdmin.deleteTopic(fullTopicName);
+ } catch (Exception e) {
+ log.error("Failed to delete queue [{}]", fullTopicName);
+ }
+ }
+ }, DELETE_DELAY, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public void updateQueuesByTenants(List tenantIds, TenantProfile newTenantProfile, TenantProfile
+ oldTenantProfile) {
+ boolean oldIsolated = oldTenantProfile != null && oldTenantProfile.isIsolatedTbRuleEngine();
+ boolean newIsolated = newTenantProfile.isIsolatedTbRuleEngine();
+
+ if (!oldIsolated && !newIsolated) {
+ return;
+ }
+
+ if (newTenantProfile.equals(oldTenantProfile)) {
+ return;
+ }
+
+ Map oldQueues;
+ Map newQueues;
+
+ if (oldIsolated) {
+ oldQueues = oldTenantProfile.getProfileData().getQueueConfiguration().stream()
+ .collect(Collectors.toMap(TenantProfileQueueConfiguration::getName, q -> q));
+ } else {
+ oldQueues = Collections.emptyMap();
+ }
+
+ if (newIsolated) {
+ newQueues = newTenantProfile.getProfileData().getQueueConfiguration().stream()
+ .collect(Collectors.toMap(TenantProfileQueueConfiguration::getName, q -> q));
+ } else {
+ newQueues = Collections.emptyMap();
+ }
+
+ List toRemove = new ArrayList<>();
+ List toCreate = new ArrayList<>();
+ List toUpdate = new ArrayList<>();
+
+ for (String oldQueue : oldQueues.keySet()) {
+ if (!newQueues.containsKey(oldQueue)) {
+ toRemove.add(oldQueue);
+ }
+ }
+
+ for (String newQueue : newQueues.keySet()) {
+ if (oldQueues.containsKey(newQueue)) {
+ toUpdate.add(newQueue);
+ } else {
+ toCreate.add(newQueue);
+ }
+ }
+
+ tenantIds.forEach(tenantId -> {
+ Map> deviceProfileQueues;
+
+ if (oldTenantProfile != null && !newTenantProfile.getId().equals(oldTenantProfile.getId()) || !toRemove.isEmpty()) {
+ List deviceProfiles = deviceProfileService.findDeviceProfiles(tenantId, new PageLink(Integer.MAX_VALUE)).getData();
+ deviceProfileQueues = deviceProfiles.stream()
+ .filter(dp -> dp.getDefaultQueueId() != null)
+ .collect(Collectors.groupingBy(DeviceProfile::getDefaultQueueId));
+ } else {
+ deviceProfileQueues = Collections.emptyMap();
+ }
+
+ Map createdQueues = toCreate.stream()
+ .map(key -> saveQueue(new Queue(tenantId, newQueues.get(key))))
+ .collect(Collectors.toMap(Queue::getName, Queue::getId));
+
+ // assigning created queues to device profiles instead of system queues
+ if (oldTenantProfile != null && !oldTenantProfile.isIsolatedTbRuleEngine()) {
+ deviceProfileQueues.forEach((queueId, list) -> {
+ Queue queue = queueService.findQueueById(TenantId.SYS_TENANT_ID, queueId);
+ QueueId queueIdToAssign = createdQueues.get(queue.getName());
+ if (queueIdToAssign == null) {
+ queueIdToAssign = createdQueues.get(MAIN);
+ }
+ for (DeviceProfile deviceProfile : list) {
+ deviceProfile.setDefaultQueueId(queueIdToAssign);
+ saveDeviceProfile(deviceProfile);
+ }
+ });
+ }
+
+ toUpdate.forEach(key -> {
+ Queue queueToUpdate = new Queue(tenantId, newQueues.get(key));
+ Queue foundQueue = queueService.findQueueByTenantIdAndName(tenantId, key);
+ queueToUpdate.setId(foundQueue.getId());
+ queueToUpdate.setCreatedTime(foundQueue.getCreatedTime());
+
+ if (queueToUpdate.equals(foundQueue)) {
+ //Queue not changed
+ } else {
+ saveQueue(queueToUpdate);
+ }
+ });
+
+ toRemove.forEach(q -> {
+ Queue queue = queueService.findQueueByTenantIdAndNameInternal(tenantId, q);
+ QueueId queueIdForRemove = queue.getId();
+ if (deviceProfileQueues.containsKey(queueIdForRemove)) {
+ Queue foundQueue = queueService.findQueueByTenantIdAndName(tenantId, q);
+ if (foundQueue == null || queue.equals(foundQueue)) {
+ foundQueue = queueService.findQueueByTenantIdAndName(tenantId, MAIN);
+ }
+ QueueId newQueueId = foundQueue.getId();
+ deviceProfileQueues.get(queueIdForRemove).stream()
+ .peek(dp -> dp.setDefaultQueueId(newQueueId))
+ .forEach(this::saveDeviceProfile);
+ }
+ deleteQueue(tenantId, queueIdForRemove);
+ });
+ });
+ }
+
+ //TODO: remove after implementing TbDeviceProfileService
+ private void saveDeviceProfile(DeviceProfile deviceProfile) {
+ DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
+ tbClusterService.onDeviceProfileChange(savedDeviceProfile, null);
+ tbClusterService.broadcastEntityStateChangeEvent(deviceProfile.getTenantId(), savedDeviceProfile.getId(), ComponentLifecycleEvent.UPDATED);
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/queue/TbQueueService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/queue/TbQueueService.java
new file mode 100644
index 0000000000..86f82b1236
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/queue/TbQueueService.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.queue;
+
+import org.thingsboard.server.common.data.TenantProfile;
+import org.thingsboard.server.common.data.id.QueueId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.queue.Queue;
+
+import java.util.List;
+
+public interface TbQueueService {
+
+ Queue saveQueue(Queue queue);
+
+ void deleteQueue(TenantId tenantId, QueueId queueId);
+
+ void deleteQueueByQueueName(TenantId tenantId, String queueName);
+
+ void updateQueuesByTenants(List tenantIds, TenantProfile newTenantProfile, TenantProfile oldTenantProfile);
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java
index ac3d651b6b..2a8fc1bf0a 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java
@@ -15,37 +15,48 @@
*/
package org.thingsboard.server.service.entitiy.tenant;
-import lombok.AllArgsConstructor;
-import org.springframework.beans.factory.annotation.Autowired;
+import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Tenant;
+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.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.dao.tenant.TenantProfileService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
+import org.thingsboard.server.service.entitiy.queue.TbQueueService;
import org.thingsboard.server.service.install.InstallScripts;
+import java.util.Collections;
+
@Service
@TbCoreComponent
-@AllArgsConstructor
+@RequiredArgsConstructor
public class DefaultTbTenantService extends AbstractTbEntityService implements TbTenantService {
- @Autowired
- private InstallScripts installScripts;
+ private final InstallScripts installScripts;
+ private final TbQueueService tbQueueService;
+ private final TenantProfileService tenantProfileService;
@Override
public Tenant save(Tenant tenant) throws ThingsboardException {
try {
- boolean newTenant = tenant.getId() == null;
+ boolean created = tenant.getId() == null;
+ Tenant oldTenant = !created ? tenantService.findTenantById(tenant.getId()) : null;
+
Tenant savedTenant = checkNotNull(tenantService.saveTenant(tenant));
- if (newTenant) {
+ if (created) {
installScripts.createDefaultRuleChains(savedTenant.getId());
installScripts.createDefaultEdgeRuleChains(savedTenant.getId());
}
tenantProfileCache.evict(savedTenant.getId());
- notificationEntityService.notifyCreateOruUpdateTenant(savedTenant, newTenant ?
+ notificationEntityService.notifyCreateOruUpdateTenant(savedTenant, created ?
ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
+
+ TenantProfile oldTenantProfile = oldTenant != null ? tenantProfileService.findTenantProfileById(TenantId.SYS_TENANT_ID, oldTenant.getTenantProfileId()) : null;
+ TenantProfile newTenantProfile = tenantProfileService.findTenantProfileById(TenantId.SYS_TENANT_ID, savedTenant.getTenantProfileId());
+ tbQueueService.updateQueuesByTenants(Collections.singletonList(savedTenant.getTenantId()), newTenantProfile, oldTenantProfile);
return savedTenant;
} catch (Exception e) {
throw handleException(e);
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant_profile/DefaultTbTenantProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant_profile/DefaultTbTenantProfileService.java
new file mode 100644
index 0000000000..23d9f74b8e
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant_profile/DefaultTbTenantProfileService.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.tenant_profile;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.TenantProfile;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.dao.tenant.TenantProfileService;
+import org.thingsboard.server.dao.tenant.TenantService;
+import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.queue.TbQueueService;
+
+import java.util.List;
+
+@Slf4j
+@Service
+@TbCoreComponent
+@AllArgsConstructor
+public class DefaultTbTenantProfileService implements TbTenantProfileService {
+ private final TbQueueService tbQueueService;
+ private final TenantProfileService tenantProfileService;
+ private final TenantService tenantService;
+
+ @Override
+ public TenantProfile saveTenantProfile(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile) {
+ TenantProfile savedTenantProfile = tenantProfileService.saveTenantProfile(tenantId, tenantProfile);
+
+ if (oldTenantProfile != null && savedTenantProfile.isIsolatedTbRuleEngine()) {
+ List tenantIds = tenantService.findTenantIdsByTenantProfileId(savedTenantProfile.getId());
+ tbQueueService.updateQueuesByTenants(tenantIds, savedTenantProfile, oldTenantProfile);
+ }
+
+ return savedTenantProfile;
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant_profile/TbTenantProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant_profile/TbTenantProfileService.java
new file mode 100644
index 0000000000..66c3cb138c
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant_profile/TbTenantProfileService.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.tenant_profile;
+
+import org.thingsboard.server.common.data.TenantProfile;
+import org.thingsboard.server.common.data.id.TenantId;
+
+public interface TbTenantProfileService {
+ TenantProfile saveTenantProfile(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile);
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java
new file mode 100644
index 0000000000..592496e6f8
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.user;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.thingsboard.rule.engine.api.MailService;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.User;
+import org.thingsboard.server.common.data.audit.ActionType;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.EdgeId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.UserId;
+import org.thingsboard.server.common.data.security.UserCredentials;
+import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
+import org.thingsboard.server.service.security.model.SecurityUser;
+import org.thingsboard.server.service.security.system.SystemSecurityService;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+import static org.thingsboard.server.controller.UserController.ACTIVATE_URL_PATTERN;
+
+@Service
+@TbCoreComponent
+@AllArgsConstructor
+@Slf4j
+public class DefaultUserService extends AbstractTbEntityService implements TbUserService {
+
+ private final MailService mailService;
+ private final SystemSecurityService systemSecurityService;
+
+ @Override
+ public User save(TenantId tenantId, CustomerId customerId, User tbUser, boolean sendActivationMail,
+ HttpServletRequest request, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = tbUser.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
+ try {
+ boolean sendEmail = tbUser.getId() == null && sendActivationMail;
+ User savedUser = checkNotNull(userService.saveUser(tbUser));
+ if (sendEmail) {
+ UserCredentials userCredentials = userService.findUserCredentialsByUserId(tenantId, savedUser.getId());
+ String baseUrl = systemSecurityService.getBaseUrl(tenantId, customerId, request);
+ String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl,
+ userCredentials.getActivateToken());
+ String email = savedUser.getEmail();
+ try {
+ mailService.sendActivationEmail(activateUrl, email);
+ } catch (ThingsboardException e) {
+ userService.deleteUser(tenantId, savedUser.getId());
+ throw e;
+ }
+ }
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, customerId, savedUser.getId(),
+ savedUser, user, actionType, true, null);
+ return savedUser;
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.USER),
+ tbUser, user, actionType, false, e);
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void delete(TenantId tenantId, CustomerId customerId, User tbUser, SecurityUser user) throws ThingsboardException {
+ UserId userId = tbUser.getId();
+ try {
+ List relatedEdgeIds = findRelatedEdgeIds(tenantId, userId);
+
+ userService.deleteUser(tenantId, userId);
+ notificationEntityService.notifyDeleteEntity(tenantId, userId, tbUser, customerId,
+ ActionType.DELETED, relatedEdgeIds, user, userId.toString());
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.USER),
+ null, user, ActionType.DELETED, false, e, userId.toString());
+ throw handleException(e);
+ }
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java
new file mode 100644
index 0000000000..177aad17cc
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.user;
+
+import org.thingsboard.server.common.data.User;
+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.service.security.model.SecurityUser;
+
+import javax.servlet.http.HttpServletRequest;
+
+public interface TbUserService {
+ User save(TenantId tenantId, CustomerId customerId, User tbUser, boolean sendActivationMail, HttpServletRequest request, SecurityUser user) throws ThingsboardException;
+
+ void delete (TenantId tenantId, CustomerId customerId, User tbUser, SecurityUser user) throws ThingsboardException;
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/DefaultWidgetsBundleService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/DefaultWidgetsBundleService.java
new file mode 100644
index 0000000000..f3d8dfc138
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/DefaultWidgetsBundleService.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.widgetsBundle;
+
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.edge.EdgeEventActionType;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.widget.WidgetsBundle;
+import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+@Service
+@TbCoreComponent
+@AllArgsConstructor
+public class DefaultWidgetsBundleService extends AbstractTbEntityService implements TbWidgetsBundleService{
+ @Override
+ public WidgetsBundle save(WidgetsBundle widgetsBundle, SecurityUser user) throws ThingsboardException {
+ try {
+ WidgetsBundle savedWidgetsBundle = checkNotNull(widgetsBundleService.saveWidgetsBundle(widgetsBundle));
+ notificationEntityService.notifySendMsgToEdgeService(widgetsBundle.getTenantId(), savedWidgetsBundle.getId(),
+ widgetsBundle.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
+ return savedWidgetsBundle;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void delete(WidgetsBundle widgetsBundle, SecurityUser user) throws ThingsboardException {
+ try {
+ widgetsBundleService.deleteWidgetsBundle(widgetsBundle.getTenantId(), widgetsBundle.getId());
+ notificationEntityService.notifySendMsgToEdgeService(widgetsBundle.getTenantId(), widgetsBundle.getId(),
+ EdgeEventActionType.DELETED);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/TbWidgetsBundleService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/TbWidgetsBundleService.java
new file mode 100644
index 0000000000..7672db1a71
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/widgetsBundle/TbWidgetsBundleService.java
@@ -0,0 +1,22 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.entitiy.widgetsBundle;
+
+import org.thingsboard.server.common.data.widget.WidgetsBundle;
+import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
+
+public interface TbWidgetsBundleService extends SimpleTbEntityService {
+}
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 9e7636879f..cece3377b0 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
@@ -68,12 +68,18 @@ import org.thingsboard.server.common.data.query.DynamicValueSourceType;
import org.thingsboard.server.common.data.query.EntityKeyValueType;
import org.thingsboard.server.common.data.query.FilterPredicateValue;
import org.thingsboard.server.common.data.query.NumericFilterPredicate;
+import org.thingsboard.server.common.data.queue.ProcessingStrategy;
+import org.thingsboard.server.common.data.queue.ProcessingStrategyType;
+import org.thingsboard.server.common.data.queue.Queue;
+import org.thingsboard.server.common.data.queue.SubmitStrategy;
+import org.thingsboard.server.common.data.queue.SubmitStrategyType;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
+import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.customer.CustomerService;
@@ -81,6 +87,7 @@ import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.exception.DataValidationException;
+import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.dao.tenant.TenantProfileService;
@@ -155,6 +162,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
@Getter
private boolean persistActivityToTelemetry;
+ @Autowired
+ private QueueService queueService;
+
@Bean
protected BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
@@ -199,13 +209,37 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
log.warn(e.getMessage());
}
+ TenantProfileData isolatedRuleEngineTenantProfileData = new TenantProfileData();
+ isolatedRuleEngineTenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration());
+
+ TenantProfileQueueConfiguration mainQueueConfiguration = new TenantProfileQueueConfiguration();
+ mainQueueConfiguration.setName("Main");
+ mainQueueConfiguration.setTopic("tb_rule_engine.main");
+ mainQueueConfiguration.setPollInterval(25);
+ mainQueueConfiguration.setPartitions(10);
+ mainQueueConfiguration.setConsumerPerPartition(true);
+ mainQueueConfiguration.setPackProcessingTimeout(2000);
+ SubmitStrategy mainQueueSubmitStrategy = new SubmitStrategy();
+ mainQueueSubmitStrategy.setType(SubmitStrategyType.BURST);
+ mainQueueSubmitStrategy.setBatchSize(1000);
+ mainQueueConfiguration.setSubmitStrategy(mainQueueSubmitStrategy);
+ ProcessingStrategy mainQueueProcessingStrategy = new ProcessingStrategy();
+ mainQueueProcessingStrategy.setType(ProcessingStrategyType.SKIP_ALL_FAILURES);
+ mainQueueProcessingStrategy.setRetries(3);
+ mainQueueProcessingStrategy.setFailurePercentage(0);
+ mainQueueProcessingStrategy.setPauseBetweenRetries(3);
+ mainQueueProcessingStrategy.setMaxPauseBetweenRetries(3);
+ mainQueueConfiguration.setProcessingStrategy(mainQueueProcessingStrategy);
+
+ isolatedRuleEngineTenantProfileData.setQueueConfiguration(Collections.singletonList(mainQueueConfiguration));
+
TenantProfile isolatedTbRuleEngineProfile = new TenantProfile();
isolatedTbRuleEngineProfile.setDefault(false);
isolatedTbRuleEngineProfile.setName("Isolated TB Rule Engine");
isolatedTbRuleEngineProfile.setDescription("Isolated TB Rule Engine tenant profile");
isolatedTbRuleEngineProfile.setIsolatedTbCore(false);
isolatedTbRuleEngineProfile.setIsolatedTbRuleEngine(true);
- isolatedTbRuleEngineProfile.setProfileData(tenantProfileData);
+ isolatedTbRuleEngineProfile.setProfileData(isolatedRuleEngineTenantProfileData);
try {
tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, isolatedTbRuleEngineProfile);
@@ -219,7 +253,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
isolatedTbCoreAndTbRuleEngineProfile.setDescription("Isolated TB Core and TB Rule Engine tenant profile");
isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbCore(true);
isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbRuleEngine(true);
- isolatedTbCoreAndTbRuleEngineProfile.setProfileData(tenantProfileData);
+ isolatedTbCoreAndTbRuleEngineProfile.setProfileData(isolatedRuleEngineTenantProfileData);
try {
tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, isolatedTbCoreAndTbRuleEngineProfile);
@@ -575,4 +609,78 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
}, tsCallBackExecutor);
}
+ @Override
+ public void createQueues() {
+ Queue mainQueue = queueService.findQueueByTenantIdAndName(TenantId.SYS_TENANT_ID, "Main");
+ if (mainQueue == null) {
+ mainQueue = new Queue();
+ mainQueue.setTenantId(TenantId.SYS_TENANT_ID);
+ mainQueue.setName("Main");
+ mainQueue.setTopic("tb_rule_engine.main");
+ mainQueue.setPollInterval(25);
+ mainQueue.setPartitions(10);
+ mainQueue.setConsumerPerPartition(true);
+ mainQueue.setPackProcessingTimeout(2000);
+ SubmitStrategy mainQueueSubmitStrategy = new SubmitStrategy();
+ mainQueueSubmitStrategy.setType(SubmitStrategyType.BURST);
+ mainQueueSubmitStrategy.setBatchSize(1000);
+ mainQueue.setSubmitStrategy(mainQueueSubmitStrategy);
+ ProcessingStrategy mainQueueProcessingStrategy = new ProcessingStrategy();
+ mainQueueProcessingStrategy.setType(ProcessingStrategyType.SKIP_ALL_FAILURES);
+ mainQueueProcessingStrategy.setRetries(3);
+ mainQueueProcessingStrategy.setFailurePercentage(0);
+ mainQueueProcessingStrategy.setPauseBetweenRetries(3);
+ mainQueueProcessingStrategy.setMaxPauseBetweenRetries(3);
+ mainQueue.setProcessingStrategy(mainQueueProcessingStrategy);
+ queueService.saveQueue(mainQueue);
+ }
+
+ Queue highPriorityQueue = queueService.findQueueByTenantIdAndName(TenantId.SYS_TENANT_ID, "HighPriority");
+ if (highPriorityQueue == null) {
+ highPriorityQueue = new Queue();
+ highPriorityQueue.setTenantId(TenantId.SYS_TENANT_ID);
+ highPriorityQueue.setName("HighPriority");
+ highPriorityQueue.setTopic("tb_rule_engine.hp");
+ highPriorityQueue.setPollInterval(25);
+ highPriorityQueue.setPartitions(10);
+ highPriorityQueue.setConsumerPerPartition(true);
+ highPriorityQueue.setPackProcessingTimeout(2000);
+ SubmitStrategy highPriorityQueueSubmitStrategy = new SubmitStrategy();
+ highPriorityQueueSubmitStrategy.setType(SubmitStrategyType.BURST);
+ highPriorityQueueSubmitStrategy.setBatchSize(100);
+ highPriorityQueue.setSubmitStrategy(highPriorityQueueSubmitStrategy);
+ ProcessingStrategy highPriorityQueueProcessingStrategy = new ProcessingStrategy();
+ highPriorityQueueProcessingStrategy.setType(ProcessingStrategyType.RETRY_FAILED_AND_TIMED_OUT);
+ highPriorityQueueProcessingStrategy.setRetries(0);
+ highPriorityQueueProcessingStrategy.setFailurePercentage(0);
+ highPriorityQueueProcessingStrategy.setPauseBetweenRetries(5);
+ highPriorityQueueProcessingStrategy.setMaxPauseBetweenRetries(5);
+ highPriorityQueue.setProcessingStrategy(highPriorityQueueProcessingStrategy);
+ queueService.saveQueue(highPriorityQueue);
+ }
+
+ Queue sequentialByOriginatorQueue = queueService.findQueueByTenantIdAndName(TenantId.SYS_TENANT_ID, "SequentialByOriginator");
+ if (sequentialByOriginatorQueue == null) {
+ sequentialByOriginatorQueue = new Queue();
+ sequentialByOriginatorQueue.setTenantId(TenantId.SYS_TENANT_ID);
+ sequentialByOriginatorQueue.setName("SequentialByOriginator");
+ sequentialByOriginatorQueue.setTopic("tb_rule_engine.sq");
+ sequentialByOriginatorQueue.setPollInterval(25);
+ sequentialByOriginatorQueue.setPartitions(10);
+ sequentialByOriginatorQueue.setPackProcessingTimeout(2000);
+ sequentialByOriginatorQueue.setConsumerPerPartition(true);
+ SubmitStrategy sequentialByOriginatorQueueSubmitStrategy = new SubmitStrategy();
+ sequentialByOriginatorQueueSubmitStrategy.setType(SubmitStrategyType.SEQUENTIAL_BY_ORIGINATOR);
+ sequentialByOriginatorQueueSubmitStrategy.setBatchSize(100);
+ sequentialByOriginatorQueue.setSubmitStrategy(sequentialByOriginatorQueueSubmitStrategy);
+ ProcessingStrategy sequentialByOriginatorQueueProcessingStrategy = new ProcessingStrategy();
+ sequentialByOriginatorQueueProcessingStrategy.setType(ProcessingStrategyType.RETRY_FAILED_AND_TIMED_OUT);
+ sequentialByOriginatorQueueProcessingStrategy.setRetries(3);
+ sequentialByOriginatorQueueProcessingStrategy.setFailurePercentage(0);
+ sequentialByOriginatorQueueProcessingStrategy.setPauseBetweenRetries(5);
+ sequentialByOriginatorQueueProcessingStrategy.setMaxPauseBetweenRetries(5);
+ sequentialByOriginatorQueue.setProcessingStrategy(sequentialByOriginatorQueueProcessingStrategy);
+ queueService.saveQueue(sequentialByOriginatorQueue);
+ }
+ }
}
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 f7b9e5cad1..306102c66c 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
@@ -15,20 +15,37 @@
*/
package org.thingsboard.server.service.install;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.Tenant;
+import org.thingsboard.server.common.data.TenantProfile;
+import org.thingsboard.server.common.data.id.QueueId;
+import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.common.data.queue.ProcessingStrategy;
+import org.thingsboard.server.common.data.queue.ProcessingStrategyType;
+import org.thingsboard.server.common.data.queue.Queue;
+import org.thingsboard.server.common.data.queue.SubmitStrategy;
+import org.thingsboard.server.common.data.queue.SubmitStrategyType;
+import org.thingsboard.server.common.data.rule.RuleNode;
+import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService;
+import org.thingsboard.server.dao.queue.QueueService;
+import org.thingsboard.server.dao.rule.RuleChainService;
+import org.thingsboard.server.dao.tenant.TenantProfileService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
+import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
import org.thingsboard.server.service.install.sql.SqlDbHelper;
import java.nio.charset.Charset;
@@ -42,8 +59,11 @@ import java.sql.SQLException;
import java.sql.SQLSyntaxErrorException;
import java.sql.SQLWarning;
import java.sql.Statement;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
import static org.thingsboard.server.service.install.DatabaseHelper.ADDITIONAL_INFO;
import static org.thingsboard.server.service.install.DatabaseHelper.ASSIGNED_CUSTOMERS;
@@ -101,6 +121,17 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
@Autowired
private ApiUsageStateService apiUsageStateService;
+ @Autowired
+ private QueueService queueService;
+
+ @Autowired
+ private TbRuleEngineQueueConfigService queueConfig;
+
+ @Autowired
+ private RuleChainService ruleChainService;
+
+ @Autowired
+ private TenantProfileService tenantProfileService;
@Override
public void upgradeDatabase(String fromVersion) throws Exception {
@@ -534,6 +565,39 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
log.error("Failed updating schema!!!", e);
}
break;
+ case "3.3.4":
+ try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
+ log.info("Updating schema ...");
+ schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.4", SCHEMA_UPDATE_SQL);
+ loadSql(schemaUpdateFile, conn);
+
+ log.info("Loading queues...");
+ try {
+ if (!CollectionUtils.isEmpty(queueConfig.getQueues())) {
+ queueConfig.getQueues().forEach(queueSettings -> {
+ Queue queue = queueConfigToQueue(queueSettings);
+ Queue existing = queueService.findQueueByTenantIdAndName(queue.getTenantId(), queue.getName());
+ if (existing == null) {
+ queueService.saveQueue(queue);
+ }
+ });
+ } else {
+ systemDataLoaderService.createQueues();
+ }
+ } catch (Exception e) {
+ }
+
+ log.info("Updating device profiles...");
+ schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.4", "schema_update_device_profile.sql");
+ loadSql(schemaUpdateFile, conn);
+
+ log.info("Updating schema settings...");
+ conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3004000;");
+ log.info("Schema updated.");
+ } catch (Exception e) {
+ log.error("Failed updating schema!!!", e);
+ }
+ break;
default:
throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
}
@@ -579,4 +643,30 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
}
return isOldSchema;
}
+
+ private Queue queueConfigToQueue(TbRuleEngineQueueConfiguration queueSettings) {
+ Queue queue = new Queue();
+ queue.setTenantId(TenantId.SYS_TENANT_ID);
+ queue.setName(queueSettings.getName());
+ queue.setTopic(queueSettings.getTopic());
+ queue.setPollInterval(queueSettings.getPollInterval());
+ queue.setPartitions(queueSettings.getPartitions());
+ queue.setPackProcessingTimeout(queueSettings.getPackProcessingTimeout());
+ SubmitStrategy submitStrategy = new SubmitStrategy();
+ submitStrategy.setBatchSize(queueSettings.getSubmitStrategy().getBatchSize());
+ submitStrategy.setType(SubmitStrategyType.valueOf(queueSettings.getSubmitStrategy().getType()));
+ queue.setSubmitStrategy(submitStrategy);
+ ProcessingStrategy processingStrategy = new ProcessingStrategy();
+ processingStrategy.setType(ProcessingStrategyType.valueOf(queueSettings.getProcessingStrategy().getType()));
+ processingStrategy.setRetries(queueSettings.getProcessingStrategy().getRetries());
+ processingStrategy.setFailurePercentage(queueSettings.getProcessingStrategy().getFailurePercentage());
+ processingStrategy.setPauseBetweenRetries(queueSettings.getProcessingStrategy().getPauseBetweenRetries());
+ processingStrategy.setMaxPauseBetweenRetries(queueSettings.getProcessingStrategy().getMaxPauseBetweenRetries());
+ queue.setProcessingStrategy(processingStrategy);
+ queue.setConsumerPerPartition(queueSettings.isConsumerPerPartition());
+ return queue;
+ }
+
+
+
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/PsqlEntityDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlEntityDatabaseSchemaService.java
similarity index 88%
rename from application/src/main/java/org/thingsboard/server/service/install/PsqlEntityDatabaseSchemaService.java
rename to application/src/main/java/org/thingsboard/server/service/install/SqlEntityDatabaseSchemaService.java
index eaa9d708be..7ae155402a 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/PsqlEntityDatabaseSchemaService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/SqlEntityDatabaseSchemaService.java
@@ -18,19 +18,17 @@ package org.thingsboard.server.service.install;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
-import org.thingsboard.server.dao.util.PsqlDao;
@Service
-@PsqlDao
@Profile("install")
@Slf4j
-public class PsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
+public class SqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
implements EntityDatabaseSchemaService {
public static final String SCHEMA_ENTITIES_SQL = "schema-entities.sql";
public static final String SCHEMA_ENTITIES_IDX_SQL = "schema-entities-idx.sql";
public static final String SCHEMA_ENTITIES_IDX_PSQL_ADDON_SQL = "schema-entities-idx-psql-addon.sql";
- public PsqlEntityDatabaseSchemaService() {
+ public SqlEntityDatabaseSchemaService() {
super(SCHEMA_ENTITIES_SQL, SCHEMA_ENTITIES_IDX_SQL);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseSchemaService.java
similarity index 85%
rename from application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseSchemaService.java
rename to application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseSchemaService.java
index 0e7cff7d63..f3ede624ff 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseSchemaService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseSchemaService.java
@@ -18,19 +18,17 @@ package org.thingsboard.server.service.install;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
-import org.thingsboard.server.dao.util.PsqlDao;
import org.thingsboard.server.dao.util.SqlTsDao;
@Service
@SqlTsDao
-@PsqlDao
@Profile("install")
-public class PsqlTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaService implements TsDatabaseSchemaService {
+public class SqlTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaService implements TsDatabaseSchemaService {
@Value("${sql.postgres.ts_key_value_partitioning:MONTHS}")
private String partitionType;
- public PsqlTsDatabaseSchemaService() {
+ public SqlTsDatabaseSchemaService() {
super("schema-ts-psql.sql", null);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseUpgradeService.java
similarity index 98%
rename from application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java
rename to application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseUpgradeService.java
index 55fa73fb27..c1d41a6f6e 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseUpgradeService.java
@@ -21,7 +21,6 @@ import org.apache.commons.lang3.SystemUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
-import org.thingsboard.server.dao.util.PsqlDao;
import org.thingsboard.server.dao.util.SqlTsDao;
import java.io.File;
@@ -36,8 +35,7 @@ import java.sql.DriverManager;
@Profile("install")
@Slf4j
@SqlTsDao
-@PsqlDao
-public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeService implements DatabaseTsUpgradeService {
+public class SqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeService implements DatabaseTsUpgradeService {
@Value("${sql.postgres.ts_key_value_partitioning:MONTHS}")
private String partitionType;
diff --git a/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java
index aca458bd15..1ceb1be289 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java
@@ -33,4 +33,5 @@ public interface SystemDataLoaderService {
void deleteSystemWidgetBundle(String bundleAlias) throws Exception;
+ void createQueues();
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/TbRuleEngineQueueConfigService.java b/application/src/main/java/org/thingsboard/server/service/install/TbRuleEngineQueueConfigService.java
new file mode 100644
index 0000000000..1125467082
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/install/TbRuleEngineQueueConfigService.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.install;
+
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
+
+import javax.annotation.PostConstruct;
+import java.util.List;
+
+@Slf4j
+@Data
+@EnableAutoConfiguration
+@Configuration
+@ConfigurationProperties(prefix = "queue.rule-engine")
+@Profile("install")
+public class TbRuleEngineQueueConfigService {
+
+ private String topic;
+ private List queues;
+
+ @PostConstruct
+ public void validate() {
+ queues.stream().filter(queue -> queue.getName().equals("Main")).findFirst().orElseThrow(() -> {
+ log.error("Main queue is not configured in thingsboard.yml");
+ return new RuntimeException("No \"Main\" queue configured!");
+ });
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseSchemaService.java
index a0257ba0e4..409498cfd3 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseSchemaService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseSchemaService.java
@@ -19,16 +19,10 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
-import org.thingsboard.server.dao.util.PsqlDao;
import org.thingsboard.server.dao.util.TimescaleDBTsDao;
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
@Service
@TimescaleDBTsDao
-@PsqlDao
@Profile("install")
@Slf4j
public class TimescaleTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaService implements TsDatabaseSchemaService {
diff --git a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java
index 1834ca6552..245dc4503d 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java
@@ -22,7 +22,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
-import org.thingsboard.server.dao.util.PsqlDao;
import org.thingsboard.server.dao.util.TimescaleDBTsDao;
import java.io.File;
@@ -37,7 +36,6 @@ import java.sql.DriverManager;
@Profile("install")
@Slf4j
@TimescaleDBTsDao
-@PsqlDao
public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeService implements DatabaseTsUpgradeService {
@Value("${sql.timescale.chunk_time_interval:86400000}")
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 93c4bb835e..e27f2e88c2 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
@@ -21,6 +21,7 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@@ -31,14 +32,12 @@ import org.thingsboard.rule.engine.profile.TbDeviceProfileNode;
import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.Tenant;
+import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQuery;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
-import org.thingsboard.server.common.data.id.EntityViewId;
-import org.thingsboard.server.common.data.id.RuleChainId;
-import org.thingsboard.server.common.data.id.RuleNodeId;
-import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry;
@@ -47,12 +46,14 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.query.DynamicValue;
import org.thingsboard.server.common.data.query.FilterPredicateValue;
+import org.thingsboard.server.common.data.queue.*;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleNode;
+import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.alarm.AlarmDao;
import org.thingsboard.server.dao.alarm.AlarmService;
@@ -61,16 +62,22 @@ import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.model.sql.DeviceProfileEntity;
import org.thingsboard.server.dao.model.sql.RelationEntity;
import org.thingsboard.server.dao.oauth2.OAuth2Service;
+import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.sql.device.DeviceProfileRepository;
+import org.thingsboard.server.dao.tenant.TenantProfileService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
+import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
import org.thingsboard.server.service.install.InstallScripts;
+import org.thingsboard.server.service.install.SystemDataLoaderService;
+import org.thingsboard.server.service.install.TbRuleEngineQueueConfigService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
@@ -115,6 +122,18 @@ public class DefaultDataUpdateService implements DataUpdateService {
@Autowired
private OAuth2Service oAuth2Service;
+ @Autowired
+ private TenantProfileService tenantProfileService;
+
+ @Autowired
+ private QueueService queueService;
+
+ @Autowired
+ private TbRuleEngineQueueConfigService queueConfig;
+
+ @Autowired
+ private SystemDataLoaderService systemDataLoaderService;
+
@Override
public void updateData(String fromVersion) throws Exception {
switch (fromVersion) {
@@ -141,6 +160,11 @@ public class DefaultDataUpdateService implements DataUpdateService {
log.info("Updating data from version 3.3.2 to 3.3.3 ...");
updateNestedRuleChains();
break;
+ case "3.3.4":
+ log.info("Updating data from version 3.3.4 to 3.4.0 ...");
+ tenantsProfileQueueConfigurationUpdater.updateEntities(null);
+ checkPointRuleNodesUpdater.updateEntities(null);
+ break;
default:
throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
}
@@ -547,4 +571,110 @@ public class DefaultDataUpdateService implements DataUpdateService {
log.warn("CAUTION: Update of Oauth2 parameters from 3.2.2 to 3.3.0 available only in ThingsBoard versions 3.3.0/3.3.1");
}
+ private final PaginatedUpdater tenantsProfileQueueConfigurationUpdater =
+ new PaginatedUpdater<>() {
+
+ @Override
+ protected String getName() {
+ return "Tenant profiles queue configuration updater";
+ }
+
+ @Override
+ protected boolean forceReportTotal() {
+ return true;
+ }
+
+ @Override
+ protected PageData findEntities(String id, PageLink pageLink) {
+ return tenantProfileService.findTenantProfiles(TenantId.SYS_TENANT_ID, pageLink);
+ }
+
+ @Override
+ protected void updateEntity(TenantProfile tenantProfile) {
+ updateTenantProfileQueueConfiguration(tenantProfile);
+ }
+ };
+
+ private void updateTenantProfileQueueConfiguration(TenantProfile profile) {
+ try {
+ List queueConfiguration = profile.getProfileData().getQueueConfiguration();
+ if (profile.isIsolatedTbRuleEngine() && (queueConfiguration == null || queueConfiguration.isEmpty())) {
+ TenantProfileQueueConfiguration mainQueueConfig = getMainQueueConfiguration();
+ profile.getProfileData().setQueueConfiguration(Collections.singletonList((mainQueueConfig)));
+ tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, profile);
+ List isolatedTenants = tenantService.findTenantIdsByTenantProfileId(profile.getId());
+ isolatedTenants.forEach(tenantId -> {
+ queueService.saveQueue(new Queue(tenantId, mainQueueConfig));
+ });
+ }
+ } catch (Exception e) {
+ log.error("Failed to update tenant profile queue configuration name=["+profile.getName()+"], id=["+ profile.getId().getId() +"]", e);
+ }
+ }
+
+ private TenantProfileQueueConfiguration getMainQueueConfiguration() {
+ TenantProfileQueueConfiguration mainQueueConfiguration = new TenantProfileQueueConfiguration();
+ mainQueueConfiguration.setName("Main");
+ mainQueueConfiguration.setTopic("tb_rule_engine.main");
+ mainQueueConfiguration.setPollInterval(25);
+ mainQueueConfiguration.setPartitions(10);
+ mainQueueConfiguration.setConsumerPerPartition(true);
+ mainQueueConfiguration.setPackProcessingTimeout(2000);
+ SubmitStrategy mainQueueSubmitStrategy = new SubmitStrategy();
+ mainQueueSubmitStrategy.setType(SubmitStrategyType.BURST);
+ mainQueueSubmitStrategy.setBatchSize(1000);
+ mainQueueConfiguration.setSubmitStrategy(mainQueueSubmitStrategy);
+ ProcessingStrategy mainQueueProcessingStrategy = new ProcessingStrategy();
+ mainQueueProcessingStrategy.setType(ProcessingStrategyType.SKIP_ALL_FAILURES);
+ mainQueueProcessingStrategy.setRetries(3);
+ mainQueueProcessingStrategy.setFailurePercentage(0);
+ mainQueueProcessingStrategy.setPauseBetweenRetries(3);
+ mainQueueProcessingStrategy.setMaxPauseBetweenRetries(3);
+ mainQueueConfiguration.setProcessingStrategy(mainQueueProcessingStrategy);
+ return mainQueueConfiguration;
+ }
+
+ private final PaginatedUpdater checkPointRuleNodesUpdater =
+ new PaginatedUpdater<>() {
+
+ @Override
+ protected String getName() {
+ return "Checkpoint rule nodes updater";
+ }
+
+ @Override
+ protected boolean forceReportTotal() {
+ return true;
+ }
+
+ @Override
+ protected PageData findEntities(String id, PageLink pageLink) {
+ return ruleChainService.findAllRuleNodesByType("org.thingsboard.rule.engine.flow.TbCheckpointNode", pageLink);
+ }
+
+ @Override
+ protected void updateEntity(RuleNode ruleNode) {
+ updateCheckPointRuleNodeConfiguration(ruleNode);
+ }
+ };
+
+ private void updateCheckPointRuleNodeConfiguration(RuleNode node) {
+ try {
+ ObjectNode configuration = (ObjectNode) node.getConfiguration();
+ JsonNode queueNameNode = configuration.remove("queueName");
+ if (queueNameNode != null) {
+ RuleChain ruleChain = this.ruleChainService.findRuleChainById(TenantId.SYS_TENANT_ID, node.getRuleChainId());
+ TenantId tenantId = ruleChain.getTenantId();
+ Map queues =
+ queueService.findQueuesByTenantId(tenantId).stream().collect(Collectors.toMap(Queue::getName, Queue::getId));
+ String queueName = queueNameNode.asText();
+ QueueId queueId = queues.get(queueName);
+ configuration.put("queueId", queueId != null ? queueId.toString() : "");
+ ruleChainService.saveRuleNode(tenantId, node);
+ }
+ } catch (Exception e) {
+ log.error("Failed to update checkpoint rule node configuration name=["+node.getName()+"], id=["+ node.getId().getId() +"]", e);
+ }
+ }
+
}
diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
index fa7cb2b7ff..7e1a3d7fa7 100644
--- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
+++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
@@ -40,7 +40,6 @@ import org.thingsboard.server.common.data.ApiUsageStateValue;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
-import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.settings.AdminSettingsService;
@@ -48,6 +47,7 @@ import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import javax.annotation.PostConstruct;
+import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.ByteArrayInputStream;
import java.util.HashMap;
@@ -101,7 +101,7 @@ public class DefaultMailService implements MailService {
mailSender = createMailSender(jsonConfig);
mailFrom = jsonConfig.get("mailFrom").asText();
} else {
- throw new IncorrectParameterException("Failed to date mail configuration. Settings not found!");
+ throw new IncorrectParameterException("Failed to update mail configuration. Settings not found!");
}
}
@@ -311,6 +311,18 @@ public class DefaultMailService implements MailService {
sendMail(mailSender, mailFrom, email, subject, message);
}
+ @Override
+ public void sendTwoFaVerificationEmail(String email, String verificationCode, int expirationTimeSeconds) throws ThingsboardException {
+ String subject = messages.getMessage("2fa.verification.code.subject", null, Locale.US);
+ String message = mergeTemplateIntoString("2fa.verification.code.ftl", Map.of(
+ TARGET_EMAIL, email,
+ "code", verificationCode,
+ "expirationTimeSeconds", expirationTimeSeconds
+ ));
+
+ sendMail(mailSender, mailFrom, email, subject, message);
+ }
+
@Override
public void sendApiFeatureStateEmail(ApiFeature apiFeature, ApiUsageStateValue stateValue, String email, ApiUsageStateMailMessage msg) throws ThingsboardException {
String subject = messages.getMessage("api.usage.state", null, Locale.US);
@@ -338,6 +350,11 @@ public class DefaultMailService implements MailService {
sendMail(mailSender, mailFrom, email, subject, message);
}
+ @Override
+ public void testConnection(TenantId tenantId) throws Exception {
+ mailSender.testConnection();
+ }
+
private String toEnabledValueLabel(ApiFeature apiFeature) {
switch (apiFeature) {
case DB:
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultQueueRoutingInfoService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultQueueRoutingInfoService.java
new file mode 100644
index 0000000000..d7a333a090
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultQueueRoutingInfoService.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.queue;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.dao.queue.QueueService;
+import org.thingsboard.server.queue.discovery.QueueRoutingInfo;
+import org.thingsboard.server.queue.discovery.QueueRoutingInfoService;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@ConditionalOnExpression("'${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core' || '${service.type:null}'=='tb-rule-engine'")
+public class DefaultQueueRoutingInfoService implements QueueRoutingInfoService {
+
+ private final QueueService queueService;
+
+ public DefaultQueueRoutingInfoService(QueueService queueService) {
+ this.queueService = queueService;
+ }
+
+ @Override
+ public List getAllQueuesRoutingInfo() {
+ return queueService.findAllQueues().stream().map(QueueRoutingInfo::new).collect(Collectors.toList());
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java
index 4344507dd9..2806c35cda 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java
@@ -18,36 +18,41 @@ package org.thingsboard.server.service.queue;
import com.google.protobuf.ByteString;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.cluster.TbClusterService;
-import org.thingsboard.server.common.data.EdgeUtils;
-import org.thingsboard.server.common.data.edge.EdgeEventActionType;
-import org.thingsboard.server.common.data.edge.EdgeEventType;
-import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
+import org.thingsboard.server.common.data.edge.EdgeEventActionType;
+import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.QueueId;
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.data.queue.Queue;
import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
+import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto;
@@ -60,12 +65,12 @@ import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueProducer;
import org.thingsboard.server.queue.common.MultipleTbQueueCallbackWrapper;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
+import org.thingsboard.server.queue.discovery.NotificationsTopicService;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.service.gateway_device.GatewayNotificationsService;
import org.thingsboard.server.service.ota.OtaPackageStateService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
-import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import java.util.HashSet;
import java.util.Set;
@@ -81,6 +86,8 @@ public class DefaultTbClusterService implements TbClusterService {
private boolean statsEnabled;
@Value("${edges.enabled}")
protected boolean edgesEnabled;
+ @Value("${service.type:monolith}")
+ private String serviceType;
private final AtomicInteger toCoreMsgs = new AtomicInteger(0);
private final AtomicInteger toCoreNfs = new AtomicInteger(0);
@@ -88,11 +95,21 @@ public class DefaultTbClusterService implements TbClusterService {
private final AtomicInteger toRuleEngineNfs = new AtomicInteger(0);
private final AtomicInteger toTransportNfs = new AtomicInteger(0);
- private final TbQueueProducerProvider producerProvider;
- private final PartitionService partitionService;
+ @Autowired
+ @Lazy
+ private PartitionService partitionService;
+
+ @Autowired
+ @Lazy
+ private TbQueueProducerProvider producerProvider;
+
+ @Autowired
+ @Lazy
+ private OtaPackageStateService otaPackageStateService;
+
+ private final NotificationsTopicService notificationsTopicService;
private final DataDecodingEncodingService encodingService;
private final TbDeviceProfileCache deviceProfileCache;
- private final OtaPackageStateService otaPackageStateService;
private final GatewayNotificationsService gatewayNotificationsService;
@Override
@@ -120,7 +137,7 @@ public class DefaultTbClusterService implements TbClusterService {
@Override
public void pushNotificationToCore(String serviceId, FromDeviceRpcResponse response, TbQueueCallback callback) {
- TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_CORE, serviceId);
+ TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId);
log.trace("PUSHING msg: {} to:{}", response, tpi);
FromDeviceRPCResponseProto.Builder builder = FromDeviceRPCResponseProto.newBuilder()
.setRequestIdMSB(response.getId().getMostSignificantBits())
@@ -155,7 +172,7 @@ public class DefaultTbClusterService implements TbClusterService {
tbMsg = transformMsg(tbMsg, deviceProfileCache.get(tenantId, new DeviceProfileId(entityId.getId())));
}
}
- TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tbMsg.getQueueName(), tenantId, entityId);
+ TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tbMsg.getQueueId(), tenantId, entityId);
log.trace("PUSHING msg: {} to:{}", tbMsg, tpi);
ToRuleEngineMsg msg = ToRuleEngineMsg.newBuilder()
.setTenantIdMSB(tenantId.getId().getMostSignificantBits())
@@ -168,16 +185,16 @@ public class DefaultTbClusterService implements TbClusterService {
private TbMsg transformMsg(TbMsg tbMsg, DeviceProfile deviceProfile) {
if (deviceProfile != null) {
RuleChainId targetRuleChainId = deviceProfile.getDefaultRuleChainId();
- String targetQueueName = deviceProfile.getDefaultQueueName();
+ QueueId targetQueueId = deviceProfile.getDefaultQueueId();
boolean isRuleChainTransform = targetRuleChainId != null && !targetRuleChainId.equals(tbMsg.getRuleChainId());
- boolean isQueueTransform = targetQueueName != null && !targetQueueName.equals(tbMsg.getQueueName());
+ boolean isQueueTransform = targetQueueId != null && !targetQueueId.equals(tbMsg.getQueueId());
if (isRuleChainTransform && isQueueTransform) {
- tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId, targetQueueName);
+ tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId, targetQueueId);
} else if (isRuleChainTransform) {
tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId);
} else if (isQueueTransform) {
- tbMsg = TbMsg.transformMsg(tbMsg, targetQueueName);
+ tbMsg = TbMsg.transformMsg(tbMsg, targetQueueId);
}
}
return tbMsg;
@@ -185,7 +202,7 @@ public class DefaultTbClusterService implements TbClusterService {
@Override
public void pushNotificationToRuleEngine(String serviceId, FromDeviceRpcResponse response, TbQueueCallback callback) {
- TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceId);
+ TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceId);
log.trace("PUSHING msg: {} to:{}", response, tpi);
FromDeviceRPCResponseProto.Builder builder = FromDeviceRPCResponseProto.newBuilder()
.setRequestIdMSB(response.getId().getMostSignificantBits())
@@ -199,14 +216,14 @@ public class DefaultTbClusterService implements TbClusterService {
@Override
public void pushNotificationToTransport(String serviceId, ToTransportMsg response, TbQueueCallback callback) {
- if (serviceId == null || serviceId.isEmpty()){
+ if (serviceId == null || serviceId.isEmpty()) {
log.trace("pushNotificationToTransport: skipping message without serviceId [{}], (ToTransportMsg) response [{}]", serviceId, response);
if (callback != null) {
callback.onSuccess(null); //callback that message already sent, no useful payload expected
}
return;
}
- TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_TRANSPORT, serviceId);
+ TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, serviceId);
log.trace("PUSHING msg: {} to:{}", response, tpi);
producerProvider.getTransportNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), response), callback);
toTransportNfs.incrementAndGet();
@@ -314,7 +331,7 @@ public class DefaultTbClusterService implements TbClusterService {
Set tbTransportServices = partitionService.getAllServiceIds(ServiceType.TB_TRANSPORT);
TbQueueCallback proxyCallback = callback != null ? new MultipleTbQueueCallbackWrapper(tbTransportServices.size(), callback) : null;
for (String transportServiceId : tbTransportServices) {
- TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportServiceId);
+ TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportServiceId);
toTransportNfProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), transportMsg), proxyCallback);
toTransportNfs.incrementAndGet();
}
@@ -328,7 +345,7 @@ public class DefaultTbClusterService implements TbClusterService {
TbQueueProducer> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer();
Set tbCoreServices = partitionService.getAllServiceIds(ServiceType.TB_CORE);
for (String serviceId : tbCoreServices) {
- TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_CORE, serviceId);
+ TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId);
ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setEdgeEventUpdateMsg(ByteString.copyFrom(msgBytes)).build();
toCoreNfProducer.send(tpi, new TbProtoQueueMsg<>(msg.getEdgeId().getId(), toCoreMsg), null);
toCoreNfs.incrementAndGet();
@@ -349,7 +366,7 @@ public class DefaultTbClusterService implements TbClusterService {
TbQueueProducer> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer();
Set tbCoreServices = partitionService.getAllServiceIds(ServiceType.TB_CORE);
for (String serviceId : tbCoreServices) {
- TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_CORE, serviceId);
+ TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId);
ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setComponentLifecycleMsg(ByteString.copyFrom(msgBytes)).build();
toCoreNfProducer.send(tpi, new TbProtoQueueMsg<>(msg.getEntityId().getId(), toCoreMsg), null);
toCoreNfs.incrementAndGet();
@@ -358,7 +375,7 @@ public class DefaultTbClusterService implements TbClusterService {
tbRuleEngineServices.removeAll(tbCoreServices);
}
for (String serviceId : tbRuleEngineServices) {
- TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceId);
+ TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceId);
ToRuleEngineNotificationMsg toRuleEngineMsg = ToRuleEngineNotificationMsg.newBuilder().setComponentLifecycleMsg(ByteString.copyFrom(msgBytes)).build();
toRuleEngineProducer.send(tpi, new TbProtoQueueMsg<>(msg.getEntityId().getId(), toRuleEngineMsg), null);
toRuleEngineNfs.incrementAndGet();
@@ -473,4 +490,65 @@ public class DefaultTbClusterService implements TbClusterService {
break;
}
}
+
+ @Override
+ public void onQueueChange(Queue queue) {
+ log.trace("[{}][{}] Processing queue change [{}] event", queue.getTenantId(), queue.getId(), queue.getName());
+
+ TransportProtos.QueueUpdateMsg queueUpdateMsg = TransportProtos.QueueUpdateMsg.newBuilder()
+ .setTenantIdMSB(queue.getTenantId().getId().getMostSignificantBits())
+ .setTenantIdLSB(queue.getTenantId().getId().getLeastSignificantBits())
+ .setQueueIdMSB(queue.getId().getId().getMostSignificantBits())
+ .setQueueIdLSB(queue.getId().getId().getLeastSignificantBits())
+ .setQueueName(queue.getName())
+ .setQueueTopic(queue.getTopic())
+ .setPartitions(queue.getPartitions())
+ .build();
+
+ ToRuleEngineNotificationMsg ruleEngineMsg = ToRuleEngineNotificationMsg.newBuilder().setQueueUpdateMsg(queueUpdateMsg).build();
+ ToCoreNotificationMsg coreMsg = ToCoreNotificationMsg.newBuilder().setQueueUpdateMsg(queueUpdateMsg).build();
+ ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setQueueUpdateMsg(queueUpdateMsg).build();
+ doSendQueueNotifications(ruleEngineMsg, coreMsg, transportMsg);
+ }
+
+ @Override
+ public void onQueueDelete(Queue queue) {
+ log.trace("[{}][{}] Processing queue delete [{}] event", queue.getTenantId(), queue.getId(), queue.getName());
+
+ TransportProtos.QueueDeleteMsg queueDeleteMsg = TransportProtos.QueueDeleteMsg.newBuilder()
+ .setTenantIdMSB(queue.getTenantId().getId().getMostSignificantBits())
+ .setTenantIdLSB(queue.getTenantId().getId().getLeastSignificantBits())
+ .setQueueIdMSB(queue.getId().getId().getMostSignificantBits())
+ .setQueueIdLSB(queue.getId().getId().getLeastSignificantBits())
+ .setQueueName(queue.getName())
+ .build();
+
+ ToRuleEngineNotificationMsg ruleEngineMsg = ToRuleEngineNotificationMsg.newBuilder().setQueueDeleteMsg(queueDeleteMsg).build();
+ ToCoreNotificationMsg coreMsg = ToCoreNotificationMsg.newBuilder().setQueueDeleteMsg(queueDeleteMsg).build();
+ ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setQueueDeleteMsg(queueDeleteMsg).build();
+ doSendQueueNotifications(ruleEngineMsg, coreMsg, transportMsg);
+ }
+
+ private void doSendQueueNotifications(ToRuleEngineNotificationMsg ruleEngineMsg, ToCoreNotificationMsg coreMsg, ToTransportMsg transportMsg) {
+ Set tbRuleEngineServices = partitionService.getAllServices(ServiceType.TB_RULE_ENGINE);
+ for (TransportProtos.ServiceInfo ruleEngineService : tbRuleEngineServices) {
+ TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, ruleEngineService.getServiceId());
+ producerProvider.getRuleEngineNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), ruleEngineMsg), null);
+ toRuleEngineNfs.incrementAndGet();
+ }
+ if (!serviceType.equals("monolith")) {
+ Set tbCoreServices = partitionService.getAllServices(ServiceType.TB_CORE);
+ for (TransportProtos.ServiceInfo coreService : tbCoreServices) {
+ TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, coreService.getServiceId());
+ producerProvider.getTbCoreNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), coreMsg), null);
+ toCoreNfs.incrementAndGet();
+ }
+ Set tbTransportServices = partitionService.getAllServices(ServiceType.TB_TRANSPORT);
+ for (TransportProtos.ServiceInfo transportService : tbTransportServices) {
+ TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportService.getServiceId());
+ producerProvider.getTransportNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), transportMsg), null);
+ toTransportNfs.incrementAndGet();
+ }
+ }
+ }
}
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 f1bd296e5f..2ca47d689f 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
@@ -38,6 +38,7 @@ import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.stats.StatsFactory;
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
+import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto;
import org.thingsboard.server.gen.transport.TransportProtos.EdgeNotificationMsgProto;
import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto;
@@ -57,8 +58,10 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceM
import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
import org.thingsboard.server.queue.TbQueueConsumer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
+import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
+import org.thingsboard.server.queue.util.AfterStartUp;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.edge.EdgeNotificationService;
@@ -135,8 +138,8 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService>> consumers = new ConcurrentHashMap<>();
- private final ConcurrentMap consumerConfigurations = new ConcurrentHashMap<>();
- private final ConcurrentMap consumerStats = new ConcurrentHashMap<>();
- private final ConcurrentMap topicsConsumerPerPartition = new ConcurrentHashMap<>();
+ private final TbServiceInfoProvider serviceInfoProvider;
+ private final QueueService queueService;
+ // private final TenantId tenantId;
+ private final ConcurrentMap>> consumers = new ConcurrentHashMap<>();
+ private final ConcurrentMap consumerConfigurations = new ConcurrentHashMap<>();
+ private final ConcurrentMap consumerStats = new ConcurrentHashMap<>();
+ private final ConcurrentMap topicsConsumerPerPartition = new ConcurrentHashMap<>();
final ExecutorService submitExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-rule-engine-consumer-submit"));
final ScheduledExecutorService repartitionExecutor = Executors.newScheduledThreadPool(1, ThingsBoardThreadFactory.forName("tb-rule-engine-consumer-repartition"));
public DefaultTbRuleEngineConsumerService(TbRuleEngineProcessingStrategyFactory processingStrategyFactory,
TbRuleEngineSubmitStrategyFactory submitStrategyFactory,
- TbQueueRuleEngineSettings ruleEngineSettings,
TbRuleEngineQueueFactory tbRuleEngineQueueFactory,
RuleEngineStatisticsService statisticsService,
ActorSystemContext actorContext,
@@ -117,28 +123,36 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
StatsFactory statsFactory,
TbDeviceProfileCache deviceProfileCache,
TbTenantProfileCache tenantProfileCache,
- TbApiUsageStateService apiUsageStateService) {
- super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, apiUsageStateService, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer());
+ TbApiUsageStateService apiUsageStateService,
+ PartitionService partitionService, TbServiceInfoProvider serviceInfoProvider, QueueService queueService) {
+ super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, apiUsageStateService, partitionService, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer());
this.statisticsService = statisticsService;
- this.ruleEngineSettings = ruleEngineSettings;
this.tbRuleEngineQueueFactory = tbRuleEngineQueueFactory;
this.submitStrategyFactory = submitStrategyFactory;
this.processingStrategyFactory = processingStrategyFactory;
this.tbDeviceRpcService = tbDeviceRpcService;
this.statsFactory = statsFactory;
+ this.serviceInfoProvider = serviceInfoProvider;
+ this.queueService = queueService;
}
@PostConstruct
public void init() {
super.init("tb-rule-engine-consumer", "tb-rule-engine-notifications-consumer");
- for (TbRuleEngineQueueConfiguration configuration : ruleEngineSettings.getQueues()) {
- consumerConfigurations.putIfAbsent(configuration.getName(), configuration);
- consumerStats.put(configuration.getName(), new TbRuleEngineConsumerStats(configuration.getName(), statsFactory));
- if (!configuration.isConsumerPerPartition()) {
- consumers.computeIfAbsent(configuration.getName(), queueName -> tbRuleEngineQueueFactory.createToRuleEngineMsgConsumer(configuration));
- } else {
- topicsConsumerPerPartition.computeIfAbsent(configuration.getName(), TbTopicWithConsumerPerPartition::new);
- }
+ List queues = queueService.findAllQueues();
+ for (Queue configuration : queues) {
+ initConsumer(configuration);
+ }
+ }
+
+ private void initConsumer(Queue configuration) {
+ QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, configuration);
+ consumerConfigurations.putIfAbsent(queueKey, configuration);
+ consumerStats.putIfAbsent(queueKey, new TbRuleEngineConsumerStats(configuration.getName(), statsFactory));
+ if (!configuration.isConsumerPerPartition()) {
+ consumers.computeIfAbsent(queueKey, queueName -> tbRuleEngineQueueFactory.createToRuleEngineMsgConsumer(configuration));
+ } else {
+ topicsConsumerPerPartition.computeIfAbsent(queueKey, k -> new TbTopicWithConsumerPerPartition(k.getQueueName()));
}
}
@@ -147,38 +161,37 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
super.destroy();
submitExecutor.shutdownNow();
repartitionExecutor.shutdownNow();
- ruleEngineSettings.getQueues().forEach(config -> consumerConfigurations.put(config.getName(), config));
}
@Override
protected void onTbApplicationEvent(PartitionChangeEvent event) {
if (event.getServiceType().equals(getServiceType())) {
- ServiceQueue serviceQueue = event.getServiceQueueKey().getServiceQueue();
- log.info("[{}] Subscribing to partitions: {}", serviceQueue.getQueue(), event.getPartitions());
- if (!consumerConfigurations.get(serviceQueue.getQueue()).isConsumerPerPartition()) {
- consumers.get(serviceQueue.getQueue()).subscribe(event.getPartitions());
+ String serviceQueue = event.getQueueKey().getQueueName();
+ log.info("[{}] Subscribing to partitions: {}", serviceQueue, event.getPartitions());
+ if (!consumerConfigurations.get(event.getQueueKey()).isConsumerPerPartition()) {
+ consumers.get(event.getQueueKey()).subscribe(event.getPartitions());
} else {
- log.info("[{}] Subscribing consumer per partition: {}", serviceQueue.getQueue(), event.getPartitions());
- subscribeConsumerPerPartition(serviceQueue.getQueue(), event.getPartitions());
+ log.info("[{}] Subscribing consumer per partition: {}", serviceQueue, event.getPartitions());
+ subscribeConsumerPerPartition(event.getQueueKey(), event.getPartitions());
}
}
}
- void subscribeConsumerPerPartition(String queue, Set partitions) {
+ void subscribeConsumerPerPartition(QueueKey queue, Set partitions) {
topicsConsumerPerPartition.get(queue).getSubscribeQueue().add(partitions);
scheduleTopicRepartition(queue);
}
- private void scheduleTopicRepartition(String queue) {
+ private void scheduleTopicRepartition(QueueKey queue) {
repartitionExecutor.schedule(() -> repartitionTopicWithConsumerPerPartition(queue), 1, TimeUnit.SECONDS);
}
- void repartitionTopicWithConsumerPerPartition(final String queueName) {
+ void repartitionTopicWithConsumerPerPartition(final QueueKey queueKey) {
if (stopped) {
return;
}
- TbTopicWithConsumerPerPartition tbTopicWithConsumerPerPartition = topicsConsumerPerPartition.get(queueName);
- Queue> subscribeQueue = tbTopicWithConsumerPerPartition.getSubscribeQueue();
+ TbTopicWithConsumerPerPartition tbTopicWithConsumerPerPartition = topicsConsumerPerPartition.get(queueKey);
+ java.util.Queue> subscribeQueue = tbTopicWithConsumerPerPartition.getSubscribeQueue();
if (subscribeQueue.isEmpty()) {
return;
}
@@ -202,15 +215,15 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
log.info("calculated removedPartitions {}", removedPartitions);
removedPartitions.forEach((tpi) -> {
- removeConsumerForTopicByTpi(queueName, consumers, tpi);
+ removeConsumerForTopicByTpi(queueKey.getQueueName(), consumers, tpi);
});
addedPartitions.forEach((tpi) -> {
- log.info("[{}] Adding consumer for topic: {}", queueName, tpi);
- TbRuleEngineQueueConfiguration configuration = consumerConfigurations.get(queueName);
+ log.info("[{}] Adding consumer for topic: {}", queueKey, tpi);
+ Queue configuration = consumerConfigurations.get(queueKey);
TbQueueConsumer> consumer = tbRuleEngineQueueFactory.createToRuleEngineMsgConsumer(configuration);
consumers.put(tpi, consumer);
- launchConsumer(consumer, consumerConfigurations.get(queueName), consumerStats.get(queueName), "" + queueName + "-" + tpi.getPartition().orElse(-999999));
+ launchConsumer(consumer, consumerConfigurations.get(queueKey), consumerStats.get(queueKey), "" + queueKey + "-" + tpi.getPartition().orElse(-999999));
consumer.subscribe(Collections.singleton(tpi));
});
@@ -218,7 +231,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
tbTopicWithConsumerPerPartition.getLock().unlock();
}
} else {
- scheduleTopicRepartition(queueName); //reschedule later
+ scheduleTopicRepartition(queueKey); //reschedule later
}
}
@@ -231,7 +244,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
@Override
protected void launchMainConsumers() {
- consumers.forEach((queue, consumer) -> launchConsumer(consumer, consumerConfigurations.get(queue), consumerStats.get(queue), queue));
+ consumers.forEach((queue, consumer) -> launchConsumer(consumer, consumerConfigurations.get(queue), consumerStats.get(queue), queue.getQueueName()));
}
@Override
@@ -241,11 +254,11 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
.forEach((tpi) -> removeConsumerForTopicByTpi(tbTopicWithConsumerPerPartition.getTopic(), tbTopicWithConsumerPerPartition.getConsumers(), tpi)));
}
- void launchConsumer(TbQueueConsumer> consumer, TbRuleEngineQueueConfiguration configuration, TbRuleEngineConsumerStats stats, String threadSuffix) {
+ void launchConsumer(TbQueueConsumer> consumer, Queue configuration, TbRuleEngineConsumerStats stats, String threadSuffix) {
consumersExecutor.execute(() -> consumerLoop(consumer, configuration, stats, threadSuffix));
}
- void consumerLoop(TbQueueConsumer> consumer, TbRuleEngineQueueConfiguration configuration, TbRuleEngineConsumerStats stats, String threadSuffix) {
+ void consumerLoop(TbQueueConsumer> consumer, org.thingsboard.server.common.data.queue.Queue configuration, TbRuleEngineConsumerStats stats, String threadSuffix) {
updateCurrentThreadName(threadSuffix);
while (!stopped && !consumer.isStopped()) {
try {
@@ -256,13 +269,13 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
final TbRuleEngineSubmitStrategy submitStrategy = getSubmitStrategy(configuration);
final TbRuleEngineProcessingStrategy ackStrategy = getAckStrategy(configuration);
submitStrategy.init(msgs);
- while (!stopped) {
+ while (!stopped && !consumer.isStopped()) {
TbMsgPackProcessingContext ctx = new TbMsgPackProcessingContext(configuration.getName(), submitStrategy, ackStrategy.isSkipTimeoutMsgs());
submitStrategy.submitAttempt((id, msg) -> submitExecutor.submit(() -> submitMessage(configuration, stats, ctx, id, msg)));
final boolean timeout = !ctx.await(configuration.getPackProcessingTimeout(), TimeUnit.MILLISECONDS);
- TbRuleEngineProcessingResult result = new TbRuleEngineProcessingResult(configuration.getName(), timeout, ctx);
+ TbRuleEngineProcessingResult result = new TbRuleEngineProcessingResult(configuration.getId(), timeout, ctx);
if (timeout) {
printFirstOrAll(configuration, ctx, ctx.getPendingMap(), "Timeout");
}
@@ -310,15 +323,15 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
Thread.currentThread().setName(name);
}
- TbRuleEngineProcessingStrategy getAckStrategy(TbRuleEngineQueueConfiguration configuration) {
+ TbRuleEngineProcessingStrategy getAckStrategy(Queue configuration) {
return processingStrategyFactory.newInstance(configuration.getName(), configuration.getProcessingStrategy());
}
- TbRuleEngineSubmitStrategy getSubmitStrategy(TbRuleEngineQueueConfiguration configuration) {
+ TbRuleEngineSubmitStrategy getSubmitStrategy(Queue configuration) {
return submitStrategyFactory.newInstance(configuration.getName(), configuration.getSubmitStrategy());
}
- void submitMessage(TbRuleEngineQueueConfiguration configuration, TbRuleEngineConsumerStats stats, TbMsgPackProcessingContext ctx, UUID id, TbProtoQueueMsg msg) {
+ void submitMessage(Queue configuration, TbRuleEngineConsumerStats stats, TbMsgPackProcessingContext ctx, UUID id, TbProtoQueueMsg msg) {
log.trace("[{}] Creating callback for topic {} message: {}", id, configuration.getName(), msg.getValue());
ToRuleEngineMsg toRuleEngineMsg = msg.getValue();
TenantId tenantId = TenantId.fromUUID(new UUID(toRuleEngineMsg.getTenantIdMSB(), toRuleEngineMsg.getTenantIdLSB()));
@@ -327,7 +340,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
new TbMsgPackCallback(id, tenantId, ctx);
try {
if (toRuleEngineMsg.getTbMsg() != null && !toRuleEngineMsg.getTbMsg().isEmpty()) {
- forwardToRuleEngineActor(configuration.getName(), tenantId, toRuleEngineMsg, callback);
+ forwardToRuleEngineActor(configuration.getId(), tenantId, toRuleEngineMsg, callback);
} else {
callback.onSuccess();
}
@@ -336,12 +349,12 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
}
}
- private void printFirstOrAll(TbRuleEngineQueueConfiguration configuration, TbMsgPackProcessingContext ctx, Map> map, String prefix) {
+ private void printFirstOrAll(Queue configuration, TbMsgPackProcessingContext ctx, Map> map, String prefix) {
boolean printAll = log.isTraceEnabled();
log.info("{} to process [{}] messages", prefix, map.size());
for (Map.Entry> pending : map.entrySet()) {
ToRuleEngineMsg tmp = pending.getValue().getValue();
- TbMsg tmpMsg = TbMsg.fromBytes(configuration.getName(), tmp.getTbMsg().toByteArray(), TbMsgCallback.EMPTY);
+ TbMsg tmpMsg = TbMsg.fromBytes(configuration.getId(), tmp.getTbMsg().toByteArray(), TbMsgCallback.EMPTY);
RuleNodeInfo ruleNodeInfo = ctx.getLastVisitedRuleNode(pending.getKey());
if (printAll) {
log.trace("[{}] {} to process message: {}, Last Rule Node: {}", TenantId.fromUUID(new UUID(tmp.getTenantIdMSB(), tmp.getTenantIdLSB())), prefix, tmpMsg, ruleNodeInfo);
@@ -380,14 +393,77 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
, proto.getResponse(), error);
tbDeviceRpcService.processRpcResponseFromDevice(response);
callback.onSuccess();
+ } else if (nfMsg.hasQueueUpdateMsg()) {
+ repartitionExecutor.execute(() -> updateQueue(nfMsg.getQueueUpdateMsg()));
+ callback.onSuccess();
+ } else if (nfMsg.hasQueueDeleteMsg()) {
+ repartitionExecutor.execute(() -> deleteQueue(nfMsg.getQueueDeleteMsg()));
+ callback.onSuccess();
} else {
log.trace("Received notification with missing handler");
callback.onSuccess();
}
}
- private void forwardToRuleEngineActor(String queueName, TenantId tenantId, ToRuleEngineMsg toRuleEngineMsg, TbMsgCallback callback) {
- TbMsg tbMsg = TbMsg.fromBytes(queueName, toRuleEngineMsg.getTbMsg().toByteArray(), callback);
+ private void updateQueue(TransportProtos.QueueUpdateMsg queueUpdateMsg) {
+ log.info("Received queue update msg: [{}]", queueUpdateMsg);
+ String queueName = queueUpdateMsg.getQueueName();
+ TenantId tenantId = new TenantId(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB()));
+ QueueId queueId = new QueueId(new UUID(queueUpdateMsg.getQueueIdMSB(), queueUpdateMsg.getQueueIdLSB()));
+ QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueUpdateMsg.getQueueName(), tenantId);
+ Queue queue = queueService.findQueueById(tenantId, queueId);
+ Queue oldQueue = consumerConfigurations.remove(queueKey);
+ if (oldQueue != null) {
+ if (oldQueue.isConsumerPerPartition()) {
+ TbTopicWithConsumerPerPartition consumerPerPartition = topicsConsumerPerPartition.remove(queueKey);
+ ReentrantLock lock = consumerPerPartition.getLock();
+ try {
+ lock.lock();
+ consumerPerPartition.getConsumers().values().forEach(TbQueueConsumer::unsubscribe);
+ } finally {
+ lock.unlock();
+ }
+ } else {
+ TbQueueConsumer> consumer = consumers.remove(queueKey);
+ consumer.unsubscribe();
+ }
+ }
+
+ initConsumer(queue);
+
+ if (!queue.isConsumerPerPartition()) {
+ launchConsumer(consumers.get(queueKey), consumerConfigurations.get(queueKey), consumerStats.get(queueKey), queueName);
+ }
+
+ partitionService.updateQueue(queueUpdateMsg);
+ partitionService.recalculatePartitions(serviceInfoProvider.getServiceInfo(), new ArrayList<>(partitionService.getOtherServices(ServiceType.TB_RULE_ENGINE)));
+ }
+
+ private void deleteQueue(TransportProtos.QueueDeleteMsg queueDeleteMsg) {
+ log.info("Received queue delete msg: [{}]", queueDeleteMsg);
+ TenantId tenantId = new TenantId(new UUID(queueDeleteMsg.getTenantIdMSB(), queueDeleteMsg.getTenantIdLSB()));
+ QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueDeleteMsg.getQueueName(), tenantId);
+
+ Queue queue = consumerConfigurations.remove(queueKey);
+ if (queue != null) {
+ if (queue.isConsumerPerPartition()) {
+ TbTopicWithConsumerPerPartition tbTopicWithConsumerPerPartition = topicsConsumerPerPartition.remove(queueKey);
+ if (tbTopicWithConsumerPerPartition != null) {
+ tbTopicWithConsumerPerPartition.getConsumers().values().forEach(TbQueueConsumer::unsubscribe);
+ tbTopicWithConsumerPerPartition.getConsumers().clear();
+ }
+ } else {
+ TbQueueConsumer> consumer = consumers.remove(queueKey);
+ if (consumer != null) {
+ consumer.unsubscribe();
+ }
+ }
+ }
+ partitionService.removeQueue(queueDeleteMsg);
+ }
+
+ private void forwardToRuleEngineActor(QueueId queueId, TenantId tenantId, ToRuleEngineMsg toRuleEngineMsg, TbMsgCallback callback) {
+ TbMsg tbMsg = TbMsg.fromBytes(queueId, toRuleEngineMsg.getTbMsg().toByteArray(), callback);
QueueToRuleEngineMsg msg;
ProtocolStringList relationTypesList = toRuleEngineMsg.getRelationTypesList();
Set relationTypes = null;
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbTopicWithConsumerPerPartition.java b/application/src/main/java/org/thingsboard/server/service/queue/TbTopicWithConsumerPerPartition.java
index b1696dcc1e..e51b345f98 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/TbTopicWithConsumerPerPartition.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/TbTopicWithConsumerPerPartition.java
@@ -15,7 +15,6 @@
*/
package org.thingsboard.server.service.queue;
-import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@@ -25,7 +24,6 @@ import org.thingsboard.server.queue.TbQueueConsumer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import java.util.Collections;
-import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java
index 479691e21b..997674753c 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java
@@ -35,9 +35,11 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.queue.TbQueueConsumer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
+import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
+import org.thingsboard.server.queue.util.AfterStartUp;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
@@ -69,17 +71,20 @@ public abstract class AbstractConsumerService> nfConsumer;
public AbstractConsumerService(ActorSystemContext actorContext, DataDecodingEncodingService encodingService,
TbTenantProfileCache tenantProfileCache, TbDeviceProfileCache deviceProfileCache,
- TbApiUsageStateService apiUsageStateService, TbQueueConsumer> nfConsumer) {
+ TbApiUsageStateService apiUsageStateService, PartitionService partitionService,
+ TbQueueConsumer> nfConsumer) {
this.actorContext = actorContext;
this.encodingService = encodingService;
this.tenantProfileCache = tenantProfileCache;
this.deviceProfileCache = deviceProfileCache;
this.apiUsageStateService = apiUsageStateService;
+ this.partitionService = partitionService;
this.nfConsumer = nfConsumer;
}
@@ -88,8 +93,7 @@ public abstract class AbstractConsumerService log.trace("Going to reprocess [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY)));
+ toReprocess.forEach((id, msg) -> log.trace("Going to reprocess [{}]: {}", id, TbMsg.fromBytes(result.getQueueId(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY)));
}
if (pauseBetweenRetries > 0) {
try {
@@ -164,10 +164,10 @@ public class TbRuleEngineProcessingStrategyFactory {
log.debug("[{}] Reprocessing skipped for {} failed and {} timeout messages", queueName, result.getFailedMap().size(), result.getPendingMap().size());
}
if (log.isTraceEnabled()) {
- result.getFailedMap().forEach((id, msg) -> log.trace("Failed messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY)));
+ result.getFailedMap().forEach((id, msg) -> log.trace("Failed messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueId(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY)));
}
if (log.isTraceEnabled()) {
- result.getPendingMap().forEach((id, msg) -> log.trace("Timeout messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY)));
+ result.getPendingMap().forEach((id, msg) -> log.trace("Timeout messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueId(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY)));
}
return new TbRuleEngineProcessingDecision(true, null);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineSubmitStrategyFactory.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineSubmitStrategyFactory.java
index 3c4d538394..5c4d3b14ab 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineSubmitStrategyFactory.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineSubmitStrategyFactory.java
@@ -17,26 +17,27 @@ package org.thingsboard.server.service.queue.processing;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.data.queue.SubmitStrategy;
import org.thingsboard.server.queue.settings.TbRuleEngineQueueSubmitStrategyConfiguration;
@Component
@Slf4j
public class TbRuleEngineSubmitStrategyFactory {
- public TbRuleEngineSubmitStrategy newInstance(String name, TbRuleEngineQueueSubmitStrategyConfiguration configuration) {
- switch (configuration.getType()) {
- case "BURST":
+ public TbRuleEngineSubmitStrategy newInstance(String name, SubmitStrategy submitStrategy) {
+ switch (submitStrategy.getType()) {
+ case BURST:
return new BurstTbRuleEngineSubmitStrategy(name);
- case "BATCH":
- return new BatchTbRuleEngineSubmitStrategy(name, configuration.getBatchSize());
- case "SEQUENTIAL_BY_ORIGINATOR":
+ case BATCH:
+ return new BatchTbRuleEngineSubmitStrategy(name, submitStrategy.getBatchSize());
+ case SEQUENTIAL_BY_ORIGINATOR:
return new SequentialByOriginatorIdTbRuleEngineSubmitStrategy(name);
- case "SEQUENTIAL_BY_TENANT":
+ case SEQUENTIAL_BY_TENANT:
return new SequentialByTenantIdTbRuleEngineSubmitStrategy(name);
- case "SEQUENTIAL":
+ case SEQUENTIAL:
return new SequentialTbRuleEngineSubmitStrategy(name);
default:
- throw new RuntimeException("TbRuleEngineProcessingStrategy with type " + configuration.getType() + " is not supported!");
+ throw new RuntimeException("TbRuleEngineProcessingStrategy with type " + submitStrategy.getType() + " is not supported!");
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
index fab78afc70..b6ce83a4f4 100644
--- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
+++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
@@ -22,9 +22,11 @@ import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
import org.eclipse.leshan.core.model.InvalidDDFFileException;
import org.eclipse.leshan.core.model.ObjectModel;
import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo;
+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.TbResourceId;
@@ -36,6 +38,9 @@ import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.resource.ResourceService;
+import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
+import org.thingsboard.server.service.security.model.SecurityUser;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -53,7 +58,8 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
@Slf4j
@Service
-public class DefaultTbResourceService implements TbResourceService {
+@TbCoreComponent
+public class DefaultTbResourceService extends AbstractTbEntityService implements TbResourceService {
private final ResourceService resourceService;
private final DDFFileParser ddfFileParser;
@@ -64,7 +70,7 @@ public class DefaultTbResourceService implements TbResourceService {
}
@Override
- public TbResource saveResource(TbResource resource) throws ThingsboardException {
+ public TbResource saveResourceInternal(TbResource resource) throws ThingsboardException {
log.trace("Executing saveResource [{}]", resource);
if (StringUtils.isEmpty(resource.getData())) {
throw new DataValidationException("Resource data should be specified!");
@@ -150,11 +156,6 @@ public class DefaultTbResourceService implements TbResourceService {
.collect(Collectors.toList());
}
- @Override
- public void deleteResource(TenantId tenantId, TbResourceId resourceId) {
- resourceService.deleteResource(tenantId, resourceId);
- }
-
@Override
public void deleteResourcesByTenantId(TenantId tenantId) {
resourceService.deleteResourcesByTenantId(tenantId);
@@ -215,4 +216,37 @@ public class DefaultTbResourceService implements TbResourceService {
return null;
}
}
-}
+
+ @Override
+ public TbResource save(TbResource tbResource, SecurityUser user) throws ThingsboardException {
+ ActionType actionType = tbResource.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
+ TenantId tenantId = tbResource.getTenantId();
+ try {
+
+ TbResource savedResource = checkNotNull(saveResourceInternal(tbResource));
+ tbClusterService.onResourceChange(savedResource, null);
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedResource.getId(),
+ savedResource, user, actionType, false, null);
+ return savedResource;
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.TB_RESOURCE),
+ tbResource, user, actionType, false, e);
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void delete(TbResource tbResource, SecurityUser user) throws ThingsboardException {
+ TbResourceId resourceId = tbResource.getId();
+ TenantId tenantId = tbResource.getTenantId();
+ try {
+ resourceService.deleteResource(tenantId, resourceId);
+ tbClusterService.onResourceDeleted(tbResource, null);
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, resourceId, tbResource, user, ActionType.DELETED,
+ false, null, resourceId.toString());
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.TB_RESOURCE), null, user, ActionType.DELETED,
+ false, e, resourceId.toString());
+ throw handleException(e);
+ }
+ }}
diff --git a/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java
index bfa04d4cd6..3bcb7f0722 100644
--- a/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java
+++ b/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java
@@ -24,12 +24,13 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
import java.util.List;
-public interface TbResourceService {
+public interface TbResourceService extends SimpleTbEntityService {
- TbResource saveResource(TbResource resource) throws ThingsboardException;
+ TbResource saveResourceInternal(TbResource resource) throws ThingsboardException;
TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceKey);
@@ -51,8 +52,6 @@ public interface TbResourceService {
String sortOrder,
PageLink pageLink);
- void deleteResource(TenantId tenantId, TbResourceId resourceId);
-
void deleteResourcesByTenantId(TenantId tenantId);
long sumDataSizeByTenantId(TenantId tenantId);
diff --git a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java
index ae247c0e47..ac82f78ccb 100644
--- a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java
+++ b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java
@@ -22,19 +22,30 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.flow.TbRuleChainInputNode;
import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration;
import org.thingsboard.rule.engine.flow.TbRuleChainOutputNode;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.audit.ActionType;
+import org.thingsboard.server.common.data.edge.Edge;
+import org.thingsboard.server.common.data.edge.EdgeEventActionType;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage;
+import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleChainUpdateResult;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.data.rule.RuleNodeUpdateResult;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
+import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.Collections;
import java.util.Comparator;
@@ -50,7 +61,7 @@ import java.util.stream.Collectors;
@Service
@TbCoreComponent
@Slf4j
-public class DefaultTbRuleChainService implements TbRuleChainService {
+public class DefaultTbRuleChainService extends AbstractTbEntityService implements TbRuleChainService {
private final RuleChainService ruleChainService;
private final RelationService relationService;
@@ -152,6 +163,238 @@ public class DefaultTbRuleChainService implements TbRuleChainService {
return ruleChainIds.stream().map(id -> ruleChainService.findRuleChainById(tenantId, id)).collect(Collectors.toList());
}
+ @Override
+ public RuleChain save(RuleChain ruleChain, SecurityUser user) throws ThingsboardException {
+ TenantId tenantId = ruleChain.getTenantId();
+ ActionType actionType = ruleChain.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
+ try {
+ RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain));
+
+ if (RuleChainType.CORE.equals(savedRuleChain.getType())) {
+ tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedRuleChain.getId(),
+ actionType.equals(ActionType.ADDED) ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
+ }
+ boolean sendMsgToEdge = RuleChainType.EDGE.equals(savedRuleChain.getType()) && actionType.equals(ActionType.UPDATED);
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedRuleChain.getId(),
+ savedRuleChain, user, actionType, sendMsgToEdge, null);
+ return savedRuleChain;
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN),
+ ruleChain, user, actionType, false, e);
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void delete(RuleChain ruleChain, SecurityUser user) throws ThingsboardException {
+ TenantId tenantId = ruleChain.getTenantId();
+ RuleChainId ruleChainId = ruleChain.getId();
+ try {
+ List referencingRuleNodes = ruleChainService.getReferencingRuleChainNodes(tenantId, ruleChainId);
+
+ Set referencingRuleChainIds = referencingRuleNodes.stream().map(RuleNode::getRuleChainId).collect(Collectors.toSet());
+
+ List relatedEdgeIds = null;
+ if (RuleChainType.EDGE.equals(ruleChain.getType())) {
+ relatedEdgeIds = findRelatedEdgeIds(tenantId, ruleChainId);
+ }
+
+ ruleChainService.deleteRuleChainById(tenantId, ruleChainId);
+
+ referencingRuleChainIds.remove(ruleChain.getId());
+
+ if (RuleChainType.CORE.equals(ruleChain.getType())) {
+ referencingRuleChainIds.forEach(referencingRuleChainId ->
+ tbClusterService.broadcastEntityStateChangeEvent(tenantId, referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
+
+ tbClusterService.broadcastEntityStateChangeEvent(tenantId, ruleChain.getId(), ComponentLifecycleEvent.DELETED);
+ }
+
+ notificationEntityService.notifyDeleteRuleChain(tenantId, ruleChain, relatedEdgeIds, user);
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN),
+ null, user, ActionType.DELETED, false, e, ruleChainId.toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public RuleChain saveDefaultByName(TenantId tenantId, DefaultRuleChainCreateRequest request, SecurityUser user) throws ThingsboardException {
+ try {
+ RuleChain savedRuleChain = installScripts.createDefaultRuleChain(tenantId, request.getName());
+ tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedRuleChain.getId(), ComponentLifecycleEvent.CREATED);
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedRuleChain.getId(),
+ savedRuleChain, user, ActionType.ADDED, false, null);
+ return savedRuleChain;
+ } catch (Exception e) {
+ RuleChain ruleChain = new RuleChain();
+ ruleChain.setName(request.getName());
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN),
+ ruleChain, user, ActionType.ADDED, false, e);
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public RuleChain setRootRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException {
+ RuleChainId ruleChainId = ruleChain.getId();
+ try {
+ RuleChain previousRootRuleChain = ruleChainService.getRootTenantRuleChain(tenantId);
+ if (ruleChainService.setRootRuleChain(tenantId, ruleChainId)) {
+ if (previousRootRuleChain != null) {
+ RuleChainId previousRootRuleChainId = previousRootRuleChain.getId();
+ previousRootRuleChain = ruleChainService.findRuleChainById(tenantId, previousRootRuleChainId);
+
+ tbClusterService.broadcastEntityStateChangeEvent(tenantId, previousRootRuleChainId,
+ ComponentLifecycleEvent.UPDATED);
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, previousRootRuleChainId,
+ previousRootRuleChain, user, ActionType.UPDATED, false, null);
+ }
+ ruleChain = ruleChainService.findRuleChainById(tenantId, ruleChainId);
+
+ tbClusterService.broadcastEntityStateChangeEvent(tenantId, ruleChainId,
+ ComponentLifecycleEvent.UPDATED);
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, ruleChainId,
+ ruleChain, user, ActionType.UPDATED, false, null);
+ }
+ return ruleChain;
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN),
+ ruleChain, user, ActionType.UPDATED, false, e, ruleChainId.toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public RuleChainMetaData saveRuleChainMetaData(TenantId tenantId, RuleChain ruleChain, RuleChainMetaData ruleChainMetaData,
+ boolean updateRelated, SecurityUser user) throws ThingsboardException {
+ RuleChainId ruleChainId = ruleChain.getId();
+ RuleChainId ruleChainMetaDataId = ruleChainMetaData.getRuleChainId();
+ try {
+ RuleChainUpdateResult result = ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData);
+ checkNotNull(result.isSuccess() ? true : null);
+
+ List updatedRuleChains;
+ if (updateRelated && result.isSuccess()) {
+ updatedRuleChains = tbRuleChainService.updateRelatedRuleChains(tenantId, ruleChainMetaDataId, result);
+ } else {
+ updatedRuleChains = Collections.emptyList();
+ }
+
+ RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaDataId));
+
+ if (RuleChainType.CORE.equals(ruleChain.getType())) {
+ tbClusterService.broadcastEntityStateChangeEvent(tenantId, ruleChainId, ComponentLifecycleEvent.UPDATED);
+ updatedRuleChains.forEach(updatedRuleChain -> {
+ tbClusterService.broadcastEntityStateChangeEvent(tenantId, updatedRuleChain.getId(), ComponentLifecycleEvent.UPDATED);
+ });
+ }
+
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, ruleChainId,
+ ruleChain, user, ActionType.UPDATED, false, null, ruleChainMetaData);
+
+ if (RuleChainType.EDGE.equals(ruleChain.getType())) {
+ notificationEntityService.notifySendMsgToEdgeService(tenantId, ruleChain.getId(), EdgeEventActionType.UPDATED);
+ }
+
+ for (RuleChain updatedRuleChain : updatedRuleChains) {
+ if (RuleChainType.EDGE.equals(ruleChain.getType())) {
+ notificationEntityService.notifySendMsgToEdgeService(tenantId, updatedRuleChain.getId(), EdgeEventActionType.UPDATED);
+ } else {
+ RuleChainMetaData updatedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, updatedRuleChain.getId()));
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, updatedRuleChain.getId(),
+ updatedRuleChain, user, ActionType.UPDATED, false, null, updatedRuleChainMetaData);
+ }
+ }
+ return savedRuleChainMetaData;
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN),
+ null, user, ActionType.ADDED, false, e, ruleChainMetaData);
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChain ruleChain, Edge edge, SecurityUser user) throws ThingsboardException {
+ RuleChainId ruleChainId = ruleChain.getId();
+ try {
+ RuleChain savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(tenantId, ruleChainId, edge.getId()));
+ notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, ruleChainId,
+ null, edge.getId(),
+ savedRuleChain, ActionType.ASSIGNED_TO_EDGE,
+ user, ruleChainId.toString(), edge.getId().toString(), edge.getName());
+ return savedRuleChain;
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN),
+ null, user, ActionType.ASSIGNED_TO_EDGE, false, e, ruleChainId.toString(), edge.getId().toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChain ruleChain, Edge edge, SecurityUser user) throws ThingsboardException {
+ RuleChainId ruleChainId = ruleChain.getId();
+ try {
+ RuleChain savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(tenantId, ruleChainId, edge.getId(), false));
+ notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, ruleChainId,
+ null, edge.getId(),
+ savedRuleChain, ActionType.UNASSIGNED_FROM_EDGE,
+ user, ruleChainId.toString(), edge.getId().toString(), edge.getName());
+ return savedRuleChain;
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN),
+ null, user, ActionType.UNASSIGNED_FROM_EDGE, false, e, ruleChainId.toString(), edge.getId().toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public RuleChain setEdgeTemplateRootRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException {
+ RuleChainId ruleChainId = ruleChain.getId();
+ try {
+ ruleChainService.setEdgeTemplateRootRuleChain(tenantId, ruleChainId);
+
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, ruleChainId,
+ ruleChain, user, ActionType.UPDATED, false, null);
+ return ruleChain;
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN),
+ null, user, ActionType.UPDATED, false, e, ruleChainId.toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public RuleChain setAutoAssignToEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException {
+ RuleChainId ruleChainId = ruleChain.getId();
+ try {
+ ruleChainService.setAutoAssignToEdgeRuleChain(tenantId, ruleChainId);
+
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, ruleChainId,
+ ruleChain, user, ActionType.UPDATED, false, null);
+ return ruleChain;
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN),
+ null, user, ActionType.UPDATED, false, e, ruleChainId.toString());
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public RuleChain unsetAutoAssignToEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException {
+ RuleChainId ruleChainId = ruleChain.getId();
+ try {
+ ruleChainService.unsetAutoAssignToEdgeRuleChain(tenantId, ruleChainId);
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, ruleChainId,
+ ruleChain, user, ActionType.UPDATED, false, null);
+ return ruleChain;
+ } catch (Exception e) {
+ notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, emptyId(EntityType.RULE_CHAIN),
+ null, user, ActionType.UPDATED, false, e, ruleChainId.toString());
+ throw handleException(e);
+ }
+ }
+
public Set updateRelatedRuleChains(TenantId tenantId, RuleChainId ruleChainId, Map labelsMap) {
Set updatedRuleChains = new HashSet<>();
List usageList = getOutputLabelUsage(tenantId, ruleChainId);
@@ -188,4 +431,5 @@ public class DefaultTbRuleChainService implements TbRuleChainService {
private boolean isRuleNode(RuleNode ruleNode, Class> clazz) {
return ruleNode != null && ruleNode.getType().equals(clazz.getName());
}
+
}
diff --git a/application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java b/application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java
index 78a630416d..dbdba7d4ef 100644
--- a/application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java
+++ b/application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java
@@ -15,20 +15,44 @@
*/
package org.thingsboard.server.service.rule;
+import org.thingsboard.server.common.data.edge.Edge;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest;
import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage;
import org.thingsboard.server.common.data.rule.RuleChainUpdateResult;
+import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
+import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.List;
import java.util.Set;
-public interface TbRuleChainService {
+public interface TbRuleChainService extends SimpleTbEntityService {
Set getRuleChainOutputLabels(TenantId tenantId, RuleChainId ruleChainId);
List getOutputLabelUsage(TenantId tenantId, RuleChainId ruleChainId);
List updateRelatedRuleChains(TenantId tenantId, RuleChainId ruleChainId, RuleChainUpdateResult result);
+
+ RuleChain saveDefaultByName(TenantId tenantId, DefaultRuleChainCreateRequest request, SecurityUser user) throws ThingsboardException;
+
+ RuleChain setRootRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException;
+
+ RuleChainMetaData saveRuleChainMetaData(TenantId tenantId, RuleChain ruleChain, RuleChainMetaData ruleChainMetaData,
+ boolean updateRelated, SecurityUser user) throws ThingsboardException;
+
+ RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChain ruleChain, Edge edge,
+ SecurityUser user) throws ThingsboardException;
+ RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChain ruleChain, Edge edge,
+ SecurityUser user) throws ThingsboardException;
+
+ RuleChain setEdgeTemplateRootRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException;
+
+ RuleChain setAutoAssignToEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException;
+
+ RuleChain unsetAutoAssignToEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, SecurityUser user) throws ThingsboardException;
}
diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/MfaAuthenticationToken.java b/application/src/main/java/org/thingsboard/server/service/security/auth/MfaAuthenticationToken.java
new file mode 100644
index 0000000000..8c70e69179
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/security/auth/MfaAuthenticationToken.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.security.auth;
+
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+public class MfaAuthenticationToken extends AbstractJwtAuthenticationToken {
+ public MfaAuthenticationToken(SecurityUser securityUser) {
+ super(securityUser);
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java
new file mode 100644
index 0000000000..af78d307d3
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java
@@ -0,0 +1,190 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.security.auth.mfa;
+
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.LockedException;
+import org.springframework.stereotype.Service;
+import org.thingsboard.server.common.data.User;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.UserId;
+import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType;
+import org.thingsboard.server.common.msg.tools.TbRateLimits;
+import org.thingsboard.server.dao.user.UserService;
+import org.thingsboard.server.queue.util.TbCoreComponent;
+import org.thingsboard.server.service.security.auth.mfa.config.TwoFaConfigManager;
+import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings;
+import org.thingsboard.server.common.data.security.model.mfa.account.TwoFaAccountConfig;
+import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderConfig;
+import org.thingsboard.server.service.security.auth.mfa.provider.TwoFaProvider;
+import org.thingsboard.server.service.security.model.SecurityUser;
+import org.thingsboard.server.service.security.system.SystemSecurityService;
+
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+@Service
+@RequiredArgsConstructor
+@TbCoreComponent
+public class DefaultTwoFactorAuthService implements TwoFactorAuthService {
+
+ private final TwoFaConfigManager configManager;
+ private final SystemSecurityService systemSecurityService;
+ private final UserService userService;
+ private final Map> providers = new EnumMap<>(TwoFaProviderType.class);
+
+ private static final ThingsboardException ACCOUNT_NOT_CONFIGURED_ERROR = new ThingsboardException("2FA is not configured for account", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
+ private static final ThingsboardException PROVIDER_NOT_CONFIGURED_ERROR = new ThingsboardException("2FA provider is not configured", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
+ private static final ThingsboardException PROVIDER_NOT_AVAILABLE_ERROR = new ThingsboardException("2FA provider is not available", ThingsboardErrorCode.GENERAL);
+
+ private final ConcurrentMap> verificationCodeSendingRateLimits = new ConcurrentHashMap<>();
+ private final ConcurrentMap> verificationCodeCheckingRateLimits = new ConcurrentHashMap<>();
+
+ @Override
+ public boolean isTwoFaEnabled(TenantId tenantId, UserId userId) {
+ return configManager.getAccountTwoFaSettings(tenantId, userId)
+ .map(settings -> !settings.getConfigs().isEmpty())
+ .orElse(false);
+ }
+
+ @Override
+ public void checkProvider(TenantId tenantId, TwoFaProviderType providerType) throws ThingsboardException {
+ getTwoFaProvider(providerType).check(tenantId);
+ }
+
+
+ @Override
+ public void prepareVerificationCode(SecurityUser user, TwoFaProviderType providerType, boolean checkLimits) throws Exception {
+ TwoFaAccountConfig accountConfig = configManager.getTwoFaAccountConfig(user.getTenantId(), user.getId(), providerType)
+ .orElseThrow(() -> ACCOUNT_NOT_CONFIGURED_ERROR);
+ prepareVerificationCode(user, accountConfig, checkLimits);
+ }
+
+ @Override
+ public void prepareVerificationCode(SecurityUser user, TwoFaAccountConfig accountConfig, boolean checkLimits) throws ThingsboardException {
+ PlatformTwoFaSettings twoFaSettings = configManager.getPlatformTwoFaSettings(user.getTenantId(), true)
+ .orElseThrow(() -> PROVIDER_NOT_CONFIGURED_ERROR);
+ if (checkLimits) {
+ Integer minVerificationCodeSendPeriod = twoFaSettings.getMinVerificationCodeSendPeriod();
+ String rateLimit = null;
+ if (minVerificationCodeSendPeriod != null && minVerificationCodeSendPeriod > 4) {
+ rateLimit = "1:" + minVerificationCodeSendPeriod;
+ }
+ checkRateLimits(user.getId(), accountConfig.getProviderType(), rateLimit, verificationCodeSendingRateLimits);
+ }
+
+ TwoFaProviderConfig providerConfig = twoFaSettings.getProviderConfig(accountConfig.getProviderType())
+ .orElseThrow(() -> PROVIDER_NOT_CONFIGURED_ERROR);
+ getTwoFaProvider(accountConfig.getProviderType()).prepareVerificationCode(user, providerConfig, accountConfig);
+ }
+
+
+ @Override
+ public boolean checkVerificationCode(SecurityUser user, TwoFaProviderType providerType, String verificationCode, boolean checkLimits) throws ThingsboardException {
+ TwoFaAccountConfig accountConfig = configManager.getTwoFaAccountConfig(user.getTenantId(), user.getId(), providerType)
+ .orElseThrow(() -> ACCOUNT_NOT_CONFIGURED_ERROR);
+ return checkVerificationCode(user, verificationCode, accountConfig, checkLimits);
+ }
+
+ @Override
+ public boolean checkVerificationCode(SecurityUser user, String verificationCode, TwoFaAccountConfig accountConfig, boolean checkLimits) throws ThingsboardException {
+ if (!userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()).isEnabled()) {
+ throw new ThingsboardException("User is disabled", ThingsboardErrorCode.AUTHENTICATION);
+ }
+
+ PlatformTwoFaSettings twoFaSettings = configManager.getPlatformTwoFaSettings(user.getTenantId(), true)
+ .orElseThrow(() -> PROVIDER_NOT_CONFIGURED_ERROR);
+ if (checkLimits) {
+ checkRateLimits(user.getId(), accountConfig.getProviderType(), twoFaSettings.getVerificationCodeCheckRateLimit(), verificationCodeCheckingRateLimits);
+ }
+ TwoFaProviderConfig providerConfig = twoFaSettings.getProviderConfig(accountConfig.getProviderType())
+ .orElseThrow(() -> PROVIDER_NOT_CONFIGURED_ERROR);
+
+ boolean verificationSuccess = false;
+ if (StringUtils.isNotBlank(verificationCode)) {
+ if (StringUtils.isNumeric(verificationCode) || accountConfig.getProviderType() == TwoFaProviderType.BACKUP_CODE) {
+ verificationSuccess = getTwoFaProvider(accountConfig.getProviderType()).checkVerificationCode(user, verificationCode, providerConfig, accountConfig);
+ }
+ }
+ if (checkLimits) {
+ try {
+ systemSecurityService.validateTwoFaVerification(user, verificationSuccess, twoFaSettings);
+ } catch (LockedException e) {
+ verificationCodeCheckingRateLimits.remove(user.getId());
+ verificationCodeSendingRateLimits.remove(user.getId());
+ throw new ThingsboardException(e.getMessage(), ThingsboardErrorCode.AUTHENTICATION);
+ }
+ if (verificationSuccess) {
+ verificationCodeCheckingRateLimits.remove(user.getId());
+ verificationCodeSendingRateLimits.remove(user.getId());
+ }
+ }
+ return verificationSuccess;
+ }
+
+ private void checkRateLimits(UserId userId, TwoFaProviderType providerType, String rateLimitConfig,
+ ConcurrentMap> rateLimits) throws ThingsboardException {
+ if (StringUtils.isNotEmpty(rateLimitConfig)) {
+ ConcurrentMap providersRateLimits = rateLimits.computeIfAbsent(userId, i -> new ConcurrentHashMap<>());
+
+ TbRateLimits rateLimit = providersRateLimits.get(providerType);
+ if (rateLimit == null || !rateLimit.getConfig().equals(rateLimitConfig)) {
+ rateLimit = new TbRateLimits(rateLimitConfig, true);
+ providersRateLimits.put(providerType, rateLimit);
+ }
+ if (!rateLimit.tryConsume()) {
+ throw new ThingsboardException("Too many requests", ThingsboardErrorCode.TOO_MANY_REQUESTS);
+ }
+ } else {
+ rateLimits.remove(userId);
+ }
+ }
+
+
+ @Override
+ public TwoFaAccountConfig generateNewAccountConfig(User user, TwoFaProviderType providerType) throws ThingsboardException {
+ TwoFaProviderConfig providerConfig = getTwoFaProviderConfig(user.getTenantId(), providerType);
+ return getTwoFaProvider(providerType).generateNewAccountConfig(user, providerConfig);
+ }
+
+
+ private TwoFaProviderConfig getTwoFaProviderConfig(TenantId tenantId, TwoFaProviderType providerType) throws ThingsboardException {
+ return configManager.getPlatformTwoFaSettings(tenantId, true)
+ .flatMap(twoFaSettings -> twoFaSettings.getProviderConfig(providerType))
+ .orElseThrow(() -> PROVIDER_NOT_CONFIGURED_ERROR);
+ }
+
+ private TwoFaProvider getTwoFaProvider(TwoFaProviderType providerType) throws ThingsboardException {
+ return Optional.ofNullable(providers.get(providerType))
+ .orElseThrow(() -> PROVIDER_NOT_AVAILABLE_ERROR);
+ }
+
+ @Autowired
+ private void setProviders(Collection providers) {
+ providers.forEach(provider -> {
+ this.providers.put(provider.getType(), provider);
+ });
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/TwoFactorAuthService.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/TwoFactorAuthService.java
new file mode 100644
index 0000000000..aabda27ec7
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/TwoFactorAuthService.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.security.auth.mfa;
+
+import org.thingsboard.server.common.data.User;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.UserId;
+import org.thingsboard.server.common.data.security.model.mfa.account.TwoFaAccountConfig;
+import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+public interface TwoFactorAuthService {
+
+ boolean isTwoFaEnabled(TenantId tenantId, UserId userId);
+
+ void checkProvider(TenantId tenantId, TwoFaProviderType providerType) throws ThingsboardException;
+
+
+ void prepareVerificationCode(SecurityUser user, TwoFaProviderType providerType, boolean checkLimits) throws Exception;
+
+ void prepareVerificationCode(SecurityUser user, TwoFaAccountConfig accountConfig, boolean checkLimits) throws ThingsboardException;
+
+
+ boolean checkVerificationCode(SecurityUser user, TwoFaProviderType providerType, String verificationCode, boolean checkLimits) throws ThingsboardException;
+
+ boolean checkVerificationCode(SecurityUser user, String verificationCode, TwoFaAccountConfig accountConfig, boolean checkLimits) throws ThingsboardException;
+
+
+ TwoFaAccountConfig generateNewAccountConfig(User user, TwoFaProviderType providerType) throws ThingsboardException;
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java
new file mode 100644
index 0000000000..ad2ecebd2c
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java
@@ -0,0 +1,187 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.security.auth.mfa.config;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.thingsboard.common.util.JacksonUtil;
+import org.thingsboard.server.common.data.AdminSettings;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.UserId;
+import org.thingsboard.server.common.data.security.UserAuthSettings;
+import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings;
+import org.thingsboard.server.common.data.security.model.mfa.account.AccountTwoFaSettings;
+import org.thingsboard.server.common.data.security.model.mfa.account.TwoFaAccountConfig;
+import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderConfig;
+import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType;
+import org.thingsboard.server.dao.service.ConstraintValidator;
+import org.thingsboard.server.dao.settings.AdminSettingsDao;
+import org.thingsboard.server.dao.settings.AdminSettingsService;
+import org.thingsboard.server.dao.user.UserAuthSettingsDao;
+import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService;
+
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+
+@Service
+@RequiredArgsConstructor
+public class DefaultTwoFaConfigManager implements TwoFaConfigManager {
+
+ private final UserAuthSettingsDao userAuthSettingsDao;
+ private final AdminSettingsService adminSettingsService;
+ private final AdminSettingsDao adminSettingsDao;
+ @Autowired @Lazy
+ private TwoFactorAuthService twoFactorAuthService;
+
+ protected static final String TWO_FACTOR_AUTH_SETTINGS_KEY = "twoFaSettings";
+
+
+ @Override
+ public Optional