Commit 279e22152364f913da38bffa6ce31d58a394bc15

Authored by Andrii Shvaika
2 parents 07aefe53 9900cc3d

Merge with master

Showing 47 changed files with 3204 additions and 107 deletions

Too many changes to show.

To preserve performance only 47 of 580 files are displayed.

@@ -33,5 +33,6 @@ pom.xml.versionsBackup @@ -33,5 +33,6 @@ pom.xml.versionsBackup
33 **/.env 33 **/.env
34 .instance_id 34 .instance_id
35 rebuild-docker.sh 35 rebuild-docker.sh
  36 +*/.run/**
36 .run/** 37 .run/**
37 .run 38 .run
@@ -102,6 +102,10 @@ @@ -102,6 +102,10 @@
102 <artifactId>stats</artifactId> 102 <artifactId>stats</artifactId>
103 </dependency> 103 </dependency>
104 <dependency> 104 <dependency>
  105 + <groupId>org.thingsboard.common</groupId>
  106 + <artifactId>edge-api</artifactId>
  107 + </dependency>
  108 + <dependency>
105 <groupId>org.thingsboard</groupId> 109 <groupId>org.thingsboard</groupId>
106 <artifactId>dao</artifactId> 110 <artifactId>dao</artifactId>
107 <type>test-jar</type> 111 <type>test-jar</type>
  1 +{
  2 + "ruleChain": {
  3 + "additionalInfo": null,
  4 + "name": "Edge Root Rule Chain",
  5 + "type": "EDGE",
  6 + "firstRuleNodeId": null,
  7 + "root": true,
  8 + "debugMode": false,
  9 + "configuration": null
  10 + },
  11 + "metadata": {
  12 + "firstNodeIndex": 0,
  13 + "nodes": [
  14 + {
  15 + "additionalInfo": {
  16 + "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
  17 + "layoutX": 203,
  18 + "layoutY": 259
  19 + },
  20 + "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
  21 + "name": "Device Profile Node",
  22 + "debugMode": false,
  23 + "configuration": {
  24 + "persistAlarmRulesState": false,
  25 + "fetchAlarmRulesStateOnStart": false
  26 + }
  27 + },
  28 + {
  29 + "additionalInfo": {
  30 + "layoutX": 823,
  31 + "layoutY": 157
  32 + },
  33 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
  34 + "name": "Save Timeseries",
  35 + "debugMode": false,
  36 + "configuration": {
  37 + "defaultTTL": 0
  38 + }
  39 + },
  40 + {
  41 + "additionalInfo": {
  42 + "layoutX": 824,
  43 + "layoutY": 52
  44 + },
  45 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
  46 + "name": "Save Client Attributes",
  47 + "debugMode": false,
  48 + "configuration": {
  49 + "scope": "CLIENT_SCOPE"
  50 + }
  51 + },
  52 + {
  53 + "additionalInfo": {
  54 + "layoutX": 347,
  55 + "layoutY": 149
  56 + },
  57 + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
  58 + "name": "Message Type Switch",
  59 + "debugMode": false,
  60 + "configuration": {
  61 + "version": 0
  62 + }
  63 + },
  64 + {
  65 + "additionalInfo": {
  66 + "layoutX": 825,
  67 + "layoutY": 266
  68 + },
  69 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  70 + "name": "Log RPC from Device",
  71 + "debugMode": false,
  72 + "configuration": {
  73 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  74 + }
  75 + },
  76 + {
  77 + "additionalInfo": {
  78 + "layoutX": 824,
  79 + "layoutY": 378
  80 + },
  81 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  82 + "name": "Log Other",
  83 + "debugMode": false,
  84 + "configuration": {
  85 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  86 + }
  87 + },
  88 + {
  89 + "additionalInfo": {
  90 + "layoutX": 824,
  91 + "layoutY": 466
  92 + },
  93 + "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
  94 + "name": "RPC Call Request",
  95 + "debugMode": false,
  96 + "configuration": {
  97 + "timeoutInSeconds": 60
  98 + }
  99 + },
  100 + {
  101 + "additionalInfo": {
  102 + "layoutX": 1134,
  103 + "layoutY": 132
  104 + },
  105 + "type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
  106 + "name": "Push to cloud",
  107 + "debugMode": false,
  108 + "configuration": {
  109 + "version": 0
  110 + }
  111 + }
  112 + ],
  113 + "connections": [
  114 + {
  115 + "fromIndex": 0,
  116 + "toIndex": 3,
  117 + "type": "Success"
  118 + },
  119 + {
  120 + "fromIndex": 1,
  121 + "toIndex": 7,
  122 + "type": "Success"
  123 + },
  124 + {
  125 + "fromIndex": 2,
  126 + "toIndex": 7,
  127 + "type": "Success"
  128 + },
  129 + {
  130 + "fromIndex": 3,
  131 + "toIndex": 6,
  132 + "type": "RPC Request to Device"
  133 + },
  134 + {
  135 + "fromIndex": 3,
  136 + "toIndex": 5,
  137 + "type": "Other"
  138 + },
  139 + {
  140 + "fromIndex": 3,
  141 + "toIndex": 2,
  142 + "type": "Post attributes"
  143 + },
  144 + {
  145 + "fromIndex": 3,
  146 + "toIndex": 1,
  147 + "type": "Post telemetry"
  148 + },
  149 + {
  150 + "fromIndex": 3,
  151 + "toIndex": 4,
  152 + "type": "RPC Request from Device"
  153 + },
  154 + {
  155 + "fromIndex": 4,
  156 + "toIndex": 7,
  157 + "type": "Success"
  158 + }
  159 + ],
  160 + "ruleChainConnections": null
  161 + }
  162 +}
  1 +{
  2 + "widgetsBundle": {
  3 + "alias": "edge_widgets",
  4 + "title": "Edge widgets",
  5 + "image": null
  6 + },
  7 + "widgetTypes": [
  8 + {
  9 + "alias": "edges_overview",
  10 + "name": "Edge Quick Overview",
  11 + "descriptor": {
  12 + "type": "latest",
  13 + "sizeX": 7.5,
  14 + "sizeY": 5,
  15 + "resources": [],
  16 + "templateHtml": "<tb-edges-overview-widget \n [ctx]=\"ctx\">\n</tb-edges-overview-widget>",
  17 + "templateCss": "",
  18 + "controllerScript": "self.onInit = function() {\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n dataKeysOptional: true\n };\n}\n\nself.onDestroy = function() {\n};\n",
  19 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EdgeOverviewSettings\",\n \"properties\": {\n \"enableDefaultTitle\": {\n \"title\": \"Display default title\",\n \"type\": \"boolean\",\n \"default\": true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"enableDefaultTitle\"\n ]\n}",
  20 + "dataKeySettingsSchema": "{}\n",
  21 + "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"showTitleIcon\":true,\"titleIcon\":\"router\",\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{},\"title\":\"Edge Quick Overview\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"widgetStyle\":{},\"actions\":{}}"
  22 + }
  23 + }
  24 + ]
  25 +}
@@ -455,6 +455,24 @@ @@ -455,6 +455,24 @@
455 "dataKeySettingsSchema": "{}\n", 455 "dataKeySettingsSchema": "{}\n",
456 "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Photo camera input\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}" 456 "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Photo camera input\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
457 } 457 }
  458 + },
  459 + {
  460 + "alias": "update_json_attribute",
  461 + "name": "Update JSON attribute",
  462 + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUoAAADhCAMAAACZWwyuAAAB71BMVEX////w8PDt7e3z8/Pg4ODAwMDc3Nz19fUZhJQ4lKK9vb17e3v9/f2mpqbj4+P4+Pi+vr58fHzT09P7+/uTxcz2+/ucy9GpqakCAgK93OHNzc0eh5b09PSw1dusrKwvkZ+ampqysrIzMzPV1dXCwsKurq4iiZnZ2dnX19fIyMgrj50ABsiKiopmZmbo8/XQ5+ro6OjPz89JnqqCgoLX6u2cnJwmjJv39/ff39+12N7ExMQ2NjaioqKYmJjs7OzD4OTS0tLKysrh8PLq6uq6uroCCMjDw8OOjo6hzdO0tLQuj56UlJS4uvDZ7O7l5eVvsrw0k6EchZWEhIRwcHDU6ezG4eaGv8d1tb+3t7dCmqeIiIj4+/3j8fO62+AKD8tWpbGgoKBqamouLi7u7u47lqTP0PWr0tgXHM1/u8N6uMFqsLpfqrVNoKxFRUXU1faYyM92dnY7Ozvz+Pry8vK5ubm4uLh+fn5sbGzGx/OipOtzduG42t8uM9KNw8pTo690dHT6+vrt9viOkefb29um0NYfHx/p6vu9vvGqrO2YmulXW9uhztRnrrgmJiYQEBDt7fzk5frf4PjL4+eMj+a/3eJpbN9MUNk7P9SQxMuNwslfX19TU1OcnurI4uY9QdYyN9ImK9BVVVVPT097fuNYWFjSJwPyAAALvElEQVR42uzbTWviQBjA8ad5qgNONI05FCSIshUNKERCSFlM2WIxSKCH5FI8RfoFCrvVYwv7xTfG4susxqgpqJkfHoLM6c9MTCYROI7jOC4doiIRoqrEkhQRuIOJEsEVROI1Dw2pIkPlMQ8PyWMez8ItLOD2QnArAlxyIsEYhK/x5CSMJQGX0Cfu8AmXTCEqbkWUPQaBiDtd8hJXMJ6SbNByeWd3iROMRxIPAhEx09MSd1ATDwIJMdPTEndJNIid4D/kkLk4yMbFZTop2fXdonSZktb2XuFKpwRnBzeoGoeklNZS1tjj+BVu2hDKfYhxKXUXTtnXHIqgXd+esk/pNSK+GrQcjZsbuotBVrKUFmzSGs++b1djZqVV1m01D6crqlSpVOldpYKFSmzKWUCN0nI0bq5bWAwiuHquNNnj+JOlSDWA144OJW86KUcp38oAtQZIPZ/mICQEBvWGcLpw7i6acu0iCkXDtQ1hcos4MtdTOt4VYs2h5dk42y4azj3ejhYpVUyEwEZ1F+BpBJKviQVfWUkZuOTZz0e5ba8FJ4xNWZcLPw0De97vezpYTzmUK1iiL18p5T+aEaymxGRU2OhhTMAbgFISATvCMqU0LgEUo4U/KOreKV9LsSlHAeKtgY+09STr6ylvzABfRuWvlC5i4DCz8oiU4Az6Uwug25blzsMyZb8zDpkQkgj04YSxKSf16FwpTOquzZwrnx5lvT3sLlP21lKSoxY4aM4wANCn10DmKemvaFZ2HuBMMCnDT5QSe205x6R8ufICWqowKQ/92WFJ/lsTID+9vhp29FlKpye1/AbUXELqTTgDbMq60S2PDMRHKutsSqzTGrIpD74YYvUohBr+x1D+O0tZMqaO0wASfPjmWdwjbfgFd0wDUZjY+F/Ke6ptSRl/iZ6Nm3Dc4sq7Se/GMRtbQ7hZvjrR+XZGKimrhoZ7pOSbbKntV/Kt39R20fkDiZSf7Yi4i3rBkzLVJ46g4M7mHH+l4Bjpv+hiXfTyTr9lVk+U38Diqzs1n1l8XeibiJaKLP7O74FEiYdMT/RfE0RVJZICe8txSSRKmU9KEIRm8V0QnitNIWvST6m93whhy3/sm/lTEmEYx7/SOLPbdFgjCDGEWm0FBUVEGRRF0WEmlCgxeFSaeKHpNF1ajWW3V4cddh9/aLs078PqCMOCxjrs55dn3mff9/3hM7x7fGfYrSs1ll9leUWFrmrK+ERXahSk0uc9srTKJ7c31+hKjQJUHnnorc2g0lijK9eVGgWovHF1fUaV5zSVyg54RUaVNzWVearcK5FWWWPcqanMU2VjU1NTI6kcNjbpNJX5HvDGq43pA7510/Q+TaVylcyl/F65z1iuqVSi8vgpX23VqaUfO5c1lUpUrq+V2LXUh6PxnPbhuExxhnHrfi3OKFylbseV8ic/tz/WlRoroFK8V9ZMGad0pUZuKstyBSWMpnJ1qfS0tXmk6hhFHmRfR/sXnQJUVswPJy8dy0Vlvd9fL1WDFXmRfR3tv5hn39oT9+JU5TyyNwBQi8rOz3vWVnurc1HZ5XR2rZhK2n8x68buNbwNPqIq43lCVSrLpB/k1aZcVD5wODoAXOQk9ND3hD39DrzhzR39QgiIhs22Xj1YH4EQL5hjQIQ3C5OO9DrbWaCSZ9dpPu2PjdeuHZK7BPD+JdU0g4nrQVWplPizJxeVAaAVgLvF5na7gT6+rnlgAJZ6p6ujWfCD72uesEWpDxP3qvlrGzA+6hwNh2gdU8mus/m0PzYYjYexkO/3qBLxsbtQnUpf8vDCRi4HNcBFADcXsPDoDcF1FnwlYOFaWV9UpQdjiGfrSCW7TvPTVB/FQl4knlIlfr+H6lRWDZ8uU66yjjOLcM2Syj64DCmV41wd65PK1sl6m9WWUSXNz8zd9jtUiaftg6pTuXX4VFkeKk2c84KIXlTZE2IqRzgT65PKrgF/pEem0jpEKuX7ZDPZQDXNC3swGLSnnkNqUbnfW6PwFb1bgEgr1w0JUSXAVJq4AOszVSNcHTBho3VwRUkl7bOQg9WQMdj+nOo/vnwBMDMoEnwxA0AtKm/MbxE5qUBlhGt5Z9AjKtw3WSwylTFLd7gfrM9Uge/wO3ts6XWfhFf+PlJJ84nFj52XH66LxKli1m5/DQl2wM9MX1aFyl//8koFKvHRY3Y5sO5+2Gxtkau0CpMjYH1SdaFDMNf3p9fpo8LF+hBdp/nEopehH3aJ11QRHxuLL1D5y3hbFSqXAvnAV6JIbJuu1lQuC0eStRs1lctC1aU16njsaCFb8VRW5pCxvWnBauW/5ZXZDvKQJ0K6ednYMarGXHIFVFZ99ib36BTklRlVRjmTXCWNDdaccsmgXaIBSlGPSt9BMa/0KcgrM6rUOyFTSWNSmT2XxB2RucQsFKMalRLztxTklbxVEKJ6FktQXhngOK4OIiOTgjXGszHllA5p1Mo1L5FLErPtd6EcFansXDt8TkFeyff7J4RPTCXllXG3+2JKXX/YYhFVsjHllLFewCLEs+WSc2MoOoWoXFtbu6kzg8pM74+9LlJJeSWQUhfg/Kk+G9MBb+GB3g5kySWfioFF0SlE5b4TV4YvKVQ5ZCWVLK9k6tq4kaVVOjk3YkPIwtuECv4gV+C90uc9pkzlWRvljiyvZOpMmVSiq2WciyALH+ZQfApUuV65SsodKRkCPN2p/NGS6rMx5ZQYCk+Es+SSmAk+Q/EpQOX5mgpf8mGZYpUsd5SpfDBgMugR8hikPhtTTolxwdqDzLkkntlHUHzyV9l5I+lNHmhUrpLljjKVzpg55kC8x2a2hsDGlFMCvVwEmXNJPEpABayOOMPggvpZBSrHnRaPCepnBVRuOFFefmIDlg3TX/buXqVhKIzD+FuonEEJJQ4eKogEOrjFQRRSisEiuIl3YPYOvQId7Kg34OSdGgd1SaD5aPmH8zwQsv8g5Jx3OOf08c0GUP+UUezMXDzA8/3lKHNnZS630Oqf8sAK//5QvkKrG+VVsqygXEQvGyibUc5WlZTl44+gbEJ5u5pm1ZT3/hjKBpSLr5NRDeWFf4WyyR78eVRHeecnUG5PebOOaynt0K8ttNpTzpMsy5LkvIry+uNpZqHVnjK6LMvm4yrKqXd84A2X6PzBd0mZux/KERvHfsYZE884o5ch2/LscxOe5C4orfDFxMJrAFP0oQQllHpBCaVeUEKpF5RQ6gUllHpBCaVeUEKpF5RQ6gUllHpBCaVeUEKpF5RQ6gUllHpBCaVeUEKpF5RQ6gUllHpBCaVeUEKpF5RQ6gUllHpBCaVeUEKpF5RQ6gUllHpBCaVeUEKpF5RQ6gUllHpBuWdK2qLwDj8lIiIiIiIKLpdG45ZFqTP6y407heV/aTfK1Oi3qBtlgNdmfLdrxypyw0AAhifJ2AeDZtcohUAEgZGFrdJVjIsgk2DY5urt9xFu9wn2xWMFLklzV3gb45u/GHD7YSML5s3wwUASSqHcbkIplNtLKIVyewll7onaZR7oB/yfJ/UYZfvLmbMXyscpy2CKxplOKB+mtBwRNRcflLJJvhh/Anybir4nBV9sMeJKyonnZaYO/cm4GX1+JC6xr02dPgAlpaFpFMTq7onUsRkx0WEd5RD42ucP3YWJeMJQI4YTJj7H2tz3TzkBlPR0oQEgkUK6AEzz2mPnxhx6LPsW0RA2XHYc8ebKwXPcP2UPgPTynAk9KV2pTHtcRZkxZ8Ma9dkZvqJmO3KLjnPFTimP1EEm/PyP8tMfSp8p7/R9FWWyy9BMrTnp1lwRA53zNx70UrtTSmjiMhIdXykvdADQpDCT2katoiTWiC1Xli0OmbJyZkQ8m4XR7/XYASQ7JNLwSglxfh4qUl+bETUNsIqyM66YAyfPdaz5hpiYuzzDWLHeKyUcYhEH9Zcy/wzZRApebDUOa2873dWZ2iJG54oQENHVuGRrE+Ju30q5g28qoRTK7SWUQrm9hFIot5dQCuX2kvUrWQrcXrKqKgvUkiRJkiRJkiS9328dj4CaN1dagAAAAABJRU5ErkJggg==",
  463 + "description": "Simple form to input new JSON value for pre-defined attribute/timeseries key.",
  464 + "descriptor": {
  465 + "type": "latest",
  466 + "sizeX": 7.5,
  467 + "sizeY": 3,
  468 + "resources": [],
  469 + "templateHtml": "<tb-json-input-widget \n [ctx]=\"ctx\">\n</tb-json-input-widget>",
  470 + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
  471 + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.jsonInputWidget.onDataUpdated();\n}\n\nself.onResize = function() {\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n}",
  472 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"AdvancedSettings\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"widgetMode\": {\n \"title\": \"Widget mode\",\n \"type\": \"string\",\n \"default\": \"ATTRIBUTE\"\n },\n \"attributeScope\": {\n \"title\": \"Attribute scope\",\n \"type\": \"string\",\n \"default\": \"SERVER_SCOPE\"\n },\n \"showLabel\":{\n \"title\": \"Show label\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"labelValue\": {\n \"title\": \"Label\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"attributeRequired\": {\n \"title\": \"Value required\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showResultMessage\": {\n \"title\": \"Show result message\",\n \"type\": \"boolean\",\n \"default\": true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n {\n \"key\": \"widgetMode\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"ATTRIBUTE\",\n \"label\": \"Update attribute\"\n },\n {\n \"value\": \"TIME_SERIES\",\n \"label\": \"Update timeseries\"\n }\n ]\n },\n {\n \"key\": \"attributeScope\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"condition\": \"model.widgetMode === 'ATTRIBUTE'\",\n \"items\": [\n {\n \"value\": \"SERVER_SCOPE\",\n \"label\": \"Server attribute\"\n },\n {\n \"value\": \"SHARED_SCOPE\",\n \"label\": \"Shared attribute\"\n }\n ]\n },\n \"showLabel\",\n {\n \"key\": \"labelValue\",\n \"condition\": \"model.showLabel\"\n },\n \"attributeRequired\",\n \"showResultMessage\"\n ]\n}",
  473 + "dataKeySettingsSchema": "{}",
  474 + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"attributeScope\":\"SERVER_SCOPE\",\"showLabel\":true,\"attributeRequired\":true,\"showResultMessage\":true},\"title\":\"Update JSON attribute\",\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}"
  475 + }
458 } 476 }
459 ] 477 ]
460 } 478 }
  1 +{
  2 + "ruleChain": {
  3 + "additionalInfo": null,
  4 + "name": "Edge Root Rule Chain",
  5 + "type": "EDGE",
  6 + "firstRuleNodeId": null,
  7 + "root": true,
  8 + "debugMode": false,
  9 + "configuration": null
  10 + },
  11 + "metadata": {
  12 + "firstNodeIndex": 0,
  13 + "nodes": [
  14 + {
  15 + "additionalInfo": {
  16 + "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
  17 + "layoutX": 203,
  18 + "layoutY": 259
  19 + },
  20 + "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
  21 + "name": "Device Profile Node",
  22 + "debugMode": false,
  23 + "configuration": {
  24 + "persistAlarmRulesState": false,
  25 + "fetchAlarmRulesStateOnStart": false
  26 + }
  27 + },
  28 + {
  29 + "additionalInfo": {
  30 + "layoutX": 823,
  31 + "layoutY": 157
  32 + },
  33 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
  34 + "name": "Save Timeseries",
  35 + "debugMode": false,
  36 + "configuration": {
  37 + "defaultTTL": 0
  38 + }
  39 + },
  40 + {
  41 + "additionalInfo": {
  42 + "layoutX": 824,
  43 + "layoutY": 52
  44 + },
  45 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
  46 + "name": "Save Client Attributes",
  47 + "debugMode": false,
  48 + "configuration": {
  49 + "scope": "CLIENT_SCOPE"
  50 + }
  51 + },
  52 + {
  53 + "additionalInfo": {
  54 + "layoutX": 347,
  55 + "layoutY": 149
  56 + },
  57 + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
  58 + "name": "Message Type Switch",
  59 + "debugMode": false,
  60 + "configuration": {
  61 + "version": 0
  62 + }
  63 + },
  64 + {
  65 + "additionalInfo": {
  66 + "layoutX": 825,
  67 + "layoutY": 266
  68 + },
  69 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  70 + "name": "Log RPC from Device",
  71 + "debugMode": false,
  72 + "configuration": {
  73 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  74 + }
  75 + },
  76 + {
  77 + "additionalInfo": {
  78 + "layoutX": 824,
  79 + "layoutY": 378
  80 + },
  81 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  82 + "name": "Log Other",
  83 + "debugMode": false,
  84 + "configuration": {
  85 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  86 + }
  87 + },
  88 + {
  89 + "additionalInfo": {
  90 + "layoutX": 824,
  91 + "layoutY": 466
  92 + },
  93 + "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
  94 + "name": "RPC Call Request",
  95 + "debugMode": false,
  96 + "configuration": {
  97 + "timeoutInSeconds": 60
  98 + }
  99 + },
  100 + {
  101 + "additionalInfo": {
  102 + "layoutX": 1134,
  103 + "layoutY": 132
  104 + },
  105 + "type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
  106 + "name": "Push to cloud",
  107 + "debugMode": false,
  108 + "configuration": {
  109 + "version": 0
  110 + }
  111 + }
  112 + ],
  113 + "connections": [
  114 + {
  115 + "fromIndex": 0,
  116 + "toIndex": 3,
  117 + "type": "Success"
  118 + },
  119 + {
  120 + "fromIndex": 1,
  121 + "toIndex": 7,
  122 + "type": "Success"
  123 + },
  124 + {
  125 + "fromIndex": 2,
  126 + "toIndex": 7,
  127 + "type": "Success"
  128 + },
  129 + {
  130 + "fromIndex": 3,
  131 + "toIndex": 6,
  132 + "type": "RPC Request to Device"
  133 + },
  134 + {
  135 + "fromIndex": 3,
  136 + "toIndex": 5,
  137 + "type": "Other"
  138 + },
  139 + {
  140 + "fromIndex": 3,
  141 + "toIndex": 2,
  142 + "type": "Post attributes"
  143 + },
  144 + {
  145 + "fromIndex": 3,
  146 + "toIndex": 1,
  147 + "type": "Post telemetry"
  148 + },
  149 + {
  150 + "fromIndex": 3,
  151 + "toIndex": 4,
  152 + "type": "RPC Request from Device"
  153 + },
  154 + {
  155 + "fromIndex": 4,
  156 + "toIndex": 7,
  157 + "type": "Success"
  158 + }
  159 + ],
  160 + "ruleChainConnections": null
  161 + }
  162 +}
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 "ruleChain": { 2 "ruleChain": {
3 "additionalInfo": null, 3 "additionalInfo": null,
4 "name": "Root Rule Chain", 4 "name": "Root Rule Chain",
  5 + "type": "CORE",
5 "firstRuleNodeId": null, 6 "firstRuleNodeId": null,
6 "root": true, 7 "root": true,
7 "debugMode": false, 8 "debugMode": false,
@@ -14,6 +14,38 @@ @@ -14,6 +14,38 @@
14 -- limitations under the License. 14 -- limitations under the License.
15 -- 15 --
16 16
  17 +CREATE TABLE IF NOT EXISTS edge (
  18 + id uuid NOT NULL CONSTRAINT edge_pkey PRIMARY KEY,
  19 + created_time bigint NOT NULL,
  20 + additional_info varchar,
  21 + customer_id uuid,
  22 + root_rule_chain_id uuid,
  23 + type varchar(255),
  24 + name varchar(255),
  25 + label varchar(255),
  26 + routing_key varchar(255),
  27 + secret varchar(255),
  28 + edge_license_key varchar(30),
  29 + cloud_endpoint varchar(255),
  30 + search_text varchar(255),
  31 + tenant_id uuid,
  32 + CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name),
  33 + CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key)
  34 + );
  35 +
  36 +CREATE TABLE IF NOT EXISTS edge_event (
  37 + id uuid NOT NULL CONSTRAINT edge_event_pkey PRIMARY KEY,
  38 + created_time bigint NOT NULL,
  39 + edge_id uuid,
  40 + edge_event_type varchar(255),
  41 + edge_event_uid varchar(255),
  42 + entity_id uuid,
  43 + edge_event_action varchar(255),
  44 + body varchar(10000000),
  45 + tenant_id uuid,
  46 + ts bigint NOT NULL
  47 + );
  48 +
17 CREATE TABLE IF NOT EXISTS resource ( 49 CREATE TABLE IF NOT EXISTS resource (
18 id uuid NOT NULL CONSTRAINT resource_pkey PRIMARY KEY, 50 id uuid NOT NULL CONSTRAINT resource_pkey PRIMARY KEY,
19 created_time bigint NOT NULL, 51 created_time bigint NOT NULL,
@@ -64,3 +96,4 @@ DO $$ @@ -64,3 +96,4 @@ DO $$
64 END IF; 96 END IF;
65 END; 97 END;
66 $$; 98 $$;
  99 +
  1 +--
  2 +-- Copyright © 2016-2021 The Thingsboard Authors
  3 +--
  4 +-- Licensed under the Apache License, Version 2.0 (the "License");
  5 +-- you may not use this file except in compliance with the License.
  6 +-- You may obtain a copy of the License at
  7 +--
  8 +-- http://www.apache.org/licenses/LICENSE-2.0
  9 +--
  10 +-- Unless required by applicable law or agreed to in writing, software
  11 +-- distributed under the License is distributed on an "AS IS" BASIS,
  12 +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +-- See the License for the specific language governing permissions and
  14 +-- limitations under the License.
  15 +--
  16 +
  17 +CREATE OR REPLACE PROCEDURE cleanup_edge_events_by_ttl(IN ttl bigint, INOUT deleted bigint)
  18 + LANGUAGE plpgsql AS
  19 +$$
  20 +DECLARE
  21 + ttl_ts bigint;
  22 + ttl_deleted_count bigint DEFAULT 0;
  23 +BEGIN
  24 + IF ttl > 0 THEN
  25 + ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint;
  26 + EXECUTE format(
  27 + 'WITH deleted AS (DELETE FROM edge_event WHERE ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted', ttl_ts) into ttl_deleted_count;
  28 + END IF;
  29 + RAISE NOTICE 'Edge events removed by ttl: %', ttl_deleted_count;
  30 + deleted := ttl_deleted_count;
  31 +END
  32 +$$;
@@ -46,6 +46,7 @@ import org.thingsboard.server.common.msg.TbMsg; @@ -46,6 +46,7 @@ import org.thingsboard.server.common.msg.TbMsg;
46 import org.thingsboard.server.common.msg.queue.ServiceType; 46 import org.thingsboard.server.common.msg.queue.ServiceType;
47 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 47 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
48 import org.thingsboard.server.common.msg.tools.TbRateLimits; 48 import org.thingsboard.server.common.msg.tools.TbRateLimits;
  49 +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
49 import org.thingsboard.server.dao.asset.AssetService; 50 import org.thingsboard.server.dao.asset.AssetService;
50 import org.thingsboard.server.dao.attributes.AttributesService; 51 import org.thingsboard.server.dao.attributes.AttributesService;
51 import org.thingsboard.server.dao.audit.AuditLogService; 52 import org.thingsboard.server.dao.audit.AuditLogService;
@@ -54,6 +55,8 @@ import org.thingsboard.server.dao.customer.CustomerService; @@ -54,6 +55,8 @@ import org.thingsboard.server.dao.customer.CustomerService;
54 import org.thingsboard.server.dao.dashboard.DashboardService; 55 import org.thingsboard.server.dao.dashboard.DashboardService;
55 import org.thingsboard.server.dao.device.ClaimDevicesService; 56 import org.thingsboard.server.dao.device.ClaimDevicesService;
56 import org.thingsboard.server.dao.device.DeviceService; 57 import org.thingsboard.server.dao.device.DeviceService;
  58 +import org.thingsboard.server.dao.edge.EdgeEventService;
  59 +import org.thingsboard.server.dao.edge.EdgeService;
57 import org.thingsboard.server.dao.entityview.EntityViewService; 60 import org.thingsboard.server.dao.entityview.EntityViewService;
58 import org.thingsboard.server.dao.event.EventService; 61 import org.thingsboard.server.dao.event.EventService;
59 import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor; 62 import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor;
@@ -69,7 +72,7 @@ import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; @@ -69,7 +72,7 @@ import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
69 import org.thingsboard.server.queue.usagestats.TbApiUsageClient; 72 import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
70 import org.thingsboard.server.service.apiusage.TbApiUsageStateService; 73 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
71 import org.thingsboard.server.service.component.ComponentDiscoveryService; 74 import org.thingsboard.server.service.component.ComponentDiscoveryService;
72 -import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; 75 +import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
73 import org.thingsboard.server.service.executors.DbCallbackExecutorService; 76 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
74 import org.thingsboard.server.service.executors.ExternalCallExecutorService; 77 import org.thingsboard.server.service.executors.ExternalCallExecutorService;
75 import org.thingsboard.server.service.executors.SharedEventLoopGroupService; 78 import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
@@ -296,6 +299,18 @@ public class ActorSystemContext { @@ -296,6 +299,18 @@ public class ActorSystemContext {
296 @Getter 299 @Getter
297 private TbCoreDeviceRpcService tbCoreDeviceRpcService; 300 private TbCoreDeviceRpcService tbCoreDeviceRpcService;
298 301
  302 + @Lazy
  303 + @Autowired(required = false)
  304 + @Getter private EdgeService edgeService;
  305 +
  306 + @Lazy
  307 + @Autowired(required = false)
  308 + @Getter private EdgeEventService edgeEventService;
  309 +
  310 + @Lazy
  311 + @Autowired(required = false)
  312 + @Getter private EdgeRpcService edgeRpcService;
  313 +
299 @Value("${actors.session.max_concurrent_sessions_per_device:1}") 314 @Value("${actors.session.max_concurrent_sessions_per_device:1}")
300 @Getter 315 @Getter
301 private long maxConcurrentSessionsPerDevice; 316 private long maxConcurrentSessionsPerDevice;
@@ -320,6 +335,9 @@ public class ActorSystemContext { @@ -320,6 +335,9 @@ public class ActorSystemContext {
320 @Getter 335 @Getter
321 private long statisticsPersistFrequency; 336 private long statisticsPersistFrequency;
322 337
  338 + @Value("${edges.enabled}")
  339 + @Getter
  340 + private boolean edgesEnabled;
323 341
324 @Scheduled(fixedDelayString = "${actors.statistics.js_print_interval_ms}") 342 @Scheduled(fixedDelayString = "${actors.statistics.js_print_interval_ms}")
325 public void printStats() { 343 public void printStats() {
@@ -35,10 +35,12 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -35,10 +35,12 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
35 import org.thingsboard.server.common.msg.MsgType; 35 import org.thingsboard.server.common.msg.MsgType;
36 import org.thingsboard.server.common.msg.TbActorMsg; 36 import org.thingsboard.server.common.msg.TbActorMsg;
37 import org.thingsboard.server.common.msg.aware.TenantAwareMsg; 37 import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
  38 +import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg;
38 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 39 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
39 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; 40 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
40 import org.thingsboard.server.common.msg.queue.RuleEngineException; 41 import org.thingsboard.server.common.msg.queue.RuleEngineException;
41 import org.thingsboard.server.common.msg.queue.ServiceType; 42 import org.thingsboard.server.common.msg.queue.ServiceType;
  43 +import org.thingsboard.server.dao.model.ModelConstants;
42 import org.thingsboard.server.dao.tenant.TenantService; 44 import org.thingsboard.server.dao.tenant.TenantService;
43 import org.thingsboard.server.dao.tenant.TbTenantProfileCache; 45 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
44 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; 46 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
@@ -89,10 +91,15 @@ public class AppActor extends ContextAwareActor { @@ -89,10 +91,15 @@ public class AppActor extends ContextAwareActor {
89 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: 91 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
90 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: 92 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
91 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: 93 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
  94 + case DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG:
92 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: 95 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
  96 + case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
93 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: 97 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
94 onToDeviceActorMsg((TenantAwareMsg) msg, true); 98 onToDeviceActorMsg((TenantAwareMsg) msg, true);
95 break; 99 break;
  100 + case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG:
  101 + onToTenantActorMsg((EdgeEventUpdateMsg) msg);
  102 + break;
96 default: 103 default:
97 return false; 104 return false;
98 } 105 }
@@ -192,6 +199,20 @@ public class AppActor extends ContextAwareActor { @@ -192,6 +199,20 @@ public class AppActor extends ContextAwareActor {
192 () -> new TenantActor.ActorCreator(systemContext, tenantId)); 199 () -> new TenantActor.ActorCreator(systemContext, tenantId));
193 } 200 }
194 201
  202 + private void onToTenantActorMsg(EdgeEventUpdateMsg msg) {
  203 + TbActorRef target = null;
  204 + if (ModelConstants.SYSTEM_TENANT.equals(msg.getTenantId())) {
  205 + log.warn("Message has system tenant id: {}", msg);
  206 + } else {
  207 + target = getOrCreateTenantActor(msg.getTenantId());
  208 + }
  209 + if (target != null) {
  210 + target.tellWithHighPriority(msg);
  211 + } else {
  212 + log.debug("[{}] Invalid edge event update msg: {}", msg.getTenantId(), msg);
  213 + }
  214 + }
  215 +
195 public static class ActorCreator extends ContextBasedCreator { 216 public static class ActorCreator extends ContextBasedCreator {
196 217
197 public ActorCreator(ActorSystemContext context) { 218 public ActorCreator(ActorSystemContext context) {
@@ -17,6 +17,7 @@ package org.thingsboard.server.actors.device; @@ -17,6 +17,7 @@ package org.thingsboard.server.actors.device;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; 19 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
  20 +import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
20 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; 21 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
21 import org.thingsboard.server.actors.ActorSystemContext; 22 import org.thingsboard.server.actors.ActorSystemContext;
22 import org.thingsboard.server.actors.TbActorCtx; 23 import org.thingsboard.server.actors.TbActorCtx;
@@ -26,6 +27,7 @@ import org.thingsboard.server.common.data.id.DeviceId; @@ -26,6 +27,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
26 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
27 import org.thingsboard.server.common.msg.TbActorMsg; 28 import org.thingsboard.server.common.msg.TbActorMsg;
28 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; 29 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
  30 +import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
29 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; 31 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
30 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; 32 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
31 33
@@ -70,12 +72,18 @@ public class DeviceActor extends ContextAwareActor { @@ -70,12 +72,18 @@ public class DeviceActor extends ContextAwareActor {
70 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: 72 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
71 processor.processRpcRequest(ctx, (ToDeviceRpcRequestActorMsg) msg); 73 processor.processRpcRequest(ctx, (ToDeviceRpcRequestActorMsg) msg);
72 break; 74 break;
  75 + case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  76 + processor.processRpcResponsesFromEdge(ctx, (FromDeviceRpcResponseActorMsg) msg);
  77 + break;
73 case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG: 78 case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG:
74 processor.processServerSideRpcTimeout(ctx, (DeviceActorServerSideRpcTimeoutMsg) msg); 79 processor.processServerSideRpcTimeout(ctx, (DeviceActorServerSideRpcTimeoutMsg) msg);
75 break; 80 break;
76 case SESSION_TIMEOUT_MSG: 81 case SESSION_TIMEOUT_MSG:
77 processor.checkSessionsTimeout(); 82 processor.checkSessionsTimeout();
78 break; 83 break;
  84 + case DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG:
  85 + processor.processEdgeUpdate((DeviceEdgeUpdateMsg) msg);
  86 + break;
79 default: 87 default:
80 return false; 88 return false;
81 } 89 }
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.actors.device; 16 package org.thingsboard.server.actors.device;
17 17
  18 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 import com.google.common.util.concurrent.FutureCallback; 19 import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.Futures; 20 import com.google.common.util.concurrent.Futures;
20 import com.google.common.util.concurrent.ListenableFuture; 21 import com.google.common.util.concurrent.ListenableFuture;
@@ -24,6 +25,7 @@ import lombok.extern.slf4j.Slf4j; @@ -24,6 +25,7 @@ import lombok.extern.slf4j.Slf4j;
24 import org.apache.commons.collections.CollectionUtils; 25 import org.apache.commons.collections.CollectionUtils;
25 import org.thingsboard.rule.engine.api.RpcError; 26 import org.thingsboard.rule.engine.api.RpcError;
26 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; 27 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
  28 +import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
27 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg; 29 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
28 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; 30 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
29 import org.thingsboard.server.actors.ActorSystemContext; 31 import org.thingsboard.server.actors.ActorSystemContext;
@@ -31,11 +33,17 @@ import org.thingsboard.server.actors.TbActorCtx; @@ -31,11 +33,17 @@ import org.thingsboard.server.actors.TbActorCtx;
31 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; 33 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
32 import org.thingsboard.server.common.data.DataConstants; 34 import org.thingsboard.server.common.data.DataConstants;
33 import org.thingsboard.server.common.data.Device; 35 import org.thingsboard.server.common.data.Device;
  36 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  37 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  38 +import org.thingsboard.server.common.data.edge.EdgeEventType;
34 import org.thingsboard.server.common.data.id.DeviceId; 39 import org.thingsboard.server.common.data.id.DeviceId;
  40 +import org.thingsboard.server.common.data.id.EdgeId;
35 import org.thingsboard.server.common.data.id.TenantId; 41 import org.thingsboard.server.common.data.id.TenantId;
36 import org.thingsboard.server.common.data.kv.AttributeKey; 42 import org.thingsboard.server.common.data.kv.AttributeKey;
37 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 43 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
38 import org.thingsboard.server.common.data.kv.KvEntry; 44 import org.thingsboard.server.common.data.kv.KvEntry;
  45 +import org.thingsboard.server.common.data.relation.EntityRelation;
  46 +import org.thingsboard.server.common.data.relation.RelationTypeGroup;
39 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; 47 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
40 import org.thingsboard.server.common.data.security.DeviceCredentials; 48 import org.thingsboard.server.common.data.security.DeviceCredentials;
41 import org.thingsboard.server.common.data.security.DeviceCredentialsType; 49 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
@@ -68,6 +76,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceAct @@ -68,6 +76,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceAct
68 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; 76 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
69 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; 77 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
70 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; 78 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
  79 +import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
71 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; 80 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
72 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; 81 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
73 82
@@ -103,6 +112,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -103,6 +112,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
103 private String deviceName; 112 private String deviceName;
104 private String deviceType; 113 private String deviceType;
105 private TbMsgMetaData defaultMetaData; 114 private TbMsgMetaData defaultMetaData;
  115 + private EdgeId edgeId;
106 116
107 DeviceActorMessageProcessor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) { 117 DeviceActorMessageProcessor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {
108 super(systemContext); 118 super(systemContext);
@@ -125,12 +135,32 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -125,12 +135,32 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
125 this.defaultMetaData = new TbMsgMetaData(); 135 this.defaultMetaData = new TbMsgMetaData();
126 this.defaultMetaData.putValue("deviceName", deviceName); 136 this.defaultMetaData.putValue("deviceName", deviceName);
127 this.defaultMetaData.putValue("deviceType", deviceType); 137 this.defaultMetaData.putValue("deviceType", deviceType);
  138 + if (systemContext.isEdgesEnabled()) {
  139 + this.edgeId = findRelatedEdgeId();
  140 + }
128 return true; 141 return true;
129 } else { 142 } else {
130 return false; 143 return false;
131 } 144 }
132 } 145 }
133 146
  147 + private EdgeId findRelatedEdgeId() {
  148 + List<EntityRelation> result =
  149 + systemContext.getRelationService().findByToAndType(tenantId, deviceId, EntityRelation.EDGE_TYPE, RelationTypeGroup.COMMON);
  150 + if (result != null && result.size() > 0) {
  151 + EntityRelation relationToEdge = result.get(0);
  152 + if (relationToEdge.getFrom() != null && relationToEdge.getFrom().getId() != null) {
  153 + log.trace("[{}][{}] found edge [{}] for device", tenantId, deviceId, relationToEdge.getFrom().getId());
  154 + return new EdgeId(relationToEdge.getFrom().getId());
  155 + } else {
  156 + log.trace("[{}][{}] edge relation is empty {}", tenantId, deviceId, relationToEdge);
  157 + }
  158 + } else {
  159 + log.trace("[{}][{}] device doesn't have any related edge", tenantId, deviceId);
  160 + }
  161 + return null;
  162 + }
  163 +
134 void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) { 164 void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) {
135 ToDeviceRpcRequest request = msg.getMsg(); 165 ToDeviceRpcRequest request = msg.getMsg();
136 ToDeviceRpcRequestBody body = request.getBody(); 166 ToDeviceRpcRequestBody body = request.getBody();
@@ -143,15 +173,22 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -143,15 +173,22 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
143 return; 173 return;
144 } 174 }
145 175
146 - boolean sent = rpcSubscriptions.size() > 0;  
147 - Set<UUID> syncSessionSet = new HashSet<>();  
148 - rpcSubscriptions.forEach((key, value) -> {  
149 - sendToTransport(rpcRequest, key, value.getNodeId());  
150 - if (SessionType.SYNC == value.getType()) {  
151 - syncSessionSet.add(key);  
152 - }  
153 - });  
154 - syncSessionSet.forEach(rpcSubscriptions::remove); 176 + boolean sent;
  177 + if (systemContext.isEdgesEnabled() && edgeId != null) {
  178 + log.debug("[{}][{}] device is related to edge [{}]. Saving RPC request to edge queue", tenantId, deviceId, edgeId.getId());
  179 + saveRpcRequestToEdgeQueue(request, rpcRequest.getRequestId());
  180 + sent = true;
  181 + } else {
  182 + sent = rpcSubscriptions.size() > 0;
  183 + Set<UUID> syncSessionSet = new HashSet<>();
  184 + rpcSubscriptions.forEach((key, value) -> {
  185 + sendToTransport(rpcRequest, key, value.getNodeId());
  186 + if (SessionType.SYNC == value.getType()) {
  187 + syncSessionSet.add(key);
  188 + }
  189 + });
  190 + syncSessionSet.forEach(rpcSubscriptions::remove);
  191 + }
155 192
156 if (request.isOneway() && sent) { 193 if (request.isOneway() && sent) {
157 log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId()); 194 log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
@@ -166,6 +203,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -166,6 +203,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
166 } 203 }
167 } 204 }
168 205
  206 + void processRpcResponsesFromEdge(TbActorCtx context, FromDeviceRpcResponseActorMsg responseMsg) {
  207 + log.debug("[{}] Processing rpc command response from edge session", deviceId);
  208 + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
  209 + boolean success = requestMd != null;
  210 + if (success) {
  211 + systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(responseMsg.getMsg());
  212 + } else {
  213 + log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
  214 + }
  215 + }
  216 +
169 private void registerPendingRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) { 217 private void registerPendingRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {
170 toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent)); 218 toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));
171 DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout); 219 DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout);
@@ -498,6 +546,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -498,6 +546,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
498 this.defaultMetaData.putValue("deviceType", deviceType); 546 this.defaultMetaData.putValue("deviceType", deviceType);
499 } 547 }
500 548
  549 + void processEdgeUpdate(DeviceEdgeUpdateMsg msg) {
  550 + log.trace("[{}] Processing edge update {}", deviceId, msg);
  551 + this.edgeId = msg.getEdgeId();
  552 + }
  553 +
501 private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) { 554 private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) {
502 ToTransportMsg msg = ToTransportMsg.newBuilder() 555 ToTransportMsg msg = ToTransportMsg.newBuilder()
503 .setSessionIdMSB(sessionInfo.getSessionIdMSB()) 556 .setSessionIdMSB(sessionInfo.getSessionIdMSB())
@@ -530,6 +583,36 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -530,6 +583,36 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
530 systemContext.getTbCoreToTransportService().process(nodeId, msg); 583 systemContext.getTbCoreToTransportService().process(nodeId, msg);
531 } 584 }
532 585
  586 + private void saveRpcRequestToEdgeQueue(ToDeviceRpcRequest msg, Integer requestId) {
  587 + EdgeEvent edgeEvent = new EdgeEvent();
  588 + edgeEvent.setTenantId(tenantId);
  589 + edgeEvent.setAction(EdgeEventActionType.RPC_CALL);
  590 + edgeEvent.setEntityId(deviceId.getId());
  591 + edgeEvent.setType(EdgeEventType.DEVICE);
  592 +
  593 + ObjectNode body = mapper.createObjectNode();
  594 + body.put("requestId", requestId);
  595 + body.put("requestUUID", msg.getId().toString());
  596 + body.put("oneway", msg.isOneway());
  597 + body.put("expirationTime", msg.getExpirationTime());
  598 + body.put("method", msg.getBody().getMethod());
  599 + body.put("params", msg.getBody().getParams());
  600 + edgeEvent.setBody(body);
  601 +
  602 + edgeEvent.setEdgeId(edgeId);
  603 + ListenableFuture<EdgeEvent> future = systemContext.getEdgeEventService().saveAsync(edgeEvent);
  604 + Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
  605 + @Override
  606 + public void onSuccess( EdgeEvent result) {
  607 + systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
  608 + }
  609 +
  610 + @Override
  611 + public void onFailure(Throwable t) {
  612 + log.warn("[{}] Can't save edge event [{}] for edge [{}]", tenantId.getId(), edgeEvent, edgeId.getId(), t);
  613 + }
  614 + }, systemContext.getDbCallbackExecutor());
  615 + }
533 616
534 private List<TsKvProto> toTsKvProtos(@Nullable List<AttributeKvEntry> result) { 617 private List<TsKvProto> toTsKvProtos(@Nullable List<AttributeKvEntry> result) {
535 List<TsKvProto> clientAttributes; 618 List<TsKvProto> clientAttributes;
@@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.TenantProfile; @@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.TenantProfile;
42 import org.thingsboard.server.common.data.alarm.Alarm; 42 import org.thingsboard.server.common.data.alarm.Alarm;
43 import org.thingsboard.server.common.data.asset.Asset; 43 import org.thingsboard.server.common.data.asset.Asset;
44 import org.thingsboard.server.common.data.id.DeviceId; 44 import org.thingsboard.server.common.data.id.DeviceId;
  45 +import org.thingsboard.server.common.data.id.EdgeId;
45 import org.thingsboard.server.common.data.id.EntityId; 46 import org.thingsboard.server.common.data.id.EntityId;
46 import org.thingsboard.server.common.data.id.RuleChainId; 47 import org.thingsboard.server.common.data.id.RuleChainId;
47 import org.thingsboard.server.common.data.id.RuleNodeId; 48 import org.thingsboard.server.common.data.id.RuleNodeId;
@@ -62,6 +63,8 @@ import org.thingsboard.server.dao.cassandra.CassandraCluster; @@ -62,6 +63,8 @@ import org.thingsboard.server.dao.cassandra.CassandraCluster;
62 import org.thingsboard.server.dao.customer.CustomerService; 63 import org.thingsboard.server.dao.customer.CustomerService;
63 import org.thingsboard.server.dao.dashboard.DashboardService; 64 import org.thingsboard.server.dao.dashboard.DashboardService;
64 import org.thingsboard.server.dao.device.DeviceService; 65 import org.thingsboard.server.dao.device.DeviceService;
  66 +import org.thingsboard.server.dao.edge.EdgeEventService;
  67 +import org.thingsboard.server.dao.edge.EdgeService;
65 import org.thingsboard.server.dao.entityview.EntityViewService; 68 import org.thingsboard.server.dao.entityview.EntityViewService;
66 import org.thingsboard.server.dao.nosql.CassandraStatementTask; 69 import org.thingsboard.server.dao.nosql.CassandraStatementTask;
67 import org.thingsboard.server.dao.nosql.TbResultSetFuture; 70 import org.thingsboard.server.dao.nosql.TbResultSetFuture;
@@ -319,6 +322,11 @@ class DefaultTbContext implements TbContext { @@ -319,6 +322,11 @@ class DefaultTbContext implements TbContext {
319 return entityActionMsg(alarm, alarm.getId(), ruleNodeId, action, queueName, ruleChainId); 322 return entityActionMsg(alarm, alarm.getId(), ruleNodeId, action, queueName, ruleChainId);
320 } 323 }
321 324
  325 + @Override
  326 + public void onEdgeEventUpdate(TenantId tenantId, EdgeId edgeId) {
  327 + mainCtx.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
  328 + }
  329 +
322 public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action) { 330 public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action) {
323 return entityActionMsg(entity, id, ruleNodeId, action, ServiceQueue.MAIN, null); 331 return entityActionMsg(entity, id, ruleNodeId, action, ServiceQueue.MAIN, null);
324 } 332 }
@@ -478,6 +486,16 @@ class DefaultTbContext implements TbContext { @@ -478,6 +486,16 @@ class DefaultTbContext implements TbContext {
478 } 486 }
479 487
480 @Override 488 @Override
  489 + public EdgeService getEdgeService() {
  490 + return mainCtx.getEdgeService();
  491 + }
  492 +
  493 + @Override
  494 + public EdgeEventService getEdgeEventService() {
  495 + return mainCtx.getEdgeEventService();
  496 + }
  497 +
  498 + @Override
481 public EventLoopGroup getSharedEventLoop() { 499 public EventLoopGroup getSharedEventLoop() {
482 return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup(); 500 return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup();
483 } 501 }
@@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
32 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; 32 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
33 import org.thingsboard.server.common.data.relation.EntityRelation; 33 import org.thingsboard.server.common.data.relation.EntityRelation;
34 import org.thingsboard.server.common.data.rule.RuleChain; 34 import org.thingsboard.server.common.data.rule.RuleChain;
  35 +import org.thingsboard.server.common.data.rule.RuleChainType;
35 import org.thingsboard.server.common.data.rule.RuleNode; 36 import org.thingsboard.server.common.data.rule.RuleNode;
36 import org.thingsboard.server.common.msg.TbMsg; 37 import org.thingsboard.server.common.msg.TbMsg;
37 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 38 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
@@ -99,7 +100,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -99,7 +100,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
99 public void start(TbActorCtx context) { 100 public void start(TbActorCtx context) {
100 if (!started) { 101 if (!started) {
101 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); 102 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
102 - if (ruleChain != null) { 103 + if (ruleChain != null && RuleChainType.CORE.equals(ruleChain.getType())) {
103 List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId); 104 List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
104 log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); 105 log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
105 // Creating and starting the actors; 106 // Creating and starting the actors;
@@ -119,7 +120,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -119,7 +120,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
119 @Override 120 @Override
120 public void onUpdate(TbActorCtx context) { 121 public void onUpdate(TbActorCtx context) {
121 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); 122 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
122 - if (ruleChain != null) { 123 + if (ruleChain != null && RuleChainType.CORE.equals(ruleChain.getType())) {
123 ruleChainName = ruleChain.getName(); 124 ruleChainName = ruleChain.getName();
124 List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId); 125 List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
125 log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); 126 log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
@@ -23,13 +23,13 @@ import org.thingsboard.server.actors.TbEntityActorId; @@ -23,13 +23,13 @@ import org.thingsboard.server.actors.TbEntityActorId;
23 import org.thingsboard.server.actors.TbEntityTypeActorIdPredicate; 23 import org.thingsboard.server.actors.TbEntityTypeActorIdPredicate;
24 import org.thingsboard.server.actors.service.ContextAwareActor; 24 import org.thingsboard.server.actors.service.ContextAwareActor;
25 import org.thingsboard.server.actors.service.DefaultActorService; 25 import org.thingsboard.server.actors.service.DefaultActorService;
26 -import org.thingsboard.server.actors.tenant.TenantActor;  
27 import org.thingsboard.server.common.data.EntityType; 26 import org.thingsboard.server.common.data.EntityType;
28 import org.thingsboard.server.common.data.id.EntityId; 27 import org.thingsboard.server.common.data.id.EntityId;
29 import org.thingsboard.server.common.data.id.RuleChainId; 28 import org.thingsboard.server.common.data.id.RuleChainId;
30 import org.thingsboard.server.common.data.id.TenantId; 29 import org.thingsboard.server.common.data.id.TenantId;
31 import org.thingsboard.server.common.data.page.PageDataIterable; 30 import org.thingsboard.server.common.data.page.PageDataIterable;
32 import org.thingsboard.server.common.data.rule.RuleChain; 31 import org.thingsboard.server.common.data.rule.RuleChain;
  32 +import org.thingsboard.server.common.data.rule.RuleChainType;
33 import org.thingsboard.server.common.msg.TbActorMsg; 33 import org.thingsboard.server.common.msg.TbActorMsg;
34 import org.thingsboard.server.dao.rule.RuleChainService; 34 import org.thingsboard.server.dao.rule.RuleChainService;
35 35
@@ -55,7 +55,7 @@ public abstract class RuleChainManagerActor extends ContextAwareActor { @@ -55,7 +55,7 @@ public abstract class RuleChainManagerActor extends ContextAwareActor {
55 } 55 }
56 56
57 protected void initRuleChains() { 57 protected void initRuleChains() {
58 - for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChains(tenantId, link), ContextAwareActor.ENTITY_PACK_LIMIT)) { 58 + for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, link), ContextAwareActor.ENTITY_PACK_LIMIT)) {
59 RuleChainId ruleChainId = ruleChain.getId(); 59 RuleChainId ruleChainId = ruleChain.getId();
60 log.debug("[{}|{}] Creating rule chain actor", ruleChainId.getEntityType(), ruleChain.getId()); 60 log.debug("[{}|{}] Creating rule chain actor", ruleChainId.getEntityType(), ruleChain.getId());
61 TbActorRef actorRef = getOrCreateActor(ruleChainId, id -> ruleChain); 61 TbActorRef actorRef = getOrCreateActor(ruleChainId, id -> ruleChain);
@@ -65,13 +65,13 @@ public abstract class RuleChainManagerActor extends ContextAwareActor { @@ -65,13 +65,13 @@ public abstract class RuleChainManagerActor extends ContextAwareActor {
65 } 65 }
66 66
67 protected void destroyRuleChains() { 67 protected void destroyRuleChains() {
68 - for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChains(tenantId, link), ContextAwareActor.ENTITY_PACK_LIMIT)) { 68 + for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, link), ContextAwareActor.ENTITY_PACK_LIMIT)) {
69 ctx.stop(new TbEntityActorId(ruleChain.getId())); 69 ctx.stop(new TbEntityActorId(ruleChain.getId()));
70 } 70 }
71 } 71 }
72 72
73 protected void visit(RuleChain entity, TbActorRef actorRef) { 73 protected void visit(RuleChain entity, TbActorRef actorRef) {
74 - if (entity != null && entity.isRoot()) { 74 + if (entity != null && entity.isRoot() && entity.getType().equals(RuleChainType.CORE)) {
75 rootChain = entity; 75 rootChain = entity;
76 rootChainActor = actorRef; 76 rootChainActor = actorRef;
77 } 77 }
@@ -33,21 +33,27 @@ import org.thingsboard.server.common.data.ApiUsageState; @@ -33,21 +33,27 @@ import org.thingsboard.server.common.data.ApiUsageState;
33 import org.thingsboard.server.common.data.EntityType; 33 import org.thingsboard.server.common.data.EntityType;
34 import org.thingsboard.server.common.data.Tenant; 34 import org.thingsboard.server.common.data.Tenant;
35 import org.thingsboard.server.common.data.TenantProfile; 35 import org.thingsboard.server.common.data.TenantProfile;
  36 +import org.thingsboard.server.common.data.edge.Edge;
36 import org.thingsboard.server.common.data.id.DeviceId; 37 import org.thingsboard.server.common.data.id.DeviceId;
  38 +import org.thingsboard.server.common.data.id.EdgeId;
37 import org.thingsboard.server.common.data.id.EntityId; 39 import org.thingsboard.server.common.data.id.EntityId;
38 import org.thingsboard.server.common.data.id.RuleChainId; 40 import org.thingsboard.server.common.data.id.RuleChainId;
39 import org.thingsboard.server.common.data.id.TenantId; 41 import org.thingsboard.server.common.data.id.TenantId;
  42 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
40 import org.thingsboard.server.common.data.rule.RuleChain; 43 import org.thingsboard.server.common.data.rule.RuleChain;
  44 +import org.thingsboard.server.common.data.rule.RuleChainType;
41 import org.thingsboard.server.common.msg.MsgType; 45 import org.thingsboard.server.common.msg.MsgType;
42 import org.thingsboard.server.common.msg.TbActorMsg; 46 import org.thingsboard.server.common.msg.TbActorMsg;
43 import org.thingsboard.server.common.msg.TbMsg; 47 import org.thingsboard.server.common.msg.TbMsg;
44 import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; 48 import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
45 import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg; 49 import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
  50 +import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg;
46 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 51 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
47 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; 52 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
48 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; 53 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
49 import org.thingsboard.server.common.msg.queue.RuleEngineException; 54 import org.thingsboard.server.common.msg.queue.RuleEngineException;
50 import org.thingsboard.server.common.msg.queue.ServiceType; 55 import org.thingsboard.server.common.msg.queue.ServiceType;
  56 +import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
51 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; 57 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
52 58
53 import java.util.List; 59 import java.util.List;
@@ -155,13 +161,18 @@ public class TenantActor extends RuleChainManagerActor { @@ -155,13 +161,18 @@ public class TenantActor extends RuleChainManagerActor {
155 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: 161 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
156 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: 162 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
157 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: 163 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
  164 + case DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG:
158 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: 165 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
  166 + case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
159 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: 167 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
160 onToDeviceActorMsg((DeviceAwareMsg) msg, true); 168 onToDeviceActorMsg((DeviceAwareMsg) msg, true);
161 break; 169 break;
162 case RULE_CHAIN_TO_RULE_CHAIN_MSG: 170 case RULE_CHAIN_TO_RULE_CHAIN_MSG:
163 onRuleChainMsg((RuleChainAwareMsg) msg); 171 onRuleChainMsg((RuleChainAwareMsg) msg);
164 break; 172 break;
  173 + case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG:
  174 + onToEdgeSessionMsg((EdgeEventUpdateMsg) msg);
  175 + break;
165 default: 176 default:
166 return false; 177 return false;
167 } 178 }
@@ -230,14 +241,26 @@ public class TenantActor extends RuleChainManagerActor { @@ -230,14 +241,26 @@ public class TenantActor extends RuleChainManagerActor {
230 log.info("[{}] Received API state update. Going to ENABLE Rule Engine execution.", tenantId); 241 log.info("[{}] Received API state update. Going to ENABLE Rule Engine execution.", tenantId);
231 initRuleChains(); 242 initRuleChains();
232 } 243 }
233 - }  
234 - if (isRuleEngineForCurrentTenant) { 244 + } else if (msg.getEntityId().getEntityType() == EntityType.EDGE) {
  245 + EdgeId edgeId = new EdgeId(msg.getEntityId().getId());
  246 + EdgeRpcService edgeRpcService = systemContext.getEdgeRpcService();
  247 + if (msg.getEvent() == ComponentLifecycleEvent.DELETED) {
  248 + edgeRpcService.deleteEdge(edgeId);
  249 + } else {
  250 + Edge edge = systemContext.getEdgeService().findEdgeById(tenantId, edgeId);
  251 + if (msg.getEvent() == ComponentLifecycleEvent.UPDATED) {
  252 + edgeRpcService.updateEdge(edge);
  253 + }
  254 + }
  255 + } else if (isRuleEngineForCurrentTenant) {
235 TbActorRef target = getEntityActorRef(msg.getEntityId()); 256 TbActorRef target = getEntityActorRef(msg.getEntityId());
236 if (target != null) { 257 if (target != null) {
237 if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) { 258 if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) {
238 RuleChain ruleChain = systemContext.getRuleChainService(). 259 RuleChain ruleChain = systemContext.getRuleChainService().
239 findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId())); 260 findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId()));
240 - visit(ruleChain, target); 261 + if (ruleChain != null && RuleChainType.CORE.equals(ruleChain.getType())) {
  262 + visit(ruleChain, target);
  263 + }
241 } 264 }
242 target.tellWithHighPriority(msg); 265 target.tellWithHighPriority(msg);
243 } else { 266 } else {
@@ -252,6 +275,11 @@ public class TenantActor extends RuleChainManagerActor { @@ -252,6 +275,11 @@ public class TenantActor extends RuleChainManagerActor {
252 () -> new DeviceActorCreator(systemContext, tenantId, deviceId)); 275 () -> new DeviceActorCreator(systemContext, tenantId, deviceId));
253 } 276 }
254 277
  278 + private void onToEdgeSessionMsg(EdgeEventUpdateMsg msg) {
  279 + log.trace("[{}] onToEdgeSessionMsg [{}]", msg.getTenantId(), msg);
  280 + systemContext.getEdgeRpcService().onEdgeEvent(msg.getEdgeId());
  281 + }
  282 +
255 public static class ActorCreator extends ContextBasedCreator { 283 public static class ActorCreator extends ContextBasedCreator {
256 284
257 private final TenantId tenantId; 285 private final TenantId tenantId;
@@ -71,7 +71,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt @@ -71,7 +71,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
71 public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login"; 71 public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";
72 public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public"; 72 public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public";
73 public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token"; 73 public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token";
74 - protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**"}; 74 + protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**", "/api/license/**"};
75 public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**"; 75 public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";
76 public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**"; 76 public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**";
77 77
@@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping; @@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
21 @Controller 21 @Controller
22 public class WebConfig { 22 public class WebConfig {
23 23
24 - @RequestMapping(value = "/{path:^(?!api$)(?!assets$)(?!static$)(?!webjars$)[^\\.]*}/**") 24 + @RequestMapping(value = {"/assets", "/assets/", "/{path:^(?!api$)(?!assets$)(?!static$)(?!webjars$)[^\\.]*}/**"})
25 public String redirect() { 25 public String redirect() {
26 return "forward:/index.html"; 26 return "forward:/index.html";
27 } 27 }
@@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
34 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 34 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
35 import org.thingsboard.server.common.data.alarm.AlarmStatus; 35 import org.thingsboard.server.common.data.alarm.AlarmStatus;
36 import org.thingsboard.server.common.data.audit.ActionType; 36 import org.thingsboard.server.common.data.audit.ActionType;
  37 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
37 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 38 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
38 import org.thingsboard.server.common.data.exception.ThingsboardException; 39 import org.thingsboard.server.common.data.exception.ThingsboardException;
39 import org.thingsboard.server.common.data.id.AlarmId; 40 import org.thingsboard.server.common.data.id.AlarmId;
@@ -45,8 +46,6 @@ import org.thingsboard.server.queue.util.TbCoreComponent; @@ -45,8 +46,6 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
45 import org.thingsboard.server.service.security.permission.Operation; 46 import org.thingsboard.server.service.security.permission.Operation;
46 import org.thingsboard.server.service.security.permission.Resource; 47 import org.thingsboard.server.service.security.permission.Resource;
47 48
48 -import java.util.UUID;  
49 -  
50 @RestController 49 @RestController
51 @TbCoreComponent 50 @TbCoreComponent
52 @RequestMapping("/api") 51 @RequestMapping("/api")
@@ -93,6 +92,9 @@ public class AlarmController extends BaseController { @@ -93,6 +92,9 @@ public class AlarmController extends BaseController {
93 logEntityAction(savedAlarm.getOriginator(), savedAlarm, 92 logEntityAction(savedAlarm.getOriginator(), savedAlarm,
94 getCurrentUser().getCustomerId(), 93 getCurrentUser().getCustomerId(),
95 alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 94 alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
  95 +
  96 + sendEntityNotificationMsg(getTenantId(), savedAlarm.getId(), alarm.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
  97 +
96 return savedAlarm; 98 return savedAlarm;
97 } catch (Exception e) { 99 } catch (Exception e) {
98 logEntityAction(emptyId(EntityType.ALARM), alarm, 100 logEntityAction(emptyId(EntityType.ALARM), alarm,
@@ -109,8 +111,11 @@ public class AlarmController extends BaseController { @@ -109,8 +111,11 @@ public class AlarmController extends BaseController {
109 try { 111 try {
110 AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); 112 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
111 checkAlarmId(alarmId, Operation.WRITE); 113 checkAlarmId(alarmId, Operation.WRITE);
  114 +
  115 + sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.DELETED);
  116 +
112 return alarmService.deleteAlarm(getTenantId(), alarmId); 117 return alarmService.deleteAlarm(getTenantId(), alarmId);
113 - } catch (Exception e) { 118 + } catch (Exception e) {
114 throw handleException(e); 119 throw handleException(e);
115 } 120 }
116 } 121 }
@@ -128,6 +133,8 @@ public class AlarmController extends BaseController { @@ -128,6 +133,8 @@ public class AlarmController extends BaseController {
128 alarm.setAckTs(ackTs); 133 alarm.setAckTs(ackTs);
129 alarm.setStatus(alarm.getStatus().isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK); 134 alarm.setStatus(alarm.getStatus().isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK);
130 logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null); 135 logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null);
  136 +
  137 + sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.ALARM_ACK);
131 } catch (Exception e) { 138 } catch (Exception e) {
132 throw handleException(e); 139 throw handleException(e);
133 } 140 }
@@ -146,6 +153,8 @@ public class AlarmController extends BaseController { @@ -146,6 +153,8 @@ public class AlarmController extends BaseController {
146 alarm.setClearTs(clearTs); 153 alarm.setClearTs(clearTs);
147 alarm.setStatus(alarm.getStatus().isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK); 154 alarm.setStatus(alarm.getStatus().isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK);
148 logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null); 155 logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null);
  156 +
  157 + sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.ALARM_CLEAR);
149 } catch (Exception e) { 158 } catch (Exception e) {
150 throw handleException(e); 159 throw handleException(e);
151 } 160 }
@@ -166,7 +175,6 @@ public class AlarmController extends BaseController { @@ -166,7 +175,6 @@ public class AlarmController extends BaseController {
166 @RequestParam(required = false) String sortOrder, 175 @RequestParam(required = false) String sortOrder,
167 @RequestParam(required = false) Long startTime, 176 @RequestParam(required = false) Long startTime,
168 @RequestParam(required = false) Long endTime, 177 @RequestParam(required = false) Long endTime,
169 - @RequestParam(required = false) String offset,  
170 @RequestParam(required = false) Boolean fetchOriginator 178 @RequestParam(required = false) Boolean fetchOriginator
171 ) throws ThingsboardException { 179 ) throws ThingsboardException {
172 checkParameter("EntityId", strEntityId); 180 checkParameter("EntityId", strEntityId);
@@ -180,12 +188,9 @@ public class AlarmController extends BaseController { @@ -180,12 +188,9 @@ public class AlarmController extends BaseController {
180 } 188 }
181 checkEntityId(entityId, Operation.READ); 189 checkEntityId(entityId, Operation.READ);
182 TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); 190 TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
183 - UUID idOffsetUuid = null;  
184 - if (StringUtils.isNotEmpty(offset)) {  
185 - idOffsetUuid = toUUID(offset);  
186 - } 191 +
187 try { 192 try {
188 - return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator, idOffsetUuid)).get()); 193 + return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get());
189 } catch (Exception e) { 194 } catch (Exception e) {
190 throw handleException(e); 195 throw handleException(e);
191 } 196 }
@@ -33,14 +33,18 @@ import org.thingsboard.server.common.data.asset.Asset; @@ -33,14 +33,18 @@ import org.thingsboard.server.common.data.asset.Asset;
33 import org.thingsboard.server.common.data.asset.AssetInfo; 33 import org.thingsboard.server.common.data.asset.AssetInfo;
34 import org.thingsboard.server.common.data.asset.AssetSearchQuery; 34 import org.thingsboard.server.common.data.asset.AssetSearchQuery;
35 import org.thingsboard.server.common.data.audit.ActionType; 35 import org.thingsboard.server.common.data.audit.ActionType;
  36 +import org.thingsboard.server.common.data.edge.Edge;
  37 +import org.thingsboard.server.common.data.edge.EdgeEventType;
36 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 38 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
  39 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
37 import org.thingsboard.server.common.data.exception.ThingsboardException; 40 import org.thingsboard.server.common.data.exception.ThingsboardException;
38 import org.thingsboard.server.common.data.id.AssetId; 41 import org.thingsboard.server.common.data.id.AssetId;
39 import org.thingsboard.server.common.data.id.CustomerId; 42 import org.thingsboard.server.common.data.id.CustomerId;
  43 +import org.thingsboard.server.common.data.id.EdgeId;
40 import org.thingsboard.server.common.data.id.TenantId; 44 import org.thingsboard.server.common.data.id.TenantId;
41 import org.thingsboard.server.common.data.page.PageData; 45 import org.thingsboard.server.common.data.page.PageData;
42 import org.thingsboard.server.common.data.page.PageLink; 46 import org.thingsboard.server.common.data.page.PageLink;
43 -import org.thingsboard.server.common.data.security.Authority; 47 +import org.thingsboard.server.common.data.page.TimePageLink;
44 import org.thingsboard.server.dao.exception.IncorrectParameterException; 48 import org.thingsboard.server.dao.exception.IncorrectParameterException;
45 import org.thingsboard.server.dao.model.ModelConstants; 49 import org.thingsboard.server.dao.model.ModelConstants;
46 import org.thingsboard.server.queue.util.TbCoreComponent; 50 import org.thingsboard.server.queue.util.TbCoreComponent;
@@ -54,6 +58,8 @@ import java.util.stream.Collectors; @@ -54,6 +58,8 @@ import java.util.stream.Collectors;
54 58
55 import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; 59 import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE;
56 60
  61 +import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
  62 +
57 @RestController 63 @RestController
58 @TbCoreComponent 64 @TbCoreComponent
59 @RequestMapping("/api") 65 @RequestMapping("/api")
@@ -98,7 +104,7 @@ public class AssetController extends BaseController { @@ -98,7 +104,7 @@ public class AssetController extends BaseController {
98 104
99 asset.setTenantId(getCurrentUser().getTenantId()); 105 asset.setTenantId(getCurrentUser().getTenantId());
100 106
101 - checkEntity(asset.getId(), asset, Resource.ASSET); 107 + checkEntity(asset.getId(), asset, Resource.ASSET);
102 108
103 Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); 109 Asset savedAsset = checkNotNull(assetService.saveAsset(asset));
104 110
@@ -106,6 +112,10 @@ public class AssetController extends BaseController { @@ -106,6 +112,10 @@ public class AssetController extends BaseController {
106 savedAsset.getCustomerId(), 112 savedAsset.getCustomerId(),
107 asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 113 asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
108 114
  115 + if (asset.getId() != null) {
  116 + sendEntityNotificationMsg(savedAsset.getTenantId(), savedAsset.getId(), EdgeEventActionType.UPDATED);
  117 + }
  118 +
109 return savedAsset; 119 return savedAsset;
110 } catch (Exception e) { 120 } catch (Exception e) {
111 logEntityAction(emptyId(EntityType.ASSET), asset, 121 logEntityAction(emptyId(EntityType.ASSET), asset,
@@ -122,12 +132,16 @@ public class AssetController extends BaseController { @@ -122,12 +132,16 @@ public class AssetController extends BaseController {
122 try { 132 try {
123 AssetId assetId = new AssetId(toUUID(strAssetId)); 133 AssetId assetId = new AssetId(toUUID(strAssetId));
124 Asset asset = checkAssetId(assetId, Operation.DELETE); 134 Asset asset = checkAssetId(assetId, Operation.DELETE);
  135 +
  136 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), assetId);
  137 +
125 assetService.deleteAsset(getTenantId(), assetId); 138 assetService.deleteAsset(getTenantId(), assetId);
126 139
127 logEntityAction(assetId, asset, 140 logEntityAction(assetId, asset,
128 asset.getCustomerId(), 141 asset.getCustomerId(),
129 ActionType.DELETED, null, strAssetId); 142 ActionType.DELETED, null, strAssetId);
130 143
  144 + sendDeleteNotificationMsg(getTenantId(), assetId, relatedEdgeIds);
131 } catch (Exception e) { 145 } catch (Exception e) {
132 logEntityAction(emptyId(EntityType.ASSET), 146 logEntityAction(emptyId(EntityType.ASSET),
133 null, 147 null,
@@ -157,6 +171,9 @@ public class AssetController extends BaseController { @@ -157,6 +171,9 @@ public class AssetController extends BaseController {
157 savedAsset.getCustomerId(), 171 savedAsset.getCustomerId(),
158 ActionType.ASSIGNED_TO_CUSTOMER, null, strAssetId, strCustomerId, customer.getName()); 172 ActionType.ASSIGNED_TO_CUSTOMER, null, strAssetId, strCustomerId, customer.getName());
159 173
  174 + sendEntityAssignToCustomerNotificationMsg(savedAsset.getTenantId(), savedAsset.getId(),
  175 + customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
  176 +
160 return savedAsset; 177 return savedAsset;
161 } catch (Exception e) { 178 } catch (Exception e) {
162 179
@@ -188,6 +205,9 @@ public class AssetController extends BaseController { @@ -188,6 +205,9 @@ public class AssetController extends BaseController {
188 asset.getCustomerId(), 205 asset.getCustomerId(),
189 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strAssetId, customer.getId().toString(), customer.getName()); 206 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strAssetId, customer.getId().toString(), customer.getName());
190 207
  208 + sendEntityAssignToCustomerNotificationMsg(savedAsset.getTenantId(), savedAsset.getId(),
  209 + customer.getId(), EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
  210 +
191 return savedAsset; 211 return savedAsset;
192 } catch (Exception e) { 212 } catch (Exception e) {
193 213
@@ -401,4 +421,113 @@ public class AssetController extends BaseController { @@ -401,4 +421,113 @@ public class AssetController extends BaseController {
401 throw handleException(e); 421 throw handleException(e);
402 } 422 }
403 } 423 }
  424 +
  425 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  426 + @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.POST)
  427 + @ResponseBody
  428 + public Asset assignAssetToEdge(@PathVariable(EDGE_ID) String strEdgeId,
  429 + @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
  430 + checkParameter(EDGE_ID, strEdgeId);
  431 + checkParameter(ASSET_ID, strAssetId);
  432 + try {
  433 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  434 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  435 +
  436 + AssetId assetId = new AssetId(toUUID(strAssetId));
  437 + checkAssetId(assetId, Operation.ASSIGN_TO_EDGE);
  438 +
  439 + Asset savedAsset = checkNotNull(assetService.assignAssetToEdge(getTenantId(), assetId, edgeId));
  440 +
  441 + logEntityAction(assetId, savedAsset,
  442 + savedAsset.getCustomerId(),
  443 + ActionType.ASSIGNED_TO_EDGE, null, strAssetId, strEdgeId, edge.getName());
  444 +
  445 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedAsset.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE);
  446 +
  447 + return savedAsset;
  448 + } catch (Exception e) {
  449 +
  450 + logEntityAction(emptyId(EntityType.ASSET), null,
  451 + null,
  452 + ActionType.ASSIGNED_TO_EDGE, e, strAssetId, strEdgeId);
  453 +
  454 + throw handleException(e);
  455 + }
  456 + }
  457 +
  458 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  459 + @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.DELETE)
  460 + @ResponseBody
  461 + public Asset unassignAssetFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
  462 + @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
  463 + checkParameter(EDGE_ID, strEdgeId);
  464 + checkParameter(ASSET_ID, strAssetId);
  465 + try {
  466 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  467 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  468 +
  469 + AssetId assetId = new AssetId(toUUID(strAssetId));
  470 + Asset asset = checkAssetId(assetId, Operation.UNASSIGN_FROM_EDGE);
  471 +
  472 + Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(getTenantId(), assetId, edgeId));
  473 +
  474 + logEntityAction(assetId, asset,
  475 + asset.getCustomerId(),
  476 + ActionType.UNASSIGNED_FROM_EDGE, null, strAssetId, strEdgeId, edge.getName());
  477 +
  478 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedAsset.getId(), EdgeEventActionType.UNASSIGNED_FROM_EDGE);
  479 +
  480 + return savedAsset;
  481 + } catch (Exception e) {
  482 +
  483 + logEntityAction(emptyId(EntityType.ASSET), null,
  484 + null,
  485 + ActionType.UNASSIGNED_FROM_EDGE, e, strAssetId, strEdgeId);
  486 +
  487 + throw handleException(e);
  488 + }
  489 + }
  490 +
  491 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  492 + @RequestMapping(value = "/edge/{edgeId}/assets", params = {"pageSize", "page"}, method = RequestMethod.GET)
  493 + @ResponseBody
  494 + public PageData<Asset> getEdgeAssets(
  495 + @PathVariable(EDGE_ID) String strEdgeId,
  496 + @RequestParam int pageSize,
  497 + @RequestParam int page,
  498 + @RequestParam(required = false) String type,
  499 + @RequestParam(required = false) String textSearch,
  500 + @RequestParam(required = false) String sortProperty,
  501 + @RequestParam(required = false) String sortOrder,
  502 + @RequestParam(required = false) Long startTime,
  503 + @RequestParam(required = false) Long endTime) throws ThingsboardException {
  504 + checkParameter(EDGE_ID, strEdgeId);
  505 + try {
  506 + TenantId tenantId = getCurrentUser().getTenantId();
  507 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  508 + checkEdgeId(edgeId, Operation.READ);
  509 + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
  510 + PageData<Asset> nonFilteredResult;
  511 + if (type != null && type.trim().length() > 0) {
  512 + nonFilteredResult = assetService.findAssetsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink);
  513 + } else {
  514 + nonFilteredResult = assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink);
  515 + }
  516 + List<Asset> filteredAssets = nonFilteredResult.getData().stream().filter(asset -> {
  517 + try {
  518 + accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, Operation.READ, asset.getId(), asset);
  519 + return true;
  520 + } catch (ThingsboardException e) {
  521 + return false;
  522 + }
  523 + }).collect(Collectors.toList());
  524 + PageData<Asset> filteredResult = new PageData<>(filteredAssets,
  525 + nonFilteredResult.getTotalPages(),
  526 + nonFilteredResult.getTotalElements(),
  527 + nonFilteredResult.hasNext());
  528 + return checkNotNull(filteredResult);
  529 + } catch (Exception e) {
  530 + throw handleException(e);
  531 + }
  532 + }
404 } 533 }
@@ -37,6 +37,7 @@ import org.thingsboard.common.util.JacksonUtil; @@ -37,6 +37,7 @@ import org.thingsboard.common.util.JacksonUtil;
37 import org.thingsboard.rule.engine.api.MailService; 37 import org.thingsboard.rule.engine.api.MailService;
38 import org.thingsboard.server.common.data.User; 38 import org.thingsboard.server.common.data.User;
39 import org.thingsboard.server.common.data.audit.ActionType; 39 import org.thingsboard.server.common.data.audit.ActionType;
  40 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
40 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 41 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
41 import org.thingsboard.server.common.data.exception.ThingsboardException; 42 import org.thingsboard.server.common.data.exception.ThingsboardException;
42 import org.thingsboard.server.common.data.id.TenantId; 43 import org.thingsboard.server.common.data.id.TenantId;
@@ -110,6 +111,8 @@ public class AuthController extends BaseController { @@ -110,6 +111,8 @@ public class AuthController extends BaseController {
110 userCredentials.setPassword(passwordEncoder.encode(newPassword)); 111 userCredentials.setPassword(passwordEncoder.encode(newPassword));
111 userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials); 112 userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials);
112 113
  114 + sendEntityNotificationMsg(getTenantId(), userCredentials.getUserId(), EdgeEventActionType.CREDENTIALS_UPDATED);
  115 +
113 eventPublisher.publishEvent(new UserAuthDataChangedEvent(securityUser.getId())); 116 eventPublisher.publishEvent(new UserAuthDataChangedEvent(securityUser.getId()));
114 ObjectNode response = JacksonUtil.newObjectNode(); 117 ObjectNode response = JacksonUtil.newObjectNode();
115 response.put("token", tokenFactory.createAccessJwtToken(securityUser).getToken()); 118 response.put("token", tokenFactory.createAccessJwtToken(securityUser).getToken());
@@ -224,6 +227,8 @@ public class AuthController extends BaseController { @@ -224,6 +227,8 @@ public class AuthController extends BaseController {
224 } 227 }
225 } 228 }
226 229
  230 + sendEntityNotificationMsg(user.getTenantId(), user.getId(), EdgeEventActionType.CREDENTIALS_UPDATED);
  231 +
227 JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); 232 JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
228 JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); 233 JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
229 234
@@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.DataConstants; @@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.DataConstants;
35 import org.thingsboard.server.common.data.Device; 35 import org.thingsboard.server.common.data.Device;
36 import org.thingsboard.server.common.data.DeviceInfo; 36 import org.thingsboard.server.common.data.DeviceInfo;
37 import org.thingsboard.server.common.data.DeviceProfile; 37 import org.thingsboard.server.common.data.DeviceProfile;
  38 +import org.thingsboard.server.common.data.EdgeUtils;
38 import org.thingsboard.server.common.data.EntityType; 39 import org.thingsboard.server.common.data.EntityType;
39 import org.thingsboard.server.common.data.EntityView; 40 import org.thingsboard.server.common.data.EntityView;
40 import org.thingsboard.server.common.data.EntityViewInfo; 41 import org.thingsboard.server.common.data.EntityViewInfo;
@@ -53,6 +54,10 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; @@ -53,6 +54,10 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo;
53 import org.thingsboard.server.common.data.asset.Asset; 54 import org.thingsboard.server.common.data.asset.Asset;
54 import org.thingsboard.server.common.data.asset.AssetInfo; 55 import org.thingsboard.server.common.data.asset.AssetInfo;
55 import org.thingsboard.server.common.data.audit.ActionType; 56 import org.thingsboard.server.common.data.audit.ActionType;
  57 +import org.thingsboard.server.common.data.edge.Edge;
  58 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  59 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  60 +import org.thingsboard.server.common.data.edge.EdgeInfo;
56 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 61 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
57 import org.thingsboard.server.common.data.exception.ThingsboardException; 62 import org.thingsboard.server.common.data.exception.ThingsboardException;
58 import org.thingsboard.server.common.data.id.AlarmId; 63 import org.thingsboard.server.common.data.id.AlarmId;
@@ -61,6 +66,7 @@ import org.thingsboard.server.common.data.id.CustomerId; @@ -61,6 +66,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
61 import org.thingsboard.server.common.data.id.DashboardId; 66 import org.thingsboard.server.common.data.id.DashboardId;
62 import org.thingsboard.server.common.data.id.DeviceId; 67 import org.thingsboard.server.common.data.id.DeviceId;
63 import org.thingsboard.server.common.data.id.DeviceProfileId; 68 import org.thingsboard.server.common.data.id.DeviceProfileId;
  69 +import org.thingsboard.server.common.data.id.EdgeId;
64 import org.thingsboard.server.common.data.id.EntityId; 70 import org.thingsboard.server.common.data.id.EntityId;
65 import org.thingsboard.server.common.data.id.EntityIdFactory; 71 import org.thingsboard.server.common.data.id.EntityIdFactory;
66 import org.thingsboard.server.common.data.id.EntityViewId; 72 import org.thingsboard.server.common.data.id.EntityViewId;
@@ -82,7 +88,9 @@ import org.thingsboard.server.common.data.page.SortOrder; @@ -82,7 +88,9 @@ import org.thingsboard.server.common.data.page.SortOrder;
82 import org.thingsboard.server.common.data.page.TimePageLink; 88 import org.thingsboard.server.common.data.page.TimePageLink;
83 import org.thingsboard.server.common.data.plugin.ComponentDescriptor; 89 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
84 import org.thingsboard.server.common.data.plugin.ComponentType; 90 import org.thingsboard.server.common.data.plugin.ComponentType;
  91 +import org.thingsboard.server.common.data.relation.EntityRelation;
85 import org.thingsboard.server.common.data.rule.RuleChain; 92 import org.thingsboard.server.common.data.rule.RuleChain;
  93 +import org.thingsboard.server.common.data.rule.RuleChainType;
86 import org.thingsboard.server.common.data.rule.RuleNode; 94 import org.thingsboard.server.common.data.rule.RuleNode;
87 import org.thingsboard.server.common.data.widget.WidgetTypeDetails; 95 import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
88 import org.thingsboard.server.common.data.widget.WidgetsBundle; 96 import org.thingsboard.server.common.data.widget.WidgetsBundle;
@@ -98,6 +106,7 @@ import org.thingsboard.server.dao.device.ClaimDevicesService; @@ -98,6 +106,7 @@ import org.thingsboard.server.dao.device.ClaimDevicesService;
98 import org.thingsboard.server.dao.device.DeviceCredentialsService; 106 import org.thingsboard.server.dao.device.DeviceCredentialsService;
99 import org.thingsboard.server.dao.device.DeviceProfileService; 107 import org.thingsboard.server.dao.device.DeviceProfileService;
100 import org.thingsboard.server.dao.device.DeviceService; 108 import org.thingsboard.server.dao.device.DeviceService;
  109 +import org.thingsboard.server.dao.edge.EdgeService;
101 import org.thingsboard.server.dao.entityview.EntityViewService; 110 import org.thingsboard.server.dao.entityview.EntityViewService;
102 import org.thingsboard.server.dao.exception.DataValidationException; 111 import org.thingsboard.server.dao.exception.DataValidationException;
103 import org.thingsboard.server.dao.exception.IncorrectParameterException; 112 import org.thingsboard.server.dao.exception.IncorrectParameterException;
@@ -106,7 +115,6 @@ import org.thingsboard.server.dao.model.ModelConstants; @@ -106,7 +115,6 @@ import org.thingsboard.server.dao.model.ModelConstants;
106 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; 115 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
107 import org.thingsboard.server.dao.oauth2.OAuth2Service; 116 import org.thingsboard.server.dao.oauth2.OAuth2Service;
108 import org.thingsboard.server.dao.relation.RelationService; 117 import org.thingsboard.server.dao.relation.RelationService;
109 -import org.thingsboard.server.dao.resource.TbResourceService;  
110 import org.thingsboard.server.dao.rule.RuleChainService; 118 import org.thingsboard.server.dao.rule.RuleChainService;
111 import org.thingsboard.server.dao.tenant.TbTenantProfileCache; 119 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
112 import org.thingsboard.server.dao.tenant.TenantProfileService; 120 import org.thingsboard.server.dao.tenant.TenantProfileService;
@@ -115,14 +123,19 @@ import org.thingsboard.server.dao.user.UserService; @@ -115,14 +123,19 @@ import org.thingsboard.server.dao.user.UserService;
115 import org.thingsboard.server.dao.widget.WidgetTypeService; 123 import org.thingsboard.server.dao.widget.WidgetTypeService;
116 import org.thingsboard.server.dao.widget.WidgetsBundleService; 124 import org.thingsboard.server.dao.widget.WidgetsBundleService;
117 import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; 125 import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
  126 +import org.thingsboard.server.gen.transport.TransportProtos;
118 import org.thingsboard.server.queue.discovery.PartitionService; 127 import org.thingsboard.server.queue.discovery.PartitionService;
119 import org.thingsboard.server.queue.provider.TbQueueProducerProvider; 128 import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
120 import org.thingsboard.server.queue.util.TbCoreComponent; 129 import org.thingsboard.server.queue.util.TbCoreComponent;
121 import org.thingsboard.server.service.component.ComponentDiscoveryService; 130 import org.thingsboard.server.service.component.ComponentDiscoveryService;
122 import org.thingsboard.server.service.firmware.FirmwareStateService; 131 import org.thingsboard.server.service.firmware.FirmwareStateService;
  132 +import org.thingsboard.server.service.edge.EdgeNotificationService;
  133 +import org.thingsboard.server.service.edge.rpc.EdgeGrpcService;
  134 +import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService;
123 import org.thingsboard.server.service.lwm2m.LwM2MModelsRepository; 135 import org.thingsboard.server.service.lwm2m.LwM2MModelsRepository;
124 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 136 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
125 import org.thingsboard.server.service.queue.TbClusterService; 137 import org.thingsboard.server.service.queue.TbClusterService;
  138 +import org.thingsboard.server.service.resource.TbResourceService;
126 import org.thingsboard.server.service.security.model.SecurityUser; 139 import org.thingsboard.server.service.security.model.SecurityUser;
127 import org.thingsboard.server.service.security.permission.AccessControlService; 140 import org.thingsboard.server.service.security.permission.AccessControlService;
128 import org.thingsboard.server.service.security.permission.Operation; 141 import org.thingsboard.server.service.security.permission.Operation;
@@ -256,10 +269,25 @@ public abstract class BaseController { @@ -256,10 +269,25 @@ public abstract class BaseController {
256 @Autowired 269 @Autowired
257 protected LwM2MModelsRepository lwM2MModelsRepository; 270 protected LwM2MModelsRepository lwM2MModelsRepository;
258 271
  272 + @Autowired(required = false)
  273 + protected EdgeService edgeService;
  274 +
  275 + @Autowired(required = false)
  276 + protected EdgeNotificationService edgeNotificationService;
  277 +
  278 + @Autowired(required = false)
  279 + protected SyncEdgeService syncEdgeService;
  280 +
  281 + @Autowired(required = false)
  282 + protected EdgeGrpcService edgeGrpcService;
  283 +
259 @Value("${server.log_controller_error_stack_trace}") 284 @Value("${server.log_controller_error_stack_trace}")
260 @Getter 285 @Getter
261 private boolean logControllerErrorStackTrace; 286 private boolean logControllerErrorStackTrace;
262 287
  288 + @Value("${edges.enabled}")
  289 + @Getter
  290 + protected boolean edgesEnabled;
263 291
264 @ExceptionHandler(ThingsboardException.class) 292 @ExceptionHandler(ThingsboardException.class)
265 public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) { 293 public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) {
@@ -473,6 +501,9 @@ public abstract class BaseController { @@ -473,6 +501,9 @@ public abstract class BaseController {
473 case ENTITY_VIEW: 501 case ENTITY_VIEW:
474 checkEntityViewId(new EntityViewId(entityId.getId()), operation); 502 checkEntityViewId(new EntityViewId(entityId.getId()), operation);
475 return; 503 return;
  504 + case EDGE:
  505 + checkEdgeId(new EdgeId(entityId.getId()), operation);
  506 + return;
476 case WIDGETS_BUNDLE: 507 case WIDGETS_BUNDLE:
477 checkWidgetsBundleId(new WidgetsBundleId(entityId.getId()), operation); 508 checkWidgetsBundleId(new WidgetsBundleId(entityId.getId()), operation);
478 return; 509 return;
@@ -637,6 +668,30 @@ public abstract class BaseController { @@ -637,6 +668,30 @@ public abstract class BaseController {
637 } 668 }
638 } 669 }
639 670
  671 + Edge checkEdgeId(EdgeId edgeId, Operation operation) throws ThingsboardException {
  672 + try {
  673 + validateId(edgeId, "Incorrect edgeId " + edgeId);
  674 + Edge edge = edgeService.findEdgeById(getTenantId(), edgeId);
  675 + checkNotNull(edge);
  676 + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, edgeId, edge);
  677 + return edge;
  678 + } catch (Exception e) {
  679 + throw handleException(e, false);
  680 + }
  681 + }
  682 +
  683 + EdgeInfo checkEdgeInfoId(EdgeId edgeId, Operation operation) throws ThingsboardException {
  684 + try {
  685 + validateId(edgeId, "Incorrect edgeId " + edgeId);
  686 + EdgeInfo edge = edgeService.findEdgeInfoById(getCurrentUser().getTenantId(), edgeId);
  687 + checkNotNull(edge);
  688 + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, edgeId, edge);
  689 + return edge;
  690 + } catch (Exception e) {
  691 + throw handleException(e, false);
  692 + }
  693 + }
  694 +
640 DashboardInfo checkDashboardInfoId(DashboardId dashboardId, Operation operation) throws ThingsboardException { 695 DashboardInfo checkDashboardInfoId(DashboardId dashboardId, Operation operation) throws ThingsboardException {
641 try { 696 try {
642 validateId(dashboardId, "Incorrect dashboardId " + dashboardId); 697 validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
@@ -658,19 +713,19 @@ public abstract class BaseController { @@ -658,19 +713,19 @@ public abstract class BaseController {
658 } 713 }
659 } 714 }
660 715
661 - List<ComponentDescriptor> checkComponentDescriptorsByType(ComponentType type) throws ThingsboardException { 716 + List<ComponentDescriptor> checkComponentDescriptorsByType(ComponentType type, RuleChainType ruleChainType) throws ThingsboardException {
662 try { 717 try {
663 log.debug("[{}] Lookup component descriptors", type); 718 log.debug("[{}] Lookup component descriptors", type);
664 - return componentDescriptorService.getComponents(type); 719 + return componentDescriptorService.getComponents(type, ruleChainType);
665 } catch (Exception e) { 720 } catch (Exception e) {
666 throw handleException(e, false); 721 throw handleException(e, false);
667 } 722 }
668 } 723 }
669 724
670 - List<ComponentDescriptor> checkComponentDescriptorsByTypes(Set<ComponentType> types) throws ThingsboardException { 725 + List<ComponentDescriptor> checkComponentDescriptorsByTypes(Set<ComponentType> types, RuleChainType ruleChainType) throws ThingsboardException {
671 try { 726 try {
672 log.debug("[{}] Lookup component descriptors", types); 727 log.debug("[{}] Lookup component descriptors", types);
673 - return componentDescriptorService.getComponents(types); 728 + return componentDescriptorService.getComponents(types, ruleChainType);
674 } catch (Exception e) { 729 } catch (Exception e) {
675 throw handleException(e, false); 730 throw handleException(e, false);
676 } 731 }
@@ -815,6 +870,12 @@ public abstract class BaseController { @@ -815,6 +870,12 @@ public abstract class BaseController {
815 case TIMESERIES_DELETED: 870 case TIMESERIES_DELETED:
816 msgType = DataConstants.TIMESERIES_DELETED; 871 msgType = DataConstants.TIMESERIES_DELETED;
817 break; 872 break;
  873 + case ASSIGNED_TO_EDGE:
  874 + msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE;
  875 + break;
  876 + case UNASSIGNED_FROM_EDGE:
  877 + msgType = DataConstants.ENTITY_UNASSIGNED_FROM_EDGE;
  878 + break;
818 } 879 }
819 if (!StringUtils.isEmpty(msgType)) { 880 if (!StringUtils.isEmpty(msgType)) {
820 try { 881 try {
@@ -844,6 +905,16 @@ public abstract class BaseController { @@ -844,6 +905,16 @@ public abstract class BaseController {
844 String strTenantName = extractParameter(String.class, 1, additionalInfo); 905 String strTenantName = extractParameter(String.class, 1, additionalInfo);
845 metaData.putValue("assignedToTenantId", strTenantId); 906 metaData.putValue("assignedToTenantId", strTenantId);
846 metaData.putValue("assignedToTenantName", strTenantName); 907 metaData.putValue("assignedToTenantName", strTenantName);
  908 + } else if (actionType == ActionType.ASSIGNED_TO_EDGE) {
  909 + String strEdgeId = extractParameter(String.class, 1, additionalInfo);
  910 + String strEdgeName = extractParameter(String.class, 2, additionalInfo);
  911 + metaData.putValue("assignedEdgeId", strEdgeId);
  912 + metaData.putValue("assignedEdgeName", strEdgeName);
  913 + } else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) {
  914 + String strEdgeId = extractParameter(String.class, 1, additionalInfo);
  915 + String strEdgeName = extractParameter(String.class, 2, additionalInfo);
  916 + metaData.putValue("unassignedEdgeId", strEdgeId);
  917 + metaData.putValue("unassignedEdgeName", strEdgeName);
847 } 918 }
848 ObjectNode entityNode; 919 ObjectNode entityNode;
849 if (entity != null) { 920 if (entity != null) {
@@ -937,6 +1008,93 @@ public abstract class BaseController { @@ -937,6 +1008,93 @@ public abstract class BaseController {
937 return null; 1008 return null;
938 } 1009 }
939 1010
  1011 + protected void sendRelationNotificationMsg(TenantId tenantId, EntityRelation relation, EdgeEventActionType action) {
  1012 + try {
  1013 + if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
  1014 + !relation.getTo().getEntityType().equals(EntityType.EDGE)) {
  1015 + sendNotificationMsgToEdgeService(tenantId, null, null, json.writeValueAsString(relation), EdgeEventType.RELATION, action);
  1016 + }
  1017 + } catch (Exception e) {
  1018 + log.warn("Failed to push relation to core: {}", relation, e);
  1019 + }
  1020 + }
  1021 +
  1022 + protected void sendDeleteNotificationMsg(TenantId tenantId, EntityId entityId, List<EdgeId> edgeIds) {
  1023 + if (edgeIds != null && !edgeIds.isEmpty()) {
  1024 + for (EdgeId edgeId : edgeIds) {
  1025 + sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, null, null, EdgeEventActionType.DELETED);
  1026 + }
  1027 + }
  1028 + }
  1029 +
  1030 + protected void sendEntityAssignToCustomerNotificationMsg(TenantId tenantId, EntityId entityId, CustomerId customerId, EdgeEventActionType action) {
  1031 + try {
  1032 + sendNotificationMsgToEdgeService(tenantId, null, entityId, json.writeValueAsString(customerId), null, action);
  1033 + } catch (Exception e) {
  1034 + log.warn("Failed to push assign/unassign to/from customer to core: {}", customerId, e);
  1035 + }
  1036 + }
  1037 +
  1038 + protected void sendEntityNotificationMsg(TenantId tenantId, EntityId entityId, EdgeEventActionType action) {
  1039 + sendNotificationMsgToEdgeService(tenantId, null, entityId, null, null, action);
  1040 + }
  1041 +
  1042 + protected void sendEntityAssignToEdgeNotificationMsg(TenantId tenantId, EdgeId edgeId, EntityId entityId, EdgeEventActionType action) {
  1043 + sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, null, null, action);
  1044 + }
  1045 +
  1046 + private void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action) {
  1047 + if (!edgesEnabled) {
  1048 + return;
  1049 + }
  1050 + if (type == null) {
  1051 + if (entityId != null) {
  1052 + type = EdgeUtils.getEdgeEventTypeByEntityType(entityId.getEntityType());
  1053 + } else {
  1054 + log.trace("[{}] entity id and type are null. Ignoring this notification", tenantId);
  1055 + return;
  1056 + }
  1057 + if (type == null) {
  1058 + log.trace("[{}] edge event type is null. Ignoring this notification [{}]", tenantId, entityId);
  1059 + return;
  1060 + }
  1061 + }
  1062 + TransportProtos.EdgeNotificationMsgProto.Builder builder = TransportProtos.EdgeNotificationMsgProto.newBuilder();
  1063 + builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
  1064 + builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
  1065 + builder.setType(type.name());
  1066 + builder.setAction(action.name());
  1067 + if (entityId != null) {
  1068 + builder.setEntityIdMSB(entityId.getId().getMostSignificantBits());
  1069 + builder.setEntityIdLSB(entityId.getId().getLeastSignificantBits());
  1070 + builder.setEntityType(entityId.getEntityType().name());
  1071 + }
  1072 + if (edgeId != null) {
  1073 + builder.setEdgeIdMSB(edgeId.getId().getMostSignificantBits());
  1074 + builder.setEdgeIdLSB(edgeId.getId().getLeastSignificantBits());
  1075 + }
  1076 + if (body != null) {
  1077 + builder.setBody(body);
  1078 + }
  1079 + TransportProtos.EdgeNotificationMsgProto msg = builder.build();
  1080 + log.trace("[{}] sending notification to edge service {}", tenantId.getId(), msg);
  1081 + tbClusterService.pushMsgToCore(tenantId, entityId != null ? entityId : tenantId,
  1082 + TransportProtos.ToCoreMsg.newBuilder().setEdgeNotificationMsg(msg).build(), null);
  1083 + }
  1084 +
  1085 + protected List<EdgeId> findRelatedEdgeIds(TenantId tenantId, EntityId entityId) {
  1086 + if (!edgesEnabled) {
  1087 + return null;
  1088 + }
  1089 + List<EdgeId> result = null;
  1090 + try {
  1091 + result = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId).get();
  1092 + } catch (Exception e) {
  1093 + log.error("[{}] can't find related edge ids for entity [{}]", tenantId, entityId, e);
  1094 + }
  1095 + return result;
  1096 + }
  1097 +
940 private void addTimeseries(ObjectNode entityNode, List<TsKvEntry> timeseries) throws Exception { 1098 private void addTimeseries(ObjectNode entityNode, List<TsKvEntry> timeseries) throws Exception {
941 if (timeseries != null && !timeseries.isEmpty()) { 1099 if (timeseries != null && !timeseries.isEmpty()) {
942 ArrayNode result = entityNode.putArray("timeseries"); 1100 ArrayNode result = entityNode.putArray("timeseries");
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import org.apache.commons.lang3.StringUtils;
18 import org.springframework.security.access.prepost.PreAuthorize; 19 import org.springframework.security.access.prepost.PreAuthorize;
19 import org.springframework.web.bind.annotation.PathVariable; 20 import org.springframework.web.bind.annotation.PathVariable;
20 import org.springframework.web.bind.annotation.RequestMapping; 21 import org.springframework.web.bind.annotation.RequestMapping;
@@ -25,6 +26,7 @@ import org.springframework.web.bind.annotation.RestController; @@ -25,6 +26,7 @@ import org.springframework.web.bind.annotation.RestController;
25 import org.thingsboard.server.common.data.exception.ThingsboardException; 26 import org.thingsboard.server.common.data.exception.ThingsboardException;
26 import org.thingsboard.server.common.data.plugin.ComponentDescriptor; 27 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
27 import org.thingsboard.server.common.data.plugin.ComponentType; 28 import org.thingsboard.server.common.data.plugin.ComponentType;
  29 +import org.thingsboard.server.common.data.rule.RuleChainType;
28 import org.thingsboard.server.queue.util.TbCoreComponent; 30 import org.thingsboard.server.queue.util.TbCoreComponent;
29 31
30 import java.util.HashSet; 32 import java.util.HashSet;
@@ -51,10 +53,11 @@ public class ComponentDescriptorController extends BaseController { @@ -51,10 +53,11 @@ public class ComponentDescriptorController extends BaseController {
51 @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')") 53 @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
52 @RequestMapping(value = "/components/{componentType}", method = RequestMethod.GET) 54 @RequestMapping(value = "/components/{componentType}", method = RequestMethod.GET)
53 @ResponseBody 55 @ResponseBody
54 - public List<ComponentDescriptor> getComponentDescriptorsByType(@PathVariable("componentType") String strComponentType) throws ThingsboardException { 56 + public List<ComponentDescriptor> getComponentDescriptorsByType(@PathVariable("componentType") String strComponentType,
  57 + @RequestParam(value = "ruleChainType", required = false) String strRuleChainType) throws ThingsboardException {
55 checkParameter("componentType", strComponentType); 58 checkParameter("componentType", strComponentType);
56 try { 59 try {
57 - return checkComponentDescriptorsByType(ComponentType.valueOf(strComponentType)); 60 + return checkComponentDescriptorsByType(ComponentType.valueOf(strComponentType), getRuleChainType(strRuleChainType));
58 } catch (Exception e) { 61 } catch (Exception e) {
59 throw handleException(e); 62 throw handleException(e);
60 } 63 }
@@ -63,17 +66,28 @@ public class ComponentDescriptorController extends BaseController { @@ -63,17 +66,28 @@ public class ComponentDescriptorController extends BaseController {
63 @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')") 66 @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
64 @RequestMapping(value = "/components", params = {"componentTypes"}, method = RequestMethod.GET) 67 @RequestMapping(value = "/components", params = {"componentTypes"}, method = RequestMethod.GET)
65 @ResponseBody 68 @ResponseBody
66 - public List<ComponentDescriptor> getComponentDescriptorsByTypes(@RequestParam("componentTypes") String[] strComponentTypes) throws ThingsboardException { 69 + public List<ComponentDescriptor> getComponentDescriptorsByTypes(@RequestParam("componentTypes") String[] strComponentTypes,
  70 + @RequestParam(value = "ruleChainType", required = false) String strRuleChainType) throws ThingsboardException {
67 checkArrayParameter("componentTypes", strComponentTypes); 71 checkArrayParameter("componentTypes", strComponentTypes);
68 try { 72 try {
69 Set<ComponentType> componentTypes = new HashSet<>(); 73 Set<ComponentType> componentTypes = new HashSet<>();
70 for (String strComponentType : strComponentTypes) { 74 for (String strComponentType : strComponentTypes) {
71 componentTypes.add(ComponentType.valueOf(strComponentType)); 75 componentTypes.add(ComponentType.valueOf(strComponentType));
72 } 76 }
73 - return checkComponentDescriptorsByTypes(componentTypes); 77 + return checkComponentDescriptorsByTypes(componentTypes, getRuleChainType(strRuleChainType));
74 } catch (Exception e) { 78 } catch (Exception e) {
75 throw handleException(e); 79 throw handleException(e);
76 } 80 }
77 } 81 }
78 82
  83 + private RuleChainType getRuleChainType(String strRuleChainType) {
  84 + RuleChainType ruleChainType;
  85 + if (StringUtils.isEmpty(strRuleChainType)) {
  86 + ruleChainType = RuleChainType.CORE;
  87 + } else {
  88 + ruleChainType = RuleChainType.valueOf(strRuleChainType);
  89 + }
  90 + return ruleChainType;
  91 + }
  92 +
79 } 93 }
@@ -31,8 +31,10 @@ import org.springframework.web.bind.annotation.RestController; @@ -31,8 +31,10 @@ import org.springframework.web.bind.annotation.RestController;
31 import org.thingsboard.server.common.data.Customer; 31 import org.thingsboard.server.common.data.Customer;
32 import org.thingsboard.server.common.data.EntityType; 32 import org.thingsboard.server.common.data.EntityType;
33 import org.thingsboard.server.common.data.audit.ActionType; 33 import org.thingsboard.server.common.data.audit.ActionType;
  34 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
34 import org.thingsboard.server.common.data.exception.ThingsboardException; 35 import org.thingsboard.server.common.data.exception.ThingsboardException;
35 import org.thingsboard.server.common.data.id.CustomerId; 36 import org.thingsboard.server.common.data.id.CustomerId;
  37 +import org.thingsboard.server.common.data.id.EdgeId;
36 import org.thingsboard.server.common.data.id.TenantId; 38 import org.thingsboard.server.common.data.id.TenantId;
37 import org.thingsboard.server.common.data.page.PageData; 39 import org.thingsboard.server.common.data.page.PageData;
38 import org.thingsboard.server.common.data.page.PageLink; 40 import org.thingsboard.server.common.data.page.PageLink;
@@ -40,6 +42,8 @@ import org.thingsboard.server.queue.util.TbCoreComponent; @@ -40,6 +42,8 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
40 import org.thingsboard.server.service.security.permission.Operation; 42 import org.thingsboard.server.service.security.permission.Operation;
41 import org.thingsboard.server.service.security.permission.Resource; 43 import org.thingsboard.server.service.security.permission.Resource;
42 44
  45 +import java.util.List;
  46 +
43 @RestController 47 @RestController
44 @TbCoreComponent 48 @TbCoreComponent
45 @RequestMapping("/api") 49 @RequestMapping("/api")
@@ -112,6 +116,10 @@ public class CustomerController extends BaseController { @@ -112,6 +116,10 @@ public class CustomerController extends BaseController {
112 savedCustomer.getId(), 116 savedCustomer.getId(),
113 customer.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 117 customer.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
114 118
  119 + if (customer.getId() != null) {
  120 + sendEntityNotificationMsg(savedCustomer.getTenantId(), savedCustomer.getId(), EdgeEventActionType.UPDATED);
  121 + }
  122 +
115 return savedCustomer; 123 return savedCustomer;
116 } catch (Exception e) { 124 } catch (Exception e) {
117 125
@@ -130,12 +138,16 @@ public class CustomerController extends BaseController { @@ -130,12 +138,16 @@ public class CustomerController extends BaseController {
130 try { 138 try {
131 CustomerId customerId = new CustomerId(toUUID(strCustomerId)); 139 CustomerId customerId = new CustomerId(toUUID(strCustomerId));
132 Customer customer = checkCustomerId(customerId, Operation.DELETE); 140 Customer customer = checkCustomerId(customerId, Operation.DELETE);
  141 +
  142 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), customerId);
  143 +
133 customerService.deleteCustomer(getTenantId(), customerId); 144 customerService.deleteCustomer(getTenantId(), customerId);
134 145
135 logEntityAction(customerId, customer, 146 logEntityAction(customerId, customer,
136 customer.getId(), 147 customer.getId(),
137 ActionType.DELETED, null, strCustomerId); 148 ActionType.DELETED, null, strCustomerId);
138 149
  150 + sendDeleteNotificationMsg(getTenantId(), customerId, relatedEdgeIds);
139 } catch (Exception e) { 151 } catch (Exception e) {
140 152
141 logEntityAction(emptyId(EntityType.CUSTOMER), 153 logEntityAction(emptyId(EntityType.CUSTOMER),
@@ -38,20 +38,26 @@ import org.thingsboard.server.common.data.ShortCustomerInfo; @@ -38,20 +38,26 @@ import org.thingsboard.server.common.data.ShortCustomerInfo;
38 import org.thingsboard.server.common.data.Tenant; 38 import org.thingsboard.server.common.data.Tenant;
39 import org.thingsboard.server.common.data.User; 39 import org.thingsboard.server.common.data.User;
40 import org.thingsboard.server.common.data.audit.ActionType; 40 import org.thingsboard.server.common.data.audit.ActionType;
  41 +import org.thingsboard.server.common.data.edge.Edge;
  42 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
41 import org.thingsboard.server.common.data.exception.ThingsboardException; 43 import org.thingsboard.server.common.data.exception.ThingsboardException;
42 import org.thingsboard.server.common.data.id.CustomerId; 44 import org.thingsboard.server.common.data.id.CustomerId;
43 import org.thingsboard.server.common.data.id.DashboardId; 45 import org.thingsboard.server.common.data.id.DashboardId;
  46 +import org.thingsboard.server.common.data.id.EdgeId;
44 import org.thingsboard.server.common.data.id.TenantId; 47 import org.thingsboard.server.common.data.id.TenantId;
45 import org.thingsboard.server.common.data.page.PageData; 48 import org.thingsboard.server.common.data.page.PageData;
46 import org.thingsboard.server.common.data.page.PageLink; 49 import org.thingsboard.server.common.data.page.PageLink;
47 import org.thingsboard.common.util.JacksonUtil; 50 import org.thingsboard.common.util.JacksonUtil;
  51 +import org.thingsboard.server.common.data.page.TimePageLink;
48 import org.thingsboard.server.queue.util.TbCoreComponent; 52 import org.thingsboard.server.queue.util.TbCoreComponent;
49 import org.thingsboard.server.service.security.model.SecurityUser; 53 import org.thingsboard.server.service.security.model.SecurityUser;
50 import org.thingsboard.server.service.security.permission.Operation; 54 import org.thingsboard.server.service.security.permission.Operation;
51 import org.thingsboard.server.service.security.permission.Resource; 55 import org.thingsboard.server.service.security.permission.Resource;
52 56
53 import java.util.HashSet; 57 import java.util.HashSet;
  58 +import java.util.List;
54 import java.util.Set; 59 import java.util.Set;
  60 +import java.util.stream.Collectors;
55 61
56 @RestController 62 @RestController
57 @TbCoreComponent 63 @TbCoreComponent
@@ -122,6 +128,10 @@ public class DashboardController extends BaseController { @@ -122,6 +128,10 @@ public class DashboardController extends BaseController {
122 null, 128 null,
123 dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 129 dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
124 130
  131 + if (dashboard.getId() != null) {
  132 + sendEntityNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), EdgeEventActionType.UPDATED);
  133 + }
  134 +
125 return savedDashboard; 135 return savedDashboard;
126 } catch (Exception e) { 136 } catch (Exception e) {
127 logEntityAction(emptyId(EntityType.DASHBOARD), dashboard, 137 logEntityAction(emptyId(EntityType.DASHBOARD), dashboard,
@@ -139,12 +149,16 @@ public class DashboardController extends BaseController { @@ -139,12 +149,16 @@ public class DashboardController extends BaseController {
139 try { 149 try {
140 DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); 150 DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
141 Dashboard dashboard = checkDashboardId(dashboardId, Operation.DELETE); 151 Dashboard dashboard = checkDashboardId(dashboardId, Operation.DELETE);
  152 +
  153 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), dashboardId);
  154 +
142 dashboardService.deleteDashboard(getCurrentUser().getTenantId(), dashboardId); 155 dashboardService.deleteDashboard(getCurrentUser().getTenantId(), dashboardId);
143 156
144 logEntityAction(dashboardId, dashboard, 157 logEntityAction(dashboardId, dashboard,
145 null, 158 null,
146 ActionType.DELETED, null, strDashboardId); 159 ActionType.DELETED, null, strDashboardId);
147 160
  161 + sendDeleteNotificationMsg(getTenantId(), dashboardId, relatedEdgeIds);
148 } catch (Exception e) { 162 } catch (Exception e) {
149 163
150 logEntityAction(emptyId(EntityType.DASHBOARD), 164 logEntityAction(emptyId(EntityType.DASHBOARD),
@@ -176,6 +190,7 @@ public class DashboardController extends BaseController { @@ -176,6 +190,7 @@ public class DashboardController extends BaseController {
176 customerId, 190 customerId,
177 ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, strCustomerId, customer.getName()); 191 ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, strCustomerId, customer.getName());
178 192
  193 + sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
179 194
180 return savedDashboard; 195 return savedDashboard;
181 } catch (Exception e) { 196 } catch (Exception e) {
@@ -207,6 +222,8 @@ public class DashboardController extends BaseController { @@ -207,6 +222,8 @@ public class DashboardController extends BaseController {
207 customerId, 222 customerId,
208 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customer.getId().toString(), customer.getName()); 223 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customer.getId().toString(), customer.getName());
209 224
  225 + sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
  226 +
210 return savedDashboard; 227 return savedDashboard;
211 } catch (Exception e) { 228 } catch (Exception e) {
212 229
@@ -262,6 +279,7 @@ public class DashboardController extends BaseController { @@ -262,6 +279,7 @@ public class DashboardController extends BaseController {
262 logEntityAction(dashboardId, savedDashboard, 279 logEntityAction(dashboardId, savedDashboard,
263 customerId, 280 customerId,
264 ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); 281 ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
  282 + sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
265 } 283 }
266 for (CustomerId customerId : removedCustomerIds) { 284 for (CustomerId customerId : removedCustomerIds) {
267 ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); 285 ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
@@ -269,7 +287,7 @@ public class DashboardController extends BaseController { @@ -269,7 +287,7 @@ public class DashboardController extends BaseController {
269 logEntityAction(dashboardId, dashboard, 287 logEntityAction(dashboardId, dashboard,
270 customerId, 288 customerId,
271 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); 289 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
272 - 290 + sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
273 } 291 }
274 return savedDashboard; 292 return savedDashboard;
275 } 293 }
@@ -313,6 +331,7 @@ public class DashboardController extends BaseController { @@ -313,6 +331,7 @@ public class DashboardController extends BaseController {
313 logEntityAction(dashboardId, savedDashboard, 331 logEntityAction(dashboardId, savedDashboard,
314 customerId, 332 customerId,
315 ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); 333 ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
  334 + sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
316 } 335 }
317 return savedDashboard; 336 return savedDashboard;
318 } 337 }
@@ -356,7 +375,7 @@ public class DashboardController extends BaseController { @@ -356,7 +375,7 @@ public class DashboardController extends BaseController {
356 logEntityAction(dashboardId, dashboard, 375 logEntityAction(dashboardId, dashboard,
357 customerId, 376 customerId,
358 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); 377 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
359 - 378 + sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
360 } 379 }
361 return savedDashboard; 380 return savedDashboard;
362 } 381 }
@@ -578,4 +597,106 @@ public class DashboardController extends BaseController { @@ -578,4 +597,106 @@ public class DashboardController extends BaseController {
578 } catch (Exception e) {} 597 } catch (Exception e) {}
579 return null; 598 return null;
580 } 599 }
  600 +
  601 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  602 + @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.POST)
  603 + @ResponseBody
  604 + public Dashboard assignDashboardToEdge(@PathVariable("edgeId") String strEdgeId,
  605 + @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
  606 + checkParameter("edgeId", strEdgeId);
  607 + checkParameter(DASHBOARD_ID, strDashboardId);
  608 + try {
  609 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  610 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  611 +
  612 + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
  613 + checkDashboardId(dashboardId, Operation.ASSIGN_TO_EDGE);
  614 +
  615 + Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
  616 +
  617 + logEntityAction(dashboardId, savedDashboard,
  618 + null,
  619 + ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, strEdgeId, edge.getName());
  620 +
  621 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedDashboard.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE);
  622 +
  623 + return savedDashboard;
  624 + } catch (Exception e) {
  625 +
  626 + logEntityAction(emptyId(EntityType.DASHBOARD), null,
  627 + null,
  628 + ActionType.ASSIGNED_TO_EDGE, e, strDashboardId, strEdgeId);
  629 +
  630 + throw handleException(e);
  631 + }
  632 + }
  633 +
  634 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  635 + @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.DELETE)
  636 + @ResponseBody
  637 + public Dashboard unassignDashboardFromEdge(@PathVariable("edgeId") String strEdgeId,
  638 + @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
  639 + checkParameter("edgeId", strEdgeId);
  640 + checkParameter(DASHBOARD_ID, strDashboardId);
  641 + try {
  642 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  643 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  644 + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
  645 + Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_EDGE);
  646 +
  647 + Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
  648 +
  649 + logEntityAction(dashboardId, dashboard,
  650 + null,
  651 + ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, strEdgeId, edge.getName());
  652 +
  653 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedDashboard.getId(), EdgeEventActionType.UNASSIGNED_FROM_EDGE);
  654 +
  655 + return savedDashboard;
  656 + } catch (Exception e) {
  657 +
  658 + logEntityAction(emptyId(EntityType.DASHBOARD), null,
  659 + null,
  660 + ActionType.UNASSIGNED_FROM_EDGE, e, strDashboardId, strEdgeId);
  661 +
  662 + throw handleException(e);
  663 + }
  664 + }
  665 +
  666 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  667 + @RequestMapping(value = "/edge/{edgeId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET)
  668 + @ResponseBody
  669 + public PageData<DashboardInfo> getEdgeDashboards(
  670 + @PathVariable("edgeId") String strEdgeId,
  671 + @RequestParam int pageSize,
  672 + @RequestParam int page,
  673 + @RequestParam(required = false) String textSearch,
  674 + @RequestParam(required = false) String sortProperty,
  675 + @RequestParam(required = false) String sortOrder,
  676 + @RequestParam(required = false) Long startTime,
  677 + @RequestParam(required = false) Long endTime) throws ThingsboardException {
  678 + checkParameter("edgeId", strEdgeId);
  679 + try {
  680 + TenantId tenantId = getCurrentUser().getTenantId();
  681 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  682 + checkEdgeId(edgeId, Operation.READ);
  683 + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
  684 + PageData<DashboardInfo> nonFilteredResult = dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edgeId, pageLink);
  685 + List<DashboardInfo> filteredDashboards = nonFilteredResult.getData().stream().filter(dashboardInfo -> {
  686 + try {
  687 + accessControlService.checkPermission(getCurrentUser(), Resource.DASHBOARD, Operation.READ, dashboardInfo.getId(), dashboardInfo);
  688 + return true;
  689 + } catch (ThingsboardException e) {
  690 + return false;
  691 + }
  692 + }).collect(Collectors.toList());
  693 + PageData<DashboardInfo> filteredResult = new PageData<>(filteredDashboards,
  694 + nonFilteredResult.getTotalPages(),
  695 + nonFilteredResult.getTotalElements(),
  696 + nonFilteredResult.hasNext());
  697 + return checkNotNull(filteredResult);
  698 + } catch (Exception e) {
  699 + throw handleException(e);
  700 + }
  701 + }
581 } 702 }
@@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.ResponseStatus; @@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
32 import org.springframework.web.bind.annotation.RestController; 32 import org.springframework.web.bind.annotation.RestController;
33 import org.springframework.web.context.request.async.DeferredResult; 33 import org.springframework.web.context.request.async.DeferredResult;
34 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg; 34 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
  35 +import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
35 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; 36 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
36 import org.thingsboard.server.common.data.ClaimRequest; 37 import org.thingsboard.server.common.data.ClaimRequest;
37 import org.thingsboard.server.common.data.Customer; 38 import org.thingsboard.server.common.data.Customer;
@@ -43,14 +44,18 @@ import org.thingsboard.server.common.data.EntityType; @@ -43,14 +44,18 @@ import org.thingsboard.server.common.data.EntityType;
43 import org.thingsboard.server.common.data.Tenant; 44 import org.thingsboard.server.common.data.Tenant;
44 import org.thingsboard.server.common.data.audit.ActionType; 45 import org.thingsboard.server.common.data.audit.ActionType;
45 import org.thingsboard.server.common.data.device.DeviceSearchQuery; 46 import org.thingsboard.server.common.data.device.DeviceSearchQuery;
  47 +import org.thingsboard.server.common.data.edge.Edge;
  48 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
46 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 49 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
47 import org.thingsboard.server.common.data.exception.ThingsboardException; 50 import org.thingsboard.server.common.data.exception.ThingsboardException;
48 import org.thingsboard.server.common.data.id.CustomerId; 51 import org.thingsboard.server.common.data.id.CustomerId;
49 import org.thingsboard.server.common.data.id.DeviceId; 52 import org.thingsboard.server.common.data.id.DeviceId;
50 import org.thingsboard.server.common.data.id.DeviceProfileId; 53 import org.thingsboard.server.common.data.id.DeviceProfileId;
  54 +import org.thingsboard.server.common.data.id.EdgeId;
51 import org.thingsboard.server.common.data.id.TenantId; 55 import org.thingsboard.server.common.data.id.TenantId;
52 import org.thingsboard.server.common.data.page.PageData; 56 import org.thingsboard.server.common.data.page.PageData;
53 import org.thingsboard.server.common.data.page.PageLink; 57 import org.thingsboard.server.common.data.page.PageLink;
  58 +import org.thingsboard.server.common.data.page.TimePageLink;
54 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 59 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
55 import org.thingsboard.server.common.data.security.DeviceCredentials; 60 import org.thingsboard.server.common.data.security.DeviceCredentials;
56 import org.thingsboard.server.common.msg.TbMsg; 61 import org.thingsboard.server.common.msg.TbMsg;
@@ -73,6 +78,8 @@ import java.util.List; @@ -73,6 +78,8 @@ import java.util.List;
73 import java.util.Objects; 78 import java.util.Objects;
74 import java.util.stream.Collectors; 79 import java.util.stream.Collectors;
75 80
  81 +import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
  82 +
76 @RestController 83 @RestController
77 @TbCoreComponent 84 @TbCoreComponent
78 @RequestMapping("/api") 85 @RequestMapping("/api")
@@ -138,6 +145,10 @@ public class DeviceController extends BaseController { @@ -138,6 +145,10 @@ public class DeviceController extends BaseController {
138 savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null); 145 savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null);
139 tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(), created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); 146 tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(), created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
140 147
  148 + if (device.getId() != null) {
  149 + sendEntityNotificationMsg(savedDevice.getTenantId(), savedDevice.getId(), EdgeEventActionType.UPDATED);
  150 + }
  151 +
141 logEntityAction(savedDevice.getId(), savedDevice, 152 logEntityAction(savedDevice.getId(), savedDevice,
142 savedDevice.getCustomerId(), 153 savedDevice.getCustomerId(),
143 device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 154 device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
@@ -170,6 +181,9 @@ public class DeviceController extends BaseController { @@ -170,6 +181,9 @@ public class DeviceController extends BaseController {
170 try { 181 try {
171 DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); 182 DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
172 Device device = checkDeviceId(deviceId, Operation.DELETE); 183 Device device = checkDeviceId(deviceId, Operation.DELETE);
  184 +
  185 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), deviceId);
  186 +
173 deviceService.deleteDevice(getCurrentUser().getTenantId(), deviceId); 187 deviceService.deleteDevice(getCurrentUser().getTenantId(), deviceId);
174 188
175 tbClusterService.onDeviceDeleted(device, null); 189 tbClusterService.onDeviceDeleted(device, null);
@@ -179,6 +193,8 @@ public class DeviceController extends BaseController { @@ -179,6 +193,8 @@ public class DeviceController extends BaseController {
179 device.getCustomerId(), 193 device.getCustomerId(),
180 ActionType.DELETED, null, strDeviceId); 194 ActionType.DELETED, null, strDeviceId);
181 195
  196 + sendDeleteNotificationMsg(getTenantId(), deviceId, relatedEdgeIds);
  197 +
182 deviceStateService.onDeviceDeleted(device); 198 deviceStateService.onDeviceDeleted(device);
183 } catch (Exception e) { 199 } catch (Exception e) {
184 logEntityAction(emptyId(EntityType.DEVICE), 200 logEntityAction(emptyId(EntityType.DEVICE),
@@ -209,6 +225,9 @@ public class DeviceController extends BaseController { @@ -209,6 +225,9 @@ public class DeviceController extends BaseController {
209 savedDevice.getCustomerId(), 225 savedDevice.getCustomerId(),
210 ActionType.ASSIGNED_TO_CUSTOMER, null, strDeviceId, strCustomerId, customer.getName()); 226 ActionType.ASSIGNED_TO_CUSTOMER, null, strDeviceId, strCustomerId, customer.getName());
211 227
  228 + sendEntityAssignToCustomerNotificationMsg(savedDevice.getTenantId(), savedDevice.getId(),
  229 + customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
  230 +
212 return savedDevice; 231 return savedDevice;
213 } catch (Exception e) { 232 } catch (Exception e) {
214 logEntityAction(emptyId(EntityType.DEVICE), null, 233 logEntityAction(emptyId(EntityType.DEVICE), null,
@@ -237,6 +256,9 @@ public class DeviceController extends BaseController { @@ -237,6 +256,9 @@ public class DeviceController extends BaseController {
237 device.getCustomerId(), 256 device.getCustomerId(),
238 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDeviceId, customer.getId().toString(), customer.getName()); 257 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDeviceId, customer.getId().toString(), customer.getName());
239 258
  259 + sendEntityAssignToCustomerNotificationMsg(savedDevice.getTenantId(), savedDevice.getId(),
  260 + customer.getId(), EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
  261 +
240 return savedDevice; 262 return savedDevice;
241 } catch (Exception e) { 263 } catch (Exception e) {
242 logEntityAction(emptyId(EntityType.DEVICE), null, 264 logEntityAction(emptyId(EntityType.DEVICE), null,
@@ -300,6 +322,9 @@ public class DeviceController extends BaseController { @@ -300,6 +322,9 @@ public class DeviceController extends BaseController {
300 Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS); 322 Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS);
301 DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials)); 323 DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials));
302 tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId(), result), null); 324 tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId(), result), null);
  325 +
  326 + sendEntityNotificationMsg(getTenantId(), device.getId(), EdgeEventActionType.CREDENTIALS_UPDATED);
  327 +
303 logEntityAction(device.getId(), device, 328 logEntityAction(device.getId(), device,
304 device.getCustomerId(), 329 device.getCustomerId(),
305 ActionType.CREDENTIALS_UPDATED, null, deviceCredentials); 330 ActionType.CREDENTIALS_UPDATED, null, deviceCredentials);
@@ -651,4 +676,115 @@ public class DeviceController extends BaseController { @@ -651,4 +676,115 @@ public class DeviceController extends BaseController {
651 metaData.putValue("assignedFromTenantName", tenant.getName()); 676 metaData.putValue("assignedFromTenantName", tenant.getName());
652 return metaData; 677 return metaData;
653 } 678 }
  679 +
  680 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  681 + @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.POST)
  682 + @ResponseBody
  683 + public Device assignDeviceToEdge(@PathVariable(EDGE_ID) String strEdgeId,
  684 + @PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
  685 + checkParameter(EDGE_ID, strEdgeId);
  686 + checkParameter(DEVICE_ID, strDeviceId);
  687 + try {
  688 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  689 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  690 +
  691 + DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
  692 + checkDeviceId(deviceId, Operation.ASSIGN_TO_EDGE);
  693 +
  694 + Device savedDevice = checkNotNull(deviceService.assignDeviceToEdge(getCurrentUser().getTenantId(), deviceId, edgeId));
  695 +
  696 + tbClusterService.pushMsgToCore(new DeviceEdgeUpdateMsg(savedDevice.getTenantId(),
  697 + savedDevice.getId(), edgeId), null);
  698 +
  699 + logEntityAction(deviceId, savedDevice,
  700 + savedDevice.getCustomerId(),
  701 + ActionType.ASSIGNED_TO_EDGE, null, strDeviceId, strEdgeId, edge.getName());
  702 +
  703 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedDevice.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE);
  704 +
  705 + return savedDevice;
  706 + } catch (Exception e) {
  707 + logEntityAction(emptyId(EntityType.DEVICE), null,
  708 + null,
  709 + ActionType.ASSIGNED_TO_EDGE, e, strDeviceId, strEdgeId);
  710 + throw handleException(e);
  711 + }
  712 + }
  713 +
  714 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  715 + @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.DELETE)
  716 + @ResponseBody
  717 + public Device unassignDeviceFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
  718 + @PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
  719 + checkParameter(EDGE_ID, strEdgeId);
  720 + checkParameter(DEVICE_ID, strDeviceId);
  721 + try {
  722 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  723 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  724 +
  725 + DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
  726 + Device device = checkDeviceId(deviceId, Operation.UNASSIGN_FROM_EDGE);
  727 +
  728 + Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(getCurrentUser().getTenantId(), deviceId, edgeId));
  729 +
  730 + tbClusterService.pushMsgToCore(new DeviceEdgeUpdateMsg(savedDevice.getTenantId(),
  731 + savedDevice.getId(), null), null);
  732 +
  733 + logEntityAction(deviceId, device,
  734 + device.getCustomerId(),
  735 + ActionType.UNASSIGNED_FROM_EDGE, null, strDeviceId, strEdgeId, edge.getName());
  736 +
  737 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedDevice.getId(), EdgeEventActionType.UNASSIGNED_FROM_EDGE);
  738 +
  739 + return savedDevice;
  740 + } catch (Exception e) {
  741 + logEntityAction(emptyId(EntityType.DEVICE), null,
  742 + null,
  743 + ActionType.UNASSIGNED_FROM_EDGE, e, strDeviceId, strEdgeId);
  744 + throw handleException(e);
  745 + }
  746 + }
  747 +
  748 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  749 + @RequestMapping(value = "/edge/{edgeId}/devices", params = {"pageSize", "page"}, method = RequestMethod.GET)
  750 + @ResponseBody
  751 + public PageData<Device> getEdgeDevices(
  752 + @PathVariable(EDGE_ID) String strEdgeId,
  753 + @RequestParam int pageSize,
  754 + @RequestParam int page,
  755 + @RequestParam(required = false) String type,
  756 + @RequestParam(required = false) String textSearch,
  757 + @RequestParam(required = false) String sortProperty,
  758 + @RequestParam(required = false) String sortOrder,
  759 + @RequestParam(required = false) Long startTime,
  760 + @RequestParam(required = false) Long endTime) throws ThingsboardException {
  761 + checkParameter(EDGE_ID, strEdgeId);
  762 + try {
  763 + TenantId tenantId = getCurrentUser().getTenantId();
  764 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  765 + checkEdgeId(edgeId, Operation.READ);
  766 + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
  767 + PageData<Device> nonFilteredResult;
  768 + if (type != null && type.trim().length() > 0) {
  769 + nonFilteredResult = deviceService.findDevicesByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink);
  770 + } else {
  771 + nonFilteredResult = deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink);
  772 + }
  773 + List<Device> filteredDevices = nonFilteredResult.getData().stream().filter(device -> {
  774 + try {
  775 + accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, Operation.READ, device.getId(), device);
  776 + return true;
  777 + } catch (ThingsboardException e) {
  778 + return false;
  779 + }
  780 + }).collect(Collectors.toList());
  781 + PageData<Device> filteredResult = new PageData<>(filteredDevices,
  782 + nonFilteredResult.getTotalPages(),
  783 + nonFilteredResult.getTotalElements(),
  784 + nonFilteredResult.hasNext());
  785 + return checkNotNull(filteredResult);
  786 + } catch (Exception e) {
  787 + throw handleException(e);
  788 + }
  789 + }
654 } 790 }
@@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.DeviceProfile; @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
32 import org.thingsboard.server.common.data.DeviceProfileInfo; 32 import org.thingsboard.server.common.data.DeviceProfileInfo;
33 import org.thingsboard.server.common.data.EntityType; 33 import org.thingsboard.server.common.data.EntityType;
34 import org.thingsboard.server.common.data.audit.ActionType; 34 import org.thingsboard.server.common.data.audit.ActionType;
  35 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
35 import org.thingsboard.server.common.data.exception.ThingsboardException; 36 import org.thingsboard.server.common.data.exception.ThingsboardException;
36 import org.thingsboard.server.common.data.id.DeviceProfileId; 37 import org.thingsboard.server.common.data.id.DeviceProfileId;
37 import org.thingsboard.server.common.data.page.PageData; 38 import org.thingsboard.server.common.data.page.PageData;
@@ -166,7 +167,8 @@ public class DeviceProfileController extends BaseController { @@ -166,7 +167,8 @@ public class DeviceProfileController extends BaseController {
166 if (isFirmwareChanged) { 167 if (isFirmwareChanged) {
167 firmwareStateService.update(savedDeviceProfile); 168 firmwareStateService.update(savedDeviceProfile);
168 } 169 }
169 - 170 + sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(),
  171 + deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
170 return savedDeviceProfile; 172 return savedDeviceProfile;
171 } catch (Exception e) { 173 } catch (Exception e) {
172 logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile, 174 logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile,
@@ -192,6 +194,7 @@ public class DeviceProfileController extends BaseController { @@ -192,6 +194,7 @@ public class DeviceProfileController extends BaseController {
192 null, 194 null,
193 ActionType.DELETED, null, strDeviceProfileId); 195 ActionType.DELETED, null, strDeviceProfileId);
194 196
  197 + sendEntityNotificationMsg(getTenantId(), deviceProfile.getId(), EdgeEventActionType.DELETED);
195 } catch (Exception e) { 198 } catch (Exception e) {
196 logEntityAction(emptyId(EntityType.DEVICE_PROFILE), 199 logEntityAction(emptyId(EntityType.DEVICE_PROFILE),
197 null, 200 null,
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.controller;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.springframework.http.HttpStatus;
  20 +import org.springframework.security.access.prepost.PreAuthorize;
  21 +import org.springframework.web.bind.annotation.PathVariable;
  22 +import org.springframework.web.bind.annotation.RequestBody;
  23 +import org.springframework.web.bind.annotation.RequestMapping;
  24 +import org.springframework.web.bind.annotation.RequestMethod;
  25 +import org.springframework.web.bind.annotation.RequestParam;
  26 +import org.springframework.web.bind.annotation.ResponseBody;
  27 +import org.springframework.web.bind.annotation.ResponseStatus;
  28 +import org.springframework.web.bind.annotation.RestController;
  29 +import org.thingsboard.server.common.data.Customer;
  30 +import org.thingsboard.server.common.data.EntitySubtype;
  31 +import org.thingsboard.server.common.data.EntityType;
  32 +import org.thingsboard.server.common.data.audit.ActionType;
  33 +import org.thingsboard.server.common.data.edge.Edge;
  34 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  35 +import org.thingsboard.server.common.data.edge.EdgeInfo;
  36 +import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
  37 +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
  38 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  39 +import org.thingsboard.server.common.data.id.CustomerId;
  40 +import org.thingsboard.server.common.data.id.EdgeId;
  41 +import org.thingsboard.server.common.data.id.RuleChainId;
  42 +import org.thingsboard.server.common.data.id.TenantId;
  43 +import org.thingsboard.server.common.data.page.PageData;
  44 +import org.thingsboard.server.common.data.page.PageLink;
  45 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  46 +import org.thingsboard.server.common.data.rule.RuleChain;
  47 +import org.thingsboard.server.common.data.security.Authority;
  48 +import org.thingsboard.server.dao.exception.DataValidationException;
  49 +import org.thingsboard.server.dao.exception.IncorrectParameterException;
  50 +import org.thingsboard.server.dao.model.ModelConstants;
  51 +import org.thingsboard.server.queue.util.TbCoreComponent;
  52 +import org.thingsboard.server.service.edge.rpc.EdgeGrpcSession;
  53 +import org.thingsboard.server.service.security.model.SecurityUser;
  54 +import org.thingsboard.server.service.security.permission.Operation;
  55 +import org.thingsboard.server.service.security.permission.Resource;
  56 +
  57 +import java.util.ArrayList;
  58 +import java.util.List;
  59 +import java.util.stream.Collectors;
  60 +
  61 +@RestController
  62 +@TbCoreComponent
  63 +@RequestMapping("/api")
  64 +public class EdgeController extends BaseController {
  65 +
  66 + public static final String EDGE_ID = "edgeId";
  67 +
  68 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  69 + @RequestMapping(value = "/edges/enabled", method = RequestMethod.GET)
  70 + @ResponseBody
  71 + public boolean isEdgesSupportEnabled() {
  72 + return edgesEnabled;
  73 + }
  74 +
  75 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  76 + @RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.GET)
  77 + @ResponseBody
  78 + public Edge getEdgeById(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
  79 + checkParameter(EDGE_ID, strEdgeId);
  80 + try {
  81 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  82 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  83 + if (Authority.CUSTOMER_USER.equals(getCurrentUser().getAuthority())) {
  84 + cleanUpSensitiveData(edge);
  85 + }
  86 + return edge;
  87 + } catch (Exception e) {
  88 + throw handleException(e);
  89 + }
  90 + }
  91 +
  92 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  93 + @RequestMapping(value = "/edge/info/{edgeId}", method = RequestMethod.GET)
  94 + @ResponseBody
  95 + public EdgeInfo getEdgeInfoById(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
  96 + checkParameter(EDGE_ID, strEdgeId);
  97 + try {
  98 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  99 + EdgeInfo edgeInfo = checkEdgeInfoId(edgeId, Operation.READ);
  100 + if (Authority.CUSTOMER_USER.equals(getCurrentUser().getAuthority())) {
  101 + cleanUpSensitiveData(edgeInfo);
  102 + }
  103 + return edgeInfo;
  104 + } catch (Exception e) {
  105 + throw handleException(e);
  106 + }
  107 + }
  108 +
  109 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  110 + @RequestMapping(value = "/edge", method = RequestMethod.POST)
  111 + @ResponseBody
  112 + public Edge saveEdge(@RequestBody Edge edge) throws ThingsboardException {
  113 + try {
  114 + TenantId tenantId = getCurrentUser().getTenantId();
  115 + edge.setTenantId(tenantId);
  116 + boolean created = edge.getId() == null;
  117 +
  118 + RuleChain edgeTemplateRootRuleChain = null;
  119 + if (created) {
  120 + edgeTemplateRootRuleChain = ruleChainService.getEdgeTemplateRootRuleChain(tenantId);
  121 + if (edgeTemplateRootRuleChain == null) {
  122 + throw new DataValidationException("Root edge rule chain is not available!");
  123 + }
  124 + }
  125 +
  126 + Operation operation = created ? Operation.CREATE : Operation.WRITE;
  127 +
  128 + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation,
  129 + edge.getId(), edge);
  130 +
  131 + Edge savedEdge = checkNotNull(edgeService.saveEdge(edge));
  132 +
  133 + if (created) {
  134 + ruleChainService.assignRuleChainToEdge(tenantId, edgeTemplateRootRuleChain.getId(), savedEdge.getId());
  135 + edgeNotificationService.setEdgeRootRuleChain(tenantId, savedEdge, edgeTemplateRootRuleChain.getId());
  136 + edgeService.assignDefaultRuleChainsToEdge(tenantId, savedEdge.getId());
  137 + }
  138 +
  139 + tbClusterService.onEntityStateChange(savedEdge.getTenantId(), savedEdge.getId(),
  140 + created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
  141 +
  142 + logEntityAction(savedEdge.getId(), savedEdge, null, created ? ActionType.ADDED : ActionType.UPDATED, null);
  143 + return savedEdge;
  144 + } catch (Exception e) {
  145 + logEntityAction(emptyId(EntityType.EDGE), edge,
  146 + null, edge.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
  147 + throw handleException(e);
  148 + }
  149 + }
  150 +
  151 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  152 + @RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.DELETE)
  153 + @ResponseStatus(value = HttpStatus.OK)
  154 + public void deleteEdge(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
  155 + checkParameter(EDGE_ID, strEdgeId);
  156 + try {
  157 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  158 + Edge edge = checkEdgeId(edgeId, Operation.DELETE);
  159 + edgeService.deleteEdge(getTenantId(), edgeId);
  160 +
  161 + tbClusterService.onEntityStateChange(getTenantId(), edgeId,
  162 + ComponentLifecycleEvent.DELETED);
  163 +
  164 + logEntityAction(edgeId, edge,
  165 + null,
  166 + ActionType.DELETED, null, strEdgeId);
  167 +
  168 + } catch (Exception e) {
  169 +
  170 + logEntityAction(emptyId(EntityType.EDGE),
  171 + null,
  172 + null,
  173 + ActionType.DELETED, e, strEdgeId);
  174 +
  175 + throw handleException(e);
  176 + }
  177 + }
  178 +
  179 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  180 + @RequestMapping(value = "/edges", params = {"pageSize", "page"}, method = RequestMethod.GET)
  181 + @ResponseBody
  182 + public PageData<Edge> getEdges(@RequestParam int pageSize,
  183 + @RequestParam int page,
  184 + @RequestParam(required = false) String textSearch,
  185 + @RequestParam(required = false) String sortProperty,
  186 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  187 + try {
  188 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  189 + TenantId tenantId = getCurrentUser().getTenantId();
  190 + return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink));
  191 + } catch (Exception e) {
  192 + throw handleException(e);
  193 + }
  194 + }
  195 +
  196 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  197 + @RequestMapping(value = "/customer/{customerId}/edge/{edgeId}", method = RequestMethod.POST)
  198 + @ResponseBody
  199 + public Edge assignEdgeToCustomer(@PathVariable("customerId") String strCustomerId,
  200 + @PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
  201 + checkParameter("customerId", strCustomerId);
  202 + checkParameter(EDGE_ID, strEdgeId);
  203 + try {
  204 + CustomerId customerId = new CustomerId(toUUID(strCustomerId));
  205 + Customer customer = checkCustomerId(customerId, Operation.READ);
  206 +
  207 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  208 + checkEdgeId(edgeId, Operation.ASSIGN_TO_CUSTOMER);
  209 +
  210 + Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(getCurrentUser().getTenantId(), edgeId, customerId));
  211 +
  212 + tbClusterService.onEntityStateChange(getTenantId(), edgeId,
  213 + ComponentLifecycleEvent.UPDATED);
  214 +
  215 + logEntityAction(edgeId, savedEdge,
  216 + savedEdge.getCustomerId(),
  217 + ActionType.ASSIGNED_TO_CUSTOMER, null, strEdgeId, strCustomerId, customer.getName());
  218 +
  219 + sendEntityAssignToCustomerNotificationMsg(savedEdge.getTenantId(), savedEdge.getId(),
  220 + customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
  221 +
  222 + return savedEdge;
  223 + } catch (Exception e) {
  224 + logEntityAction(emptyId(EntityType.EDGE), null,
  225 + null,
  226 + ActionType.ASSIGNED_TO_CUSTOMER, e, strEdgeId, strCustomerId);
  227 + throw handleException(e);
  228 + }
  229 + }
  230 +
  231 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  232 + @RequestMapping(value = "/customer/edge/{edgeId}", method = RequestMethod.DELETE)
  233 + @ResponseBody
  234 + public Edge unassignEdgeFromCustomer(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
  235 + checkParameter(EDGE_ID, strEdgeId);
  236 + try {
  237 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  238 + Edge edge = checkEdgeId(edgeId, Operation.UNASSIGN_FROM_CUSTOMER);
  239 + if (edge.getCustomerId() == null || edge.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
  240 + throw new IncorrectParameterException("Edge isn't assigned to any customer!");
  241 + }
  242 + Customer customer = checkCustomerId(edge.getCustomerId(), Operation.READ);
  243 +
  244 + Edge savedEdge = checkNotNull(edgeService.unassignEdgeFromCustomer(getCurrentUser().getTenantId(), edgeId));
  245 +
  246 + tbClusterService.onEntityStateChange(getTenantId(), edgeId,
  247 + ComponentLifecycleEvent.UPDATED);
  248 +
  249 + logEntityAction(edgeId, edge,
  250 + edge.getCustomerId(),
  251 + ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEdgeId, customer.getId().toString(), customer.getName());
  252 +
  253 + sendEntityAssignToCustomerNotificationMsg(savedEdge.getTenantId(), savedEdge.getId(),
  254 + customer.getId(), EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
  255 +
  256 + return savedEdge;
  257 + } catch (Exception e) {
  258 + logEntityAction(emptyId(EntityType.EDGE), null,
  259 + null,
  260 + ActionType.UNASSIGNED_FROM_CUSTOMER, e, strEdgeId);
  261 + throw handleException(e);
  262 + }
  263 + }
  264 +
  265 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  266 + @RequestMapping(value = "/customer/public/edge/{edgeId}", method = RequestMethod.POST)
  267 + @ResponseBody
  268 + public Edge assignEdgeToPublicCustomer(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
  269 + checkParameter(EDGE_ID, strEdgeId);
  270 + try {
  271 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  272 + Edge edge = checkEdgeId(edgeId, Operation.ASSIGN_TO_CUSTOMER);
  273 + Customer publicCustomer = customerService.findOrCreatePublicCustomer(edge.getTenantId());
  274 + Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(getCurrentUser().getTenantId(), edgeId, publicCustomer.getId()));
  275 +
  276 + tbClusterService.onEntityStateChange(getTenantId(), edgeId,
  277 + ComponentLifecycleEvent.UPDATED);
  278 +
  279 + logEntityAction(edgeId, savedEdge,
  280 + savedEdge.getCustomerId(),
  281 + ActionType.ASSIGNED_TO_CUSTOMER, null, strEdgeId, publicCustomer.getId().toString(), publicCustomer.getName());
  282 +
  283 + return savedEdge;
  284 + } catch (Exception e) {
  285 + logEntityAction(emptyId(EntityType.EDGE), null,
  286 + null,
  287 + ActionType.ASSIGNED_TO_CUSTOMER, e, strEdgeId);
  288 + throw handleException(e);
  289 + }
  290 + }
  291 +
  292 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  293 + @RequestMapping(value = "/tenant/edges", params = {"pageSize", "page"}, method = RequestMethod.GET)
  294 + @ResponseBody
  295 + public PageData<Edge> getTenantEdges(
  296 + @RequestParam int pageSize,
  297 + @RequestParam int page,
  298 + @RequestParam(required = false) String type,
  299 + @RequestParam(required = false) String textSearch,
  300 + @RequestParam(required = false) String sortProperty,
  301 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  302 + try {
  303 + TenantId tenantId = getCurrentUser().getTenantId();
  304 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  305 + if (type != null && type.trim().length() > 0) {
  306 + return checkNotNull(edgeService.findEdgesByTenantIdAndType(tenantId, type, pageLink));
  307 + } else {
  308 + return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink));
  309 + }
  310 + } catch (Exception e) {
  311 + throw handleException(e);
  312 + }
  313 + }
  314 +
  315 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  316 + @RequestMapping(value = "/tenant/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
  317 + @ResponseBody
  318 + public PageData<EdgeInfo> getTenantEdgeInfos(
  319 + @RequestParam int pageSize,
  320 + @RequestParam int page,
  321 + @RequestParam(required = false) String type,
  322 + @RequestParam(required = false) String textSearch,
  323 + @RequestParam(required = false) String sortProperty,
  324 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  325 + try {
  326 + TenantId tenantId = getCurrentUser().getTenantId();
  327 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  328 + if (type != null && type.trim().length() > 0) {
  329 + return checkNotNull(edgeService.findEdgeInfosByTenantIdAndType(tenantId, type, pageLink));
  330 + } else {
  331 + return checkNotNull(edgeService.findEdgeInfosByTenantId(tenantId, pageLink));
  332 + }
  333 + } catch (Exception e) {
  334 + throw handleException(e);
  335 + }
  336 + }
  337 +
  338 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  339 + @RequestMapping(value = "/tenant/edges", params = {"edgeName"}, method = RequestMethod.GET)
  340 + @ResponseBody
  341 + public Edge getTenantEdge(@RequestParam String edgeName) throws ThingsboardException {
  342 + try {
  343 + TenantId tenantId = getCurrentUser().getTenantId();
  344 + return checkNotNull(edgeService.findEdgeByTenantIdAndName(tenantId, edgeName));
  345 + } catch (Exception e) {
  346 + throw handleException(e);
  347 + }
  348 + }
  349 +
  350 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  351 + @RequestMapping(value = "/edge/{edgeId}/{ruleChainId}/root", method = RequestMethod.POST)
  352 + @ResponseBody
  353 + public Edge setRootRuleChain(@PathVariable(EDGE_ID) String strEdgeId,
  354 + @PathVariable("ruleChainId") String strRuleChainId) throws ThingsboardException {
  355 + checkParameter(EDGE_ID, strEdgeId);
  356 + checkParameter("ruleChainId", strRuleChainId);
  357 + try {
  358 + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
  359 + checkRuleChain(ruleChainId, Operation.WRITE);
  360 +
  361 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  362 + Edge edge = checkEdgeId(edgeId, Operation.WRITE);
  363 + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, Operation.WRITE,
  364 + edge.getId(), edge);
  365 +
  366 + Edge updatedEdge = edgeNotificationService.setEdgeRootRuleChain(getTenantId(), edge, ruleChainId);
  367 +
  368 + tbClusterService.onEntityStateChange(updatedEdge.getTenantId(), updatedEdge.getId(), ComponentLifecycleEvent.UPDATED);
  369 +
  370 + logEntityAction(updatedEdge.getId(), updatedEdge, null, ActionType.UPDATED, null);
  371 +
  372 + return updatedEdge;
  373 + } catch (Exception e) {
  374 + logEntityAction(emptyId(EntityType.EDGE),
  375 + null,
  376 + null,
  377 + ActionType.UPDATED, e, strEdgeId);
  378 + throw handleException(e);
  379 + }
  380 + }
  381 +
  382 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  383 + @RequestMapping(value = "/customer/{customerId}/edges", params = {"pageSize", "page"}, method = RequestMethod.GET)
  384 + @ResponseBody
  385 + public PageData<Edge> getCustomerEdges(
  386 + @PathVariable("customerId") String strCustomerId,
  387 + @RequestParam int pageSize,
  388 + @RequestParam int page,
  389 + @RequestParam(required = false) String type,
  390 + @RequestParam(required = false) String textSearch,
  391 + @RequestParam(required = false) String sortProperty,
  392 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  393 + checkParameter("customerId", strCustomerId);
  394 + try {
  395 + SecurityUser user = getCurrentUser();
  396 + TenantId tenantId = user.getTenantId();
  397 + CustomerId customerId = new CustomerId(toUUID(strCustomerId));
  398 + checkCustomerId(customerId, Operation.READ);
  399 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  400 + PageData<Edge> result;
  401 + if (type != null && type.trim().length() > 0) {
  402 + result = edgeService.findEdgesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink);
  403 + } else {
  404 + result = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink);
  405 + }
  406 + if (Authority.CUSTOMER_USER.equals(user.getAuthority())) {
  407 + for (Edge edge : result.getData()) {
  408 + cleanUpSensitiveData(edge);
  409 + }
  410 + }
  411 + return checkNotNull(result);
  412 + } catch (Exception e) {
  413 + throw handleException(e);
  414 + }
  415 + }
  416 +
  417 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  418 + @RequestMapping(value = "/customer/{customerId}/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
  419 + @ResponseBody
  420 + public PageData<EdgeInfo> getCustomerEdgeInfos(
  421 + @PathVariable("customerId") String strCustomerId,
  422 + @RequestParam int pageSize,
  423 + @RequestParam int page,
  424 + @RequestParam(required = false) String type,
  425 + @RequestParam(required = false) String textSearch,
  426 + @RequestParam(required = false) String sortProperty,
  427 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  428 + checkParameter("customerId", strCustomerId);
  429 + try {
  430 + SecurityUser user = getCurrentUser();
  431 + TenantId tenantId = user.getTenantId();
  432 + CustomerId customerId = new CustomerId(toUUID(strCustomerId));
  433 + checkCustomerId(customerId, Operation.READ);
  434 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  435 + PageData<EdgeInfo> result;
  436 + if (type != null && type.trim().length() > 0) {
  437 + result = edgeService.findEdgeInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink);
  438 + } else {
  439 + result = edgeService.findEdgeInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink);
  440 + }
  441 + if (Authority.CUSTOMER_USER.equals(user.getAuthority())) {
  442 + for (Edge edge : result.getData()) {
  443 + cleanUpSensitiveData(edge);
  444 + }
  445 + }
  446 + return checkNotNull(result);
  447 + } catch (Exception e) {
  448 + throw handleException(e);
  449 + }
  450 + }
  451 +
  452 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  453 + @RequestMapping(value = "/edges", params = {"edgeIds"}, method = RequestMethod.GET)
  454 + @ResponseBody
  455 + public List<Edge> getEdgesByIds(
  456 + @RequestParam("edgeIds") String[] strEdgeIds) throws ThingsboardException {
  457 + checkArrayParameter("edgeIds", strEdgeIds);
  458 + try {
  459 + SecurityUser user = getCurrentUser();
  460 + TenantId tenantId = user.getTenantId();
  461 + CustomerId customerId = user.getCustomerId();
  462 + List<EdgeId> edgeIds = new ArrayList<>();
  463 + for (String strEdgeId : strEdgeIds) {
  464 + edgeIds.add(new EdgeId(toUUID(strEdgeId)));
  465 + }
  466 + ListenableFuture<List<Edge>> edgesFuture;
  467 + if (customerId == null || customerId.isNullUid()) {
  468 + edgesFuture = edgeService.findEdgesByTenantIdAndIdsAsync(tenantId, edgeIds);
  469 + } else {
  470 + edgesFuture = edgeService.findEdgesByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, edgeIds);
  471 + }
  472 + List<Edge> edges = edgesFuture.get();
  473 + if (Authority.CUSTOMER_USER.equals(user.getAuthority())) {
  474 + for (Edge edge : edges) {
  475 + cleanUpSensitiveData(edge);
  476 + }
  477 + }
  478 + return checkNotNull(edges);
  479 + } catch (Exception e) {
  480 + throw handleException(e);
  481 + }
  482 + }
  483 +
  484 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  485 + @RequestMapping(value = "/edges", method = RequestMethod.POST)
  486 + @ResponseBody
  487 + public List<Edge> findByQuery(@RequestBody EdgeSearchQuery query) throws ThingsboardException {
  488 + checkNotNull(query);
  489 + checkNotNull(query.getParameters());
  490 + checkNotNull(query.getEdgeTypes());
  491 + checkEntityId(query.getParameters().getEntityId(), Operation.READ);
  492 + try {
  493 + SecurityUser user = getCurrentUser();
  494 + TenantId tenantId = user.getTenantId();
  495 + List<Edge> edges = checkNotNull(edgeService.findEdgesByQuery(tenantId, query).get());
  496 + edges = edges.stream().filter(edge -> {
  497 + try {
  498 + accessControlService.checkPermission(user, Resource.EDGE, Operation.READ, edge.getId(), edge);
  499 + return true;
  500 + } catch (ThingsboardException e) {
  501 + return false;
  502 + }
  503 + }).collect(Collectors.toList());
  504 + if (Authority.CUSTOMER_USER.equals(user.getAuthority())) {
  505 + for (Edge edge : edges) {
  506 + cleanUpSensitiveData(edge);
  507 + }
  508 + }
  509 + return edges;
  510 + } catch (Exception e) {
  511 + throw handleException(e);
  512 + }
  513 + }
  514 +
  515 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  516 + @RequestMapping(value = "/edge/types", method = RequestMethod.GET)
  517 + @ResponseBody
  518 + public List<EntitySubtype> getEdgeTypes() throws ThingsboardException {
  519 + try {
  520 + SecurityUser user = getCurrentUser();
  521 + TenantId tenantId = user.getTenantId();
  522 + ListenableFuture<List<EntitySubtype>> edgeTypes = edgeService.findEdgeTypesByTenantId(tenantId);
  523 + return checkNotNull(edgeTypes.get());
  524 + } catch (Exception e) {
  525 + throw handleException(e);
  526 + }
  527 + }
  528 +
  529 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  530 + @RequestMapping(value = "/edge/sync/{edgeId}", method = RequestMethod.POST)
  531 + public void syncEdge(@PathVariable("edgeId") String strEdgeId) throws ThingsboardException {
  532 + checkParameter("edgeId", strEdgeId);
  533 + try {
  534 + if (isEdgesEnabled()) {
  535 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  536 + edgeId = checkNotNull(edgeId);
  537 + SecurityUser user = getCurrentUser();
  538 + TenantId tenantId = user.getTenantId();
  539 + EdgeGrpcSession session = edgeGrpcService.getEdgeGrpcSessionById(tenantId, edgeId);
  540 + Edge edge = session.getEdge();
  541 + syncEdgeService.sync(tenantId, edge);
  542 + } else {
  543 + throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL);
  544 + }
  545 + } catch (Exception e) {
  546 + throw handleException(e);
  547 + }
  548 + }
  549 +
  550 + @RequestMapping(value = "/license/checkInstance", method = RequestMethod.POST)
  551 + @ResponseBody
  552 + public Object checkInstance(@RequestBody Object request) throws ThingsboardException {
  553 + try {
  554 + return edgeService.checkInstance(request);
  555 + } catch (Exception e) {
  556 + throw new ThingsboardException(e, ThingsboardErrorCode.SUBSCRIPTION_VIOLATION);
  557 + }
  558 + }
  559 +
  560 + @RequestMapping(value = "/license/activateInstance", params = {"licenseSecret", "releaseDate"}, method = RequestMethod.POST)
  561 + @ResponseBody
  562 + public Object activateInstance(@RequestParam String licenseSecret,
  563 + @RequestParam String releaseDate) throws ThingsboardException {
  564 + try {
  565 + return edgeService.activateInstance(licenseSecret, releaseDate);
  566 + } catch (Exception e) {
  567 + throw new ThingsboardException(e, ThingsboardErrorCode.SUBSCRIPTION_VIOLATION);
  568 + }
  569 + }
  570 +
  571 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  572 + @RequestMapping(value = "/edge/missingToRelatedRuleChains/{edgeId}", method = RequestMethod.GET)
  573 + @ResponseBody
  574 + public String findMissingToRelatedRuleChains(@PathVariable("edgeId") String strEdgeId) throws ThingsboardException {
  575 + try {
  576 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  577 + edgeId = checkNotNull(edgeId);
  578 + SecurityUser user = getCurrentUser();
  579 + TenantId tenantId = user.getTenantId();
  580 + return edgeService.findMissingToRelatedRuleChains(tenantId, edgeId);
  581 + } catch (Exception e) {
  582 + throw handleException(e);
  583 + }
  584 + }
  585 +
  586 + private void cleanUpSensitiveData(Edge edge) {
  587 + edge.setEdgeLicenseKey(null);
  588 + edge.setRoutingKey(null);
  589 + edge.setSecret(null);
  590 + edge.setCloudEndpoint(null);
  591 + edge.setRootRuleChainId(null);
  592 + }
  593 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.controller;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.beans.factory.annotation.Autowired;
  20 +import org.springframework.security.access.prepost.PreAuthorize;
  21 +import org.springframework.web.bind.annotation.PathVariable;
  22 +import org.springframework.web.bind.annotation.RequestMapping;
  23 +import org.springframework.web.bind.annotation.RequestMethod;
  24 +import org.springframework.web.bind.annotation.RequestParam;
  25 +import org.springframework.web.bind.annotation.ResponseBody;
  26 +import org.springframework.web.bind.annotation.RestController;
  27 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  28 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  29 +import org.thingsboard.server.common.data.id.EdgeId;
  30 +import org.thingsboard.server.common.data.id.TenantId;
  31 +import org.thingsboard.server.common.data.page.PageData;
  32 +import org.thingsboard.server.common.data.page.TimePageLink;
  33 +import org.thingsboard.server.dao.edge.EdgeEventService;
  34 +import org.thingsboard.server.queue.util.TbCoreComponent;
  35 +import org.thingsboard.server.service.security.permission.Operation;
  36 +
  37 +@Slf4j
  38 +@RestController
  39 +@TbCoreComponent
  40 +@RequestMapping("/api")
  41 +public class EdgeEventController extends BaseController {
  42 +
  43 + @Autowired
  44 + private EdgeEventService edgeEventService;
  45 +
  46 + public static final String EDGE_ID = "edgeId";
  47 +
  48 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  49 + @RequestMapping(value = "/edge/{edgeId}/events", method = RequestMethod.GET)
  50 + @ResponseBody
  51 + public PageData<EdgeEvent> getEdgeEvents(
  52 + @PathVariable(EDGE_ID) String strEdgeId,
  53 + @RequestParam int pageSize,
  54 + @RequestParam int page,
  55 + @RequestParam(required = false) String textSearch,
  56 + @RequestParam(required = false) String sortProperty,
  57 + @RequestParam(required = false) String sortOrder,
  58 + @RequestParam(required = false) Long startTime,
  59 + @RequestParam(required = false) Long endTime) throws ThingsboardException {
  60 + checkParameter(EDGE_ID, strEdgeId);
  61 + try {
  62 + TenantId tenantId = getCurrentUser().getTenantId();
  63 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  64 + checkEdgeId(edgeId, Operation.READ);
  65 + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
  66 + return checkNotNull(edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, false));
  67 + } catch (Exception e) {
  68 + throw handleException(e);
  69 + }
  70 + }
  71 +}
@@ -25,6 +25,7 @@ import org.springframework.web.bind.annotation.ResponseBody; @@ -25,6 +25,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
25 import org.springframework.web.bind.annotation.ResponseStatus; 25 import org.springframework.web.bind.annotation.ResponseStatus;
26 import org.springframework.web.bind.annotation.RestController; 26 import org.springframework.web.bind.annotation.RestController;
27 import org.thingsboard.server.common.data.audit.ActionType; 27 import org.thingsboard.server.common.data.audit.ActionType;
  28 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
28 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 29 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
29 import org.thingsboard.server.common.data.exception.ThingsboardException; 30 import org.thingsboard.server.common.data.exception.ThingsboardException;
30 import org.thingsboard.server.common.data.id.EntityId; 31 import org.thingsboard.server.common.data.id.EntityId;
@@ -63,10 +64,13 @@ public class EntityRelationController extends BaseController { @@ -63,10 +64,13 @@ public class EntityRelationController extends BaseController {
63 relation.setTypeGroup(RelationTypeGroup.COMMON); 64 relation.setTypeGroup(RelationTypeGroup.COMMON);
64 } 65 }
65 relationService.saveRelation(getTenantId(), relation); 66 relationService.saveRelation(getTenantId(), relation);
  67 +
66 logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(), 68 logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
67 ActionType.RELATION_ADD_OR_UPDATE, null, relation); 69 ActionType.RELATION_ADD_OR_UPDATE, null, relation);
68 logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(), 70 logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
69 ActionType.RELATION_ADD_OR_UPDATE, null, relation); 71 ActionType.RELATION_ADD_OR_UPDATE, null, relation);
  72 +
  73 + sendRelationNotificationMsg(getTenantId(), relation, EdgeEventActionType.RELATION_ADD_OR_UPDATE);
70 } catch (Exception e) { 74 } catch (Exception e) {
71 logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(), 75 logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
72 ActionType.RELATION_ADD_OR_UPDATE, e, relation); 76 ActionType.RELATION_ADD_OR_UPDATE, e, relation);
@@ -104,6 +108,8 @@ public class EntityRelationController extends BaseController { @@ -104,6 +108,8 @@ public class EntityRelationController extends BaseController {
104 ActionType.RELATION_DELETED, null, relation); 108 ActionType.RELATION_DELETED, null, relation);
105 logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(), 109 logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
106 ActionType.RELATION_DELETED, null, relation); 110 ActionType.RELATION_DELETED, null, relation);
  111 +
  112 + sendRelationNotificationMsg(getTenantId(), relation, EdgeEventActionType.RELATION_DELETED);
107 } catch (Exception e) { 113 } catch (Exception e) {
108 logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(), 114 logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
109 ActionType.RELATION_DELETED, e, relation); 115 ActionType.RELATION_DELETED, e, relation);
@@ -32,11 +32,20 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -32,11 +32,20 @@ import org.springframework.web.bind.annotation.RequestParam;
32 import org.springframework.web.bind.annotation.ResponseBody; 32 import org.springframework.web.bind.annotation.ResponseBody;
33 import org.springframework.web.bind.annotation.ResponseStatus; 33 import org.springframework.web.bind.annotation.ResponseStatus;
34 import org.springframework.web.bind.annotation.RestController; 34 import org.springframework.web.bind.annotation.RestController;
35 -import org.thingsboard.server.common.data.*; 35 +import org.thingsboard.server.common.data.Customer;
  36 +import org.thingsboard.server.common.data.DataConstants;
  37 +import org.thingsboard.server.common.data.EntitySubtype;
  38 +import org.thingsboard.server.common.data.EntityType;
  39 +import org.thingsboard.server.common.data.EntityView;
  40 +import org.thingsboard.server.common.data.EntityViewInfo;
  41 +import org.thingsboard.server.common.data.asset.Asset;
36 import org.thingsboard.server.common.data.audit.ActionType; 42 import org.thingsboard.server.common.data.audit.ActionType;
  43 +import org.thingsboard.server.common.data.edge.Edge;
  44 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
37 import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; 45 import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
38 import org.thingsboard.server.common.data.exception.ThingsboardException; 46 import org.thingsboard.server.common.data.exception.ThingsboardException;
39 import org.thingsboard.server.common.data.id.CustomerId; 47 import org.thingsboard.server.common.data.id.CustomerId;
  48 +import org.thingsboard.server.common.data.id.EdgeId;
40 import org.thingsboard.server.common.data.id.EntityId; 49 import org.thingsboard.server.common.data.id.EntityId;
41 import org.thingsboard.server.common.data.id.EntityViewId; 50 import org.thingsboard.server.common.data.id.EntityViewId;
42 import org.thingsboard.server.common.data.id.TenantId; 51 import org.thingsboard.server.common.data.id.TenantId;
@@ -47,6 +56,7 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery; @@ -47,6 +56,7 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
47 import org.thingsboard.server.common.data.kv.TsKvEntry; 56 import org.thingsboard.server.common.data.kv.TsKvEntry;
48 import org.thingsboard.server.common.data.page.PageData; 57 import org.thingsboard.server.common.data.page.PageData;
49 import org.thingsboard.server.common.data.page.PageLink; 58 import org.thingsboard.server.common.data.page.PageLink;
  59 +import org.thingsboard.server.common.data.page.TimePageLink;
50 import org.thingsboard.server.dao.exception.IncorrectParameterException; 60 import org.thingsboard.server.dao.exception.IncorrectParameterException;
51 import org.thingsboard.server.dao.model.ModelConstants; 61 import org.thingsboard.server.dao.model.ModelConstants;
52 import org.thingsboard.server.dao.timeseries.TimeseriesService; 62 import org.thingsboard.server.dao.timeseries.TimeseriesService;
@@ -65,6 +75,7 @@ import java.util.stream.Collectors; @@ -65,6 +75,7 @@ import java.util.stream.Collectors;
65 75
66 import static org.apache.commons.lang3.StringUtils.isBlank; 76 import static org.apache.commons.lang3.StringUtils.isBlank;
67 import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID; 77 import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID;
  78 +import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
68 79
69 /** 80 /**
70 * Created by Victor Basanets on 8/28/2017. 81 * Created by Victor Basanets on 8/28/2017.
@@ -150,6 +161,11 @@ public class EntityViewController extends BaseController { @@ -150,6 +161,11 @@ public class EntityViewController extends BaseController {
150 161
151 logEntityAction(savedEntityView.getId(), savedEntityView, null, 162 logEntityAction(savedEntityView.getId(), savedEntityView, null,
152 entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 163 entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
  164 +
  165 + if (entityView.getId() != null) {
  166 + sendEntityNotificationMsg(savedEntityView.getTenantId(), savedEntityView.getId(), EdgeEventActionType.UPDATED);
  167 + }
  168 +
153 return savedEntityView; 169 return savedEntityView;
154 } catch (Exception e) { 170 } catch (Exception e) {
155 logEntityAction(emptyId(EntityType.ENTITY_VIEW), entityView, null, 171 logEntityAction(emptyId(EntityType.ENTITY_VIEW), entityView, null,
@@ -243,7 +259,7 @@ public class EntityViewController extends BaseController { @@ -243,7 +259,7 @@ public class EntityViewController extends BaseController {
243 private ListenableFuture<List<Void>> copyLatestFromEntityToEntityView(EntityView entityView, SecurityUser user) { 259 private ListenableFuture<List<Void>> copyLatestFromEntityToEntityView(EntityView entityView, SecurityUser user) {
244 EntityViewId entityId = entityView.getId(); 260 EntityViewId entityId = entityView.getId();
245 List<String> keys = entityView.getKeys() != null && entityView.getKeys().getTimeseries() != null ? 261 List<String> keys = entityView.getKeys() != null && entityView.getKeys().getTimeseries() != null ?
246 - entityView.getKeys().getTimeseries() : Collections.emptyList(); 262 + entityView.getKeys().getTimeseries() : Collections.emptyList();
247 long startTs = entityView.getStartTimeMs(); 263 long startTs = entityView.getStartTimeMs();
248 long endTs = entityView.getEndTimeMs() == 0 ? Long.MAX_VALUE : entityView.getEndTimeMs(); 264 long endTs = entityView.getEndTimeMs() == 0 ? Long.MAX_VALUE : entityView.getEndTimeMs();
249 ListenableFuture<List<String>> keysFuture; 265 ListenableFuture<List<String>> keysFuture;
@@ -345,9 +361,14 @@ public class EntityViewController extends BaseController { @@ -345,9 +361,14 @@ public class EntityViewController extends BaseController {
345 try { 361 try {
346 EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); 362 EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
347 EntityView entityView = checkEntityViewId(entityViewId, Operation.DELETE); 363 EntityView entityView = checkEntityViewId(entityViewId, Operation.DELETE);
  364 +
  365 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), entityViewId);
  366 +
348 entityViewService.deleteEntityView(getTenantId(), entityViewId); 367 entityViewService.deleteEntityView(getTenantId(), entityViewId);
349 logEntityAction(entityViewId, entityView, entityView.getCustomerId(), 368 logEntityAction(entityViewId, entityView, entityView.getCustomerId(),
350 ActionType.DELETED, null, strEntityViewId); 369 ActionType.DELETED, null, strEntityViewId);
  370 +
  371 + sendDeleteNotificationMsg(getTenantId(), entityViewId, relatedEdgeIds);
351 } catch (Exception e) { 372 } catch (Exception e) {
352 logEntityAction(emptyId(EntityType.ENTITY_VIEW), 373 logEntityAction(emptyId(EntityType.ENTITY_VIEW),
353 null, 374 null,
@@ -388,6 +409,10 @@ public class EntityViewController extends BaseController { @@ -388,6 +409,10 @@ public class EntityViewController extends BaseController {
388 logEntityAction(entityViewId, savedEntityView, 409 logEntityAction(entityViewId, savedEntityView,
389 savedEntityView.getCustomerId(), 410 savedEntityView.getCustomerId(),
390 ActionType.ASSIGNED_TO_CUSTOMER, null, strEntityViewId, strCustomerId, customer.getName()); 411 ActionType.ASSIGNED_TO_CUSTOMER, null, strEntityViewId, strCustomerId, customer.getName());
  412 +
  413 + sendEntityAssignToCustomerNotificationMsg(savedEntityView.getTenantId(), savedEntityView.getId(),
  414 + customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
  415 +
391 return savedEntityView; 416 return savedEntityView;
392 } catch (Exception e) { 417 } catch (Exception e) {
393 logEntityAction(emptyId(EntityType.ENTITY_VIEW), null, 418 logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
@@ -414,6 +439,9 @@ public class EntityViewController extends BaseController { @@ -414,6 +439,9 @@ public class EntityViewController extends BaseController {
414 entityView.getCustomerId(), 439 entityView.getCustomerId(),
415 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEntityViewId, customer.getId().toString(), customer.getName()); 440 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEntityViewId, customer.getId().toString(), customer.getName());
416 441
  442 + sendEntityAssignToCustomerNotificationMsg(savedEntityView.getTenantId(), savedEntityView.getId(),
  443 + customer.getId(), EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
  444 +
417 return savedEntityView; 445 return savedEntityView;
418 } catch (Exception e) { 446 } catch (Exception e) {
419 logEntityAction(emptyId(EntityType.ENTITY_VIEW), null, 447 logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
@@ -585,4 +613,107 @@ public class EntityViewController extends BaseController { @@ -585,4 +613,107 @@ public class EntityViewController extends BaseController {
585 throw handleException(e); 613 throw handleException(e);
586 } 614 }
587 } 615 }
  616 +
  617 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  618 + @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.POST)
  619 + @ResponseBody
  620 + public EntityView assignEntityViewToEdge(@PathVariable(EDGE_ID) String strEdgeId,
  621 + @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
  622 + checkParameter(EDGE_ID, strEdgeId);
  623 + checkParameter(ENTITY_VIEW_ID, strEntityViewId);
  624 + try {
  625 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  626 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  627 +
  628 + EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
  629 + checkEntityViewId(entityViewId, Operation.ASSIGN_TO_EDGE);
  630 +
  631 + EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToEdge(getTenantId(), entityViewId, edgeId));
  632 + logEntityAction(entityViewId, savedEntityView,
  633 + savedEntityView.getCustomerId(),
  634 + ActionType.ASSIGNED_TO_EDGE, null, strEntityViewId, strEdgeId, edge.getName());
  635 +
  636 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedEntityView.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE);
  637 +
  638 + return savedEntityView;
  639 + } catch (Exception e) {
  640 + logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
  641 + null,
  642 + ActionType.ASSIGNED_TO_EDGE, e, strEntityViewId, strEdgeId);
  643 + throw handleException(e);
  644 + }
  645 + }
  646 +
  647 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  648 + @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.DELETE)
  649 + @ResponseBody
  650 + public EntityView unassignEntityViewFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
  651 + @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
  652 + checkParameter(EDGE_ID, strEdgeId);
  653 + checkParameter(ENTITY_VIEW_ID, strEntityViewId);
  654 + try {
  655 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  656 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  657 +
  658 + EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
  659 + EntityView entityView = checkEntityViewId(entityViewId, Operation.UNASSIGN_FROM_EDGE);
  660 +
  661 + EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(getTenantId(), entityViewId, edgeId));
  662 + logEntityAction(entityViewId, entityView,
  663 + entityView.getCustomerId(),
  664 + ActionType.UNASSIGNED_FROM_EDGE, null, strEntityViewId, strEdgeId, edge.getName());
  665 +
  666 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedEntityView.getId(), EdgeEventActionType.UNASSIGNED_FROM_EDGE);
  667 +
  668 + return savedEntityView;
  669 + } catch (Exception e) {
  670 + logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
  671 + null,
  672 + ActionType.UNASSIGNED_FROM_EDGE, e, strEntityViewId, strEdgeId);
  673 + throw handleException(e);
  674 + }
  675 + }
  676 +
  677 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  678 + @RequestMapping(value = "/edge/{edgeId}/entityViews", params = {"pageSize", "page"}, method = RequestMethod.GET)
  679 + @ResponseBody
  680 + public PageData<EntityView> getEdgeEntityViews(
  681 + @PathVariable(EDGE_ID) String strEdgeId,
  682 + @RequestParam int pageSize,
  683 + @RequestParam int page,
  684 + @RequestParam(required = false) String type,
  685 + @RequestParam(required = false) String textSearch,
  686 + @RequestParam(required = false) String sortProperty,
  687 + @RequestParam(required = false) String sortOrder,
  688 + @RequestParam(required = false) Long startTime,
  689 + @RequestParam(required = false) Long endTime) throws ThingsboardException {
  690 + checkParameter(EDGE_ID, strEdgeId);
  691 + try {
  692 + TenantId tenantId = getCurrentUser().getTenantId();
  693 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  694 + checkEdgeId(edgeId, Operation.READ);
  695 + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
  696 + PageData<EntityView> nonFilteredResult;
  697 + if (type != null && type.trim().length() > 0) {
  698 + nonFilteredResult = entityViewService.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink);
  699 + } else {
  700 + nonFilteredResult = entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink);
  701 + }
  702 + List<EntityView> filteredEntityViews = nonFilteredResult.getData().stream().filter(entityView -> {
  703 + try {
  704 + accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView);
  705 + return true;
  706 + } catch (ThingsboardException e) {
  707 + return false;
  708 + }
  709 + }).collect(Collectors.toList());
  710 + PageData<EntityView> filteredResult = new PageData<>(filteredEntityViews,
  711 + nonFilteredResult.getTotalPages(),
  712 + nonFilteredResult.getTotalElements(),
  713 + nonFilteredResult.hasNext());
  714 + return checkNotNull(filteredResult);
  715 + } catch (Exception e) {
  716 + throw handleException(e);
  717 + }
  718 + }
588 } 719 }
@@ -18,12 +18,14 @@ package org.thingsboard.server.controller; @@ -18,12 +18,14 @@ package org.thingsboard.server.controller;
18 import org.springframework.beans.factory.annotation.Autowired; 18 import org.springframework.beans.factory.annotation.Autowired;
19 import org.springframework.security.access.prepost.PreAuthorize; 19 import org.springframework.security.access.prepost.PreAuthorize;
20 import org.springframework.web.bind.annotation.PathVariable; 20 import org.springframework.web.bind.annotation.PathVariable;
  21 +import org.springframework.web.bind.annotation.RequestBody;
21 import org.springframework.web.bind.annotation.RequestMapping; 22 import org.springframework.web.bind.annotation.RequestMapping;
22 import org.springframework.web.bind.annotation.RequestMethod; 23 import org.springframework.web.bind.annotation.RequestMethod;
23 import org.springframework.web.bind.annotation.RequestParam; 24 import org.springframework.web.bind.annotation.RequestParam;
24 import org.springframework.web.bind.annotation.ResponseBody; 25 import org.springframework.web.bind.annotation.ResponseBody;
25 import org.springframework.web.bind.annotation.RestController; 26 import org.springframework.web.bind.annotation.RestController;
26 import org.thingsboard.server.common.data.Event; 27 import org.thingsboard.server.common.data.Event;
  28 +import org.thingsboard.server.common.data.event.EventFilter;
27 import org.thingsboard.server.common.data.exception.ThingsboardException; 29 import org.thingsboard.server.common.data.exception.ThingsboardException;
28 import org.thingsboard.server.common.data.id.EntityId; 30 import org.thingsboard.server.common.data.id.EntityId;
29 import org.thingsboard.server.common.data.id.EntityIdFactory; 31 import org.thingsboard.server.common.data.id.EntityIdFactory;
@@ -31,6 +33,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -31,6 +33,7 @@ import org.thingsboard.server.common.data.id.TenantId;
31 import org.thingsboard.server.common.data.page.PageData; 33 import org.thingsboard.server.common.data.page.PageData;
32 import org.thingsboard.server.common.data.page.TimePageLink; 34 import org.thingsboard.server.common.data.page.TimePageLink;
33 import org.thingsboard.server.dao.event.EventService; 35 import org.thingsboard.server.dao.event.EventService;
  36 +import org.thingsboard.server.dao.model.ModelConstants;
34 import org.thingsboard.server.queue.util.TbCoreComponent; 37 import org.thingsboard.server.queue.util.TbCoreComponent;
35 import org.thingsboard.server.service.security.permission.Operation; 38 import org.thingsboard.server.service.security.permission.Operation;
36 39
@@ -101,4 +104,38 @@ public class EventController extends BaseController { @@ -101,4 +104,38 @@ public class EventController extends BaseController {
101 } 104 }
102 } 105 }
103 106
  107 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  108 + @RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.POST)
  109 + @ResponseBody
  110 + public PageData<Event> getEvents(
  111 + @PathVariable("entityType") String strEntityType,
  112 + @PathVariable("entityId") String strEntityId,
  113 + @RequestParam("tenantId") String strTenantId,
  114 + @RequestParam int pageSize,
  115 + @RequestParam int page,
  116 + @RequestBody EventFilter eventFilter,
  117 + @RequestParam(required = false) String textSearch,
  118 + @RequestParam(required = false) String sortProperty,
  119 + @RequestParam(required = false) String sortOrder,
  120 + @RequestParam(required = false) Long startTime,
  121 + @RequestParam(required = false) Long endTime) throws ThingsboardException {
  122 + checkParameter("EntityId", strEntityId);
  123 + checkParameter("EntityType", strEntityType);
  124 + try {
  125 + TenantId tenantId = new TenantId(toUUID(strTenantId));
  126 +
  127 + EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
  128 + checkEntityId(entityId, Operation.READ);
  129 +
  130 + if(sortProperty != null && sortProperty.equals("createdTime") && eventFilter.hasFilterForJsonBody()) {
  131 + sortProperty = ModelConstants.CREATED_TIME_PROPERTY;
  132 + }
  133 +
  134 + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
  135 + return checkNotNull(eventService.findEventsByFilter(tenantId, entityId, eventFilter, pageLink));
  136 + } catch (Exception e) {
  137 + throw handleException(e);
  138 + }
  139 + }
  140 +
104 } 141 }
@@ -42,7 +42,10 @@ import org.thingsboard.server.common.data.DataConstants; @@ -42,7 +42,10 @@ import org.thingsboard.server.common.data.DataConstants;
42 import org.thingsboard.server.common.data.EntityType; 42 import org.thingsboard.server.common.data.EntityType;
43 import org.thingsboard.server.common.data.Event; 43 import org.thingsboard.server.common.data.Event;
44 import org.thingsboard.server.common.data.audit.ActionType; 44 import org.thingsboard.server.common.data.audit.ActionType;
  45 +import org.thingsboard.server.common.data.edge.Edge;
  46 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
45 import org.thingsboard.server.common.data.exception.ThingsboardException; 47 import org.thingsboard.server.common.data.exception.ThingsboardException;
  48 +import org.thingsboard.server.common.data.id.EdgeId;
46 import org.thingsboard.server.common.data.id.RuleChainId; 49 import org.thingsboard.server.common.data.id.RuleChainId;
47 import org.thingsboard.server.common.data.id.RuleNodeId; 50 import org.thingsboard.server.common.data.id.RuleNodeId;
48 import org.thingsboard.server.common.data.id.TenantId; 51 import org.thingsboard.server.common.data.id.TenantId;
@@ -54,6 +57,7 @@ import org.thingsboard.server.common.data.rule.RuleChain; @@ -54,6 +57,7 @@ import org.thingsboard.server.common.data.rule.RuleChain;
54 import org.thingsboard.server.common.data.rule.RuleChainData; 57 import org.thingsboard.server.common.data.rule.RuleChainData;
55 import org.thingsboard.server.common.data.rule.RuleChainImportResult; 58 import org.thingsboard.server.common.data.rule.RuleChainImportResult;
56 import org.thingsboard.server.common.data.rule.RuleChainMetaData; 59 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
  60 +import org.thingsboard.server.common.data.rule.RuleChainType;
57 import org.thingsboard.server.common.data.rule.RuleNode; 61 import org.thingsboard.server.common.data.rule.RuleNode;
58 import org.thingsboard.server.common.msg.TbMsg; 62 import org.thingsboard.server.common.msg.TbMsg;
59 import org.thingsboard.server.common.msg.TbMsgDataType; 63 import org.thingsboard.server.common.msg.TbMsgDataType;
@@ -138,13 +142,21 @@ public class RuleChainController extends BaseController { @@ -138,13 +142,21 @@ public class RuleChainController extends BaseController {
138 142
139 RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain)); 143 RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain));
140 144
141 - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(),  
142 - created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); 145 + if (RuleChainType.CORE.equals(savedRuleChain.getType())) {
  146 + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(),
  147 + created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
  148 + }
143 149
144 logEntityAction(savedRuleChain.getId(), savedRuleChain, 150 logEntityAction(savedRuleChain.getId(), savedRuleChain,
145 null, 151 null,
146 created ? ActionType.ADDED : ActionType.UPDATED, null); 152 created ? ActionType.ADDED : ActionType.UPDATED, null);
147 153
  154 + if (RuleChainType.EDGE.equals(savedRuleChain.getType())) {
  155 + if (!created) {
  156 + sendEntityNotificationMsg(savedRuleChain.getTenantId(), savedRuleChain.getId(), EdgeEventActionType.UPDATED);
  157 + }
  158 + }
  159 +
148 return savedRuleChain; 160 return savedRuleChain;
149 } catch (Exception e) { 161 } catch (Exception e) {
150 162
@@ -232,14 +244,22 @@ public class RuleChainController extends BaseController { @@ -232,14 +244,22 @@ public class RuleChainController extends BaseController {
232 } 244 }
233 245
234 RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE); 246 RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE);
235 - RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData)); 247 + checkNotNull(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData) ? true : null);
  248 + RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId()));
236 249
237 - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED); 250 + if (RuleChainType.CORE.equals(ruleChain.getType())) {
  251 + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
  252 + }
238 253
239 logEntityAction(ruleChain.getId(), ruleChain, 254 logEntityAction(ruleChain.getId(), ruleChain,
240 null, 255 null,
241 ActionType.UPDATED, null, ruleChainMetaData); 256 ActionType.UPDATED, null, ruleChainMetaData);
242 257
  258 + if (RuleChainType.EDGE.equals(ruleChain.getType())) {
  259 + sendEntityNotificationMsg(ruleChain.getTenantId(),
  260 + ruleChain.getId(), EdgeEventActionType.UPDATED);
  261 + }
  262 +
243 return savedRuleChainMetaData; 263 return savedRuleChainMetaData;
244 } catch (Exception e) { 264 } catch (Exception e) {
245 265
@@ -256,13 +276,18 @@ public class RuleChainController extends BaseController { @@ -256,13 +276,18 @@ public class RuleChainController extends BaseController {
256 public PageData<RuleChain> getRuleChains( 276 public PageData<RuleChain> getRuleChains(
257 @RequestParam int pageSize, 277 @RequestParam int pageSize,
258 @RequestParam int page, 278 @RequestParam int page,
  279 + @RequestParam(value = "type", required = false) String typeStr,
259 @RequestParam(required = false) String textSearch, 280 @RequestParam(required = false) String textSearch,
260 @RequestParam(required = false) String sortProperty, 281 @RequestParam(required = false) String sortProperty,
261 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 282 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
262 try { 283 try {
263 TenantId tenantId = getCurrentUser().getTenantId(); 284 TenantId tenantId = getCurrentUser().getTenantId();
264 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 285 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
265 - return checkNotNull(ruleChainService.findTenantRuleChains(tenantId, pageLink)); 286 + RuleChainType type = RuleChainType.CORE;
  287 + if (typeStr != null && typeStr.trim().length() > 0) {
  288 + type = RuleChainType.valueOf(typeStr);
  289 + }
  290 + return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, type, pageLink));
266 } catch (Exception e) { 291 } catch (Exception e) {
267 throw handleException(e); 292 throw handleException(e);
268 } 293 }
@@ -281,19 +306,30 @@ public class RuleChainController extends BaseController { @@ -281,19 +306,30 @@ public class RuleChainController extends BaseController {
281 306
282 Set<RuleChainId> referencingRuleChainIds = referencingRuleNodes.stream().map(RuleNode::getRuleChainId).collect(Collectors.toSet()); 307 Set<RuleChainId> referencingRuleChainIds = referencingRuleNodes.stream().map(RuleNode::getRuleChainId).collect(Collectors.toSet());
283 308
  309 + List<EdgeId> relatedEdgeIds = null;
  310 + if (RuleChainType.EDGE.equals(ruleChain.getType())) {
  311 + relatedEdgeIds = findRelatedEdgeIds(getTenantId(), ruleChainId);
  312 + }
  313 +
284 ruleChainService.deleteRuleChainById(getTenantId(), ruleChainId); 314 ruleChainService.deleteRuleChainById(getTenantId(), ruleChainId);
285 315
286 referencingRuleChainIds.remove(ruleChain.getId()); 316 referencingRuleChainIds.remove(ruleChain.getId());
287 317
288 - referencingRuleChainIds.forEach(referencingRuleChainId ->  
289 - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED)); 318 + if (RuleChainType.CORE.equals(ruleChain.getType())) {
  319 + referencingRuleChainIds.forEach(referencingRuleChainId ->
  320 + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
290 321
291 - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED); 322 + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
  323 + }
292 324
293 logEntityAction(ruleChainId, ruleChain, 325 logEntityAction(ruleChainId, ruleChain,
294 null, 326 null,
295 ActionType.DELETED, null, strRuleChainId); 327 ActionType.DELETED, null, strRuleChainId);
296 328
  329 + if (RuleChainType.EDGE.equals(ruleChain.getType())) {
  330 + sendDeleteNotificationMsg(ruleChain.getTenantId(), ruleChain.getId(), relatedEdgeIds);
  331 + }
  332 +
297 } catch (Exception e) { 333 } catch (Exception e) {
298 logEntityAction(emptyId(EntityType.RULE_CHAIN), 334 logEntityAction(emptyId(EntityType.RULE_CHAIN),
299 null, 335 null,
@@ -411,7 +447,7 @@ public class RuleChainController extends BaseController { @@ -411,7 +447,7 @@ public class RuleChainController extends BaseController {
411 public void importRuleChains(@RequestBody RuleChainData ruleChainData, @RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException { 447 public void importRuleChains(@RequestBody RuleChainData ruleChainData, @RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException {
412 try { 448 try {
413 TenantId tenantId = getCurrentUser().getTenantId(); 449 TenantId tenantId = getCurrentUser().getTenantId();
414 - List<RuleChainImportResult> importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite); 450 + List<RuleChainImportResult> importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, RuleChainType.CORE, overwrite);
415 if (!CollectionUtils.isEmpty(importResults)) { 451 if (!CollectionUtils.isEmpty(importResults)) {
416 for (RuleChainImportResult importResult : importResults) { 452 for (RuleChainImportResult importResult : importResults) {
417 tbClusterService.onEntityStateChange(importResult.getTenantId(), importResult.getRuleChainId(), importResult.getLifecycleEvent()); 453 tbClusterService.onEntityStateChange(importResult.getTenantId(), importResult.getRuleChainId(), importResult.getLifecycleEvent());
@@ -452,5 +488,160 @@ public class RuleChainController extends BaseController { @@ -452,5 +488,160 @@ public class RuleChainController extends BaseController {
452 return msgData; 488 return msgData;
453 } 489 }
454 490
  491 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  492 + @RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.POST)
  493 + @ResponseBody
  494 + public RuleChain assignRuleChainToEdge(@PathVariable("edgeId") String strEdgeId,
  495 + @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
  496 + checkParameter("edgeId", strEdgeId);
  497 + checkParameter(RULE_CHAIN_ID, strRuleChainId);
  498 + try {
  499 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  500 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  501 +
  502 + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
  503 + checkRuleChain(ruleChainId, Operation.ASSIGN_TO_EDGE);
  504 +
  505 + RuleChain savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId));
  506 +
  507 + logEntityAction(ruleChainId, savedRuleChain,
  508 + null,
  509 + ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, strEdgeId, edge.getName());
  510 +
  511 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedRuleChain.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE);
  512 +
  513 + return savedRuleChain;
  514 + } catch (Exception e) {
  515 +
  516 + logEntityAction(emptyId(EntityType.RULE_CHAIN), null,
  517 + null,
  518 + ActionType.ASSIGNED_TO_EDGE, e, strRuleChainId, strEdgeId);
  519 +
  520 + throw handleException(e);
  521 + }
  522 + }
  523 +
  524 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  525 + @RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.DELETE)
  526 + @ResponseBody
  527 + public RuleChain unassignRuleChainFromEdge(@PathVariable("edgeId") String strEdgeId,
  528 + @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
  529 + checkParameter("edgeId", strEdgeId);
  530 + checkParameter(RULE_CHAIN_ID, strRuleChainId);
  531 + try {
  532 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  533 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  534 + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
  535 + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.UNASSIGN_FROM_EDGE);
  536 +
  537 + RuleChain savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId, false));
  538 +
  539 + logEntityAction(ruleChainId, ruleChain,
  540 + null,
  541 + ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, strEdgeId, edge.getName());
  542 +
  543 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedRuleChain.getId(), EdgeEventActionType.UNASSIGNED_FROM_EDGE);
  544 +
  545 + return savedRuleChain;
  546 + } catch (Exception e) {
  547 +
  548 + logEntityAction(emptyId(EntityType.RULE_CHAIN), null,
  549 + null,
  550 + ActionType.UNASSIGNED_FROM_EDGE, e, strRuleChainId, strEdgeId);
  551 +
  552 + throw handleException(e);
  553 + }
  554 + }
  555 +
  556 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  557 + @RequestMapping(value = "/edge/{edgeId}/ruleChains", params = {"pageSize", "page"}, method = RequestMethod.GET)
  558 + @ResponseBody
  559 + public PageData<RuleChain> getEdgeRuleChains(
  560 + @PathVariable("edgeId") String strEdgeId,
  561 + @RequestParam int pageSize,
  562 + @RequestParam int page,
  563 + @RequestParam(required = false) String textSearch,
  564 + @RequestParam(required = false) String sortProperty,
  565 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  566 + checkParameter("edgeId", strEdgeId);
  567 + try {
  568 + TenantId tenantId = getCurrentUser().getTenantId();
  569 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  570 + checkEdgeId(edgeId, Operation.READ);
  571 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  572 + return checkNotNull(ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
  573 + } catch (Exception e) {
  574 + throw handleException(e);
  575 + }
  576 + }
  577 +
  578 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  579 + @RequestMapping(value = "/ruleChain/{ruleChainId}/edgeTemplateRoot", method = RequestMethod.POST)
  580 + @ResponseBody
  581 + public RuleChain setEdgeTemplateRootRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
  582 + checkParameter(RULE_CHAIN_ID, strRuleChainId);
  583 + try {
  584 + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
  585 + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
  586 + ruleChainService.setEdgeTemplateRootRuleChain(getTenantId(), ruleChainId);
  587 + return ruleChain;
  588 + } catch (Exception e) {
  589 + logEntityAction(emptyId(EntityType.RULE_CHAIN),
  590 + null,
  591 + null,
  592 + ActionType.UPDATED, e, strRuleChainId);
  593 + throw handleException(e);
  594 + }
  595 + }
  596 +
  597 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  598 + @RequestMapping(value = "/ruleChain/{ruleChainId}/autoAssignToEdge", method = RequestMethod.POST)
  599 + @ResponseBody
  600 + public RuleChain setAutoAssignToEdgeRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
  601 + checkParameter(RULE_CHAIN_ID, strRuleChainId);
  602 + try {
  603 + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
  604 + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
  605 + ruleChainService.setAutoAssignToEdgeRuleChain(getTenantId(), ruleChainId);
  606 + return ruleChain;
  607 + } catch (Exception e) {
  608 + logEntityAction(emptyId(EntityType.RULE_CHAIN),
  609 + null,
  610 + null,
  611 + ActionType.UPDATED, e, strRuleChainId);
  612 + throw handleException(e);
  613 + }
  614 + }
  615 +
  616 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  617 + @RequestMapping(value = "/ruleChain/{ruleChainId}/autoAssignToEdge", method = RequestMethod.DELETE)
  618 + @ResponseBody
  619 + public RuleChain unsetAutoAssignToEdgeRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
  620 + checkParameter(RULE_CHAIN_ID, strRuleChainId);
  621 + try {
  622 + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
  623 + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
  624 + ruleChainService.unsetAutoAssignToEdgeRuleChain(getTenantId(), ruleChainId);
  625 + return ruleChain;
  626 + } catch (Exception e) {
  627 + logEntityAction(emptyId(EntityType.RULE_CHAIN),
  628 + null,
  629 + null,
  630 + ActionType.UPDATED, e, strRuleChainId);
  631 + throw handleException(e);
  632 + }
  633 + }
  634 +
  635 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  636 + @RequestMapping(value = "/ruleChain/autoAssignToEdgeRuleChains", method = RequestMethod.GET)
  637 + @ResponseBody
  638 + public List<RuleChain> getAutoAssignToEdgeRuleChains() throws ThingsboardException {
  639 + try {
  640 + TenantId tenantId = getCurrentUser().getTenantId();
  641 + return checkNotNull(ruleChainService.findAutoAssignToEdgeRuleChainsByTenantId(tenantId)).get();
  642 + } catch (Exception e) {
  643 + throw handleException(e);
  644 + }
  645 + }
455 646
456 } 647 }
@@ -30,22 +30,19 @@ import org.springframework.web.bind.annotation.ResponseBody; @@ -30,22 +30,19 @@ import org.springframework.web.bind.annotation.ResponseBody;
30 import org.springframework.web.bind.annotation.RestController; 30 import org.springframework.web.bind.annotation.RestController;
31 import org.thingsboard.server.common.data.TbResource; 31 import org.thingsboard.server.common.data.TbResource;
32 import org.thingsboard.server.common.data.TbResourceInfo; 32 import org.thingsboard.server.common.data.TbResourceInfo;
33 -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;  
34 import org.thingsboard.server.common.data.exception.ThingsboardException; 33 import org.thingsboard.server.common.data.exception.ThingsboardException;
35 import org.thingsboard.server.common.data.id.TbResourceId; 34 import org.thingsboard.server.common.data.id.TbResourceId;
36 import org.thingsboard.server.common.data.lwm2m.LwM2mObject; 35 import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
37 import org.thingsboard.server.common.data.page.PageData; 36 import org.thingsboard.server.common.data.page.PageData;
38 import org.thingsboard.server.common.data.page.PageLink; 37 import org.thingsboard.server.common.data.page.PageLink;
39 import org.thingsboard.server.common.data.security.Authority; 38 import org.thingsboard.server.common.data.security.Authority;
40 -import org.thingsboard.server.dao.resource.TbResourceService;  
41 import org.thingsboard.server.queue.util.TbCoreComponent; 39 import org.thingsboard.server.queue.util.TbCoreComponent;
  40 +import org.thingsboard.server.service.resource.TbResourceService;
42 import org.thingsboard.server.service.security.permission.Operation; 41 import org.thingsboard.server.service.security.permission.Operation;
43 import org.thingsboard.server.service.security.permission.Resource; 42 import org.thingsboard.server.service.security.permission.Resource;
44 43
45 -import java.util.ArrayList;  
46 import java.util.Base64; 44 import java.util.Base64;
47 import java.util.List; 45 import java.util.List;
48 -import java.util.StringJoiner;  
49 46
50 @Slf4j 47 @Slf4j
51 @RestController 48 @RestController
@@ -105,25 +102,13 @@ public class TbResourceController extends BaseController { @@ -105,25 +102,13 @@ public class TbResourceController extends BaseController {
105 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 102 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
106 @RequestMapping(value = "/resource", method = RequestMethod.POST) 103 @RequestMapping(value = "/resource", method = RequestMethod.POST)
107 @ResponseBody 104 @ResponseBody
108 - public List<TbResource> saveResources(@RequestBody List<TbResource> resources) throws ThingsboardException { 105 + public TbResource saveResource(@RequestBody TbResource resource) throws ThingsboardException {
109 try { 106 try {
110 - List<TbResource> addResources = new ArrayList<>();  
111 - StringJoiner noSaveResources = new StringJoiner("; ");  
112 - resources.forEach(resource -> {  
113 - try {  
114 resource.setTenantId(getTenantId()); 107 resource.setTenantId(getTenantId());
115 checkEntity(resource.getId(), resource, Resource.TB_RESOURCE); 108 checkEntity(resource.getId(), resource, Resource.TB_RESOURCE);
116 - addResources.add(addResource(resource));  
117 - } catch (Exception e) {  
118 - noSaveResources.add(resource.getFileName());  
119 - log.warn("Fail save resource: [{}]", resource.getFileName(), e); 109 + return addResource(resource);
120 } 110 }
121 - });  
122 - if (noSaveResources.length() > 0) {  
123 - throw new ThingsboardException(String.format("Fail save resource: %s", noSaveResources.toString()), ThingsboardErrorCode.INVALID_ARGUMENTS);  
124 - }  
125 - return addResources;  
126 - } catch (Exception e) { 111 + catch (Exception e) {
127 throw handleException(e); 112 throw handleException(e);
128 } 113 }
129 } 114 }
@@ -95,6 +95,9 @@ public class TenantController extends BaseController { @@ -95,6 +95,9 @@ public class TenantController extends BaseController {
95 tenant = checkNotNull(tenantService.saveTenant(tenant)); 95 tenant = checkNotNull(tenantService.saveTenant(tenant));
96 if (newTenant) { 96 if (newTenant) {
97 installScripts.createDefaultRuleChains(tenant.getId()); 97 installScripts.createDefaultRuleChains(tenant.getId());
  98 + if (edgesEnabled) {
  99 + installScripts.createDefaultEdgeRuleChains(tenant.getId());
  100 + }
98 } 101 }
99 tenantProfileCache.evict(tenant.getId()); 102 tenantProfileCache.evict(tenant.getId());
100 tbClusterService.onTenantChange(tenant, null); 103 tbClusterService.onTenantChange(tenant, null);
@@ -36,9 +36,11 @@ import org.thingsboard.rule.engine.api.MailService; @@ -36,9 +36,11 @@ import org.thingsboard.rule.engine.api.MailService;
36 import org.thingsboard.server.common.data.EntityType; 36 import org.thingsboard.server.common.data.EntityType;
37 import org.thingsboard.server.common.data.User; 37 import org.thingsboard.server.common.data.User;
38 import org.thingsboard.server.common.data.audit.ActionType; 38 import org.thingsboard.server.common.data.audit.ActionType;
  39 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
39 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 40 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
40 import org.thingsboard.server.common.data.exception.ThingsboardException; 41 import org.thingsboard.server.common.data.exception.ThingsboardException;
41 import org.thingsboard.server.common.data.id.CustomerId; 42 import org.thingsboard.server.common.data.id.CustomerId;
  43 +import org.thingsboard.server.common.data.id.EdgeId;
42 import org.thingsboard.server.common.data.id.TenantId; 44 import org.thingsboard.server.common.data.id.TenantId;
43 import org.thingsboard.server.common.data.id.UserId; 45 import org.thingsboard.server.common.data.id.UserId;
44 import org.thingsboard.server.common.data.page.PageData; 46 import org.thingsboard.server.common.data.page.PageData;
@@ -57,6 +59,7 @@ import org.thingsboard.server.service.security.permission.Resource; @@ -57,6 +59,7 @@ import org.thingsboard.server.service.security.permission.Resource;
57 import org.thingsboard.server.service.security.system.SystemSecurityService; 59 import org.thingsboard.server.service.security.system.SystemSecurityService;
58 60
59 import javax.servlet.http.HttpServletRequest; 61 import javax.servlet.http.HttpServletRequest;
  62 +import java.util.List;
60 63
61 @RequiredArgsConstructor 64 @RequiredArgsConstructor
62 @RestController 65 @RestController
@@ -178,6 +181,9 @@ public class UserController extends BaseController { @@ -178,6 +181,9 @@ public class UserController extends BaseController {
178 savedUser.getCustomerId(), 181 savedUser.getCustomerId(),
179 user.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 182 user.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
180 183
  184 + sendEntityNotificationMsg(getTenantId(), savedUser.getId(),
  185 + user.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
  186 +
181 return savedUser; 187 return savedUser;
182 } catch (Exception e) { 188 } catch (Exception e) {
183 189
@@ -247,12 +253,17 @@ public class UserController extends BaseController { @@ -247,12 +253,17 @@ public class UserController extends BaseController {
247 try { 253 try {
248 UserId userId = new UserId(toUUID(strUserId)); 254 UserId userId = new UserId(toUUID(strUserId));
249 User user = checkUserId(userId, Operation.DELETE); 255 User user = checkUserId(userId, Operation.DELETE);
  256 +
  257 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), userId);
  258 +
250 userService.deleteUser(getCurrentUser().getTenantId(), userId); 259 userService.deleteUser(getCurrentUser().getTenantId(), userId);
251 260
252 logEntityAction(userId, user, 261 logEntityAction(userId, user,
253 user.getCustomerId(), 262 user.getCustomerId(),
254 ActionType.DELETED, null, strUserId); 263 ActionType.DELETED, null, strUserId);
255 264
  265 + sendDeleteNotificationMsg(getTenantId(), userId, relatedEdgeIds);
  266 +
256 } catch (Exception e) { 267 } catch (Exception e) {
257 logEntityAction(emptyId(EntityType.USER), 268 logEntityAction(emptyId(EntityType.USER),
258 null, 269 null,
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import lombok.extern.slf4j.Slf4j;
18 import org.springframework.http.HttpStatus; 19 import org.springframework.http.HttpStatus;
19 import org.springframework.security.access.prepost.PreAuthorize; 20 import org.springframework.security.access.prepost.PreAuthorize;
20 import org.springframework.web.bind.annotation.PathVariable; 21 import org.springframework.web.bind.annotation.PathVariable;
@@ -25,6 +26,8 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -25,6 +26,8 @@ import org.springframework.web.bind.annotation.RequestParam;
25 import org.springframework.web.bind.annotation.ResponseBody; 26 import org.springframework.web.bind.annotation.ResponseBody;
26 import org.springframework.web.bind.annotation.ResponseStatus; 27 import org.springframework.web.bind.annotation.ResponseStatus;
27 import org.springframework.web.bind.annotation.RestController; 28 import org.springframework.web.bind.annotation.RestController;
  29 +import org.thingsboard.server.common.data.EntityType;
  30 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
28 import org.thingsboard.server.common.data.exception.ThingsboardException; 31 import org.thingsboard.server.common.data.exception.ThingsboardException;
29 import org.thingsboard.server.common.data.id.TenantId; 32 import org.thingsboard.server.common.data.id.TenantId;
30 import org.thingsboard.server.common.data.id.WidgetTypeId; 33 import org.thingsboard.server.common.data.id.WidgetTypeId;
@@ -39,6 +42,7 @@ import org.thingsboard.server.service.security.permission.Resource; @@ -39,6 +42,7 @@ import org.thingsboard.server.service.security.permission.Resource;
39 42
40 import java.util.List; 43 import java.util.List;
41 44
  45 +@Slf4j
42 @RestController 46 @RestController
43 @TbCoreComponent 47 @TbCoreComponent
44 @RequestMapping("/api") 48 @RequestMapping("/api")
@@ -69,8 +73,12 @@ public class WidgetTypeController extends BaseController { @@ -69,8 +73,12 @@ public class WidgetTypeController extends BaseController {
69 } 73 }
70 74
71 checkEntity(widgetTypeDetails.getId(), widgetTypeDetails, Resource.WIDGET_TYPE); 75 checkEntity(widgetTypeDetails.getId(), widgetTypeDetails, Resource.WIDGET_TYPE);
  76 + WidgetTypeDetails savedWidgetTypeDetails = widgetTypeService.saveWidgetType(widgetTypeDetails);
72 77
73 - return checkNotNull(widgetTypeService.saveWidgetType(widgetTypeDetails)); 78 + sendEntityNotificationMsg(getTenantId(), savedWidgetTypeDetails.getId(),
  79 + widgetTypeDetails.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
  80 +
  81 + return checkNotNull(savedWidgetTypeDetails);
74 } catch (Exception e) { 82 } catch (Exception e) {
75 throw handleException(e); 83 throw handleException(e);
76 } 84 }
@@ -85,6 +93,9 @@ public class WidgetTypeController extends BaseController { @@ -85,6 +93,9 @@ public class WidgetTypeController extends BaseController {
85 WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); 93 WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId));
86 checkWidgetTypeId(widgetTypeId, Operation.DELETE); 94 checkWidgetTypeId(widgetTypeId, Operation.DELETE);
87 widgetTypeService.deleteWidgetType(getCurrentUser().getTenantId(), widgetTypeId); 95 widgetTypeService.deleteWidgetType(getCurrentUser().getTenantId(), widgetTypeId);
  96 +
  97 + sendEntityNotificationMsg(getTenantId(), widgetTypeId, EdgeEventActionType.DELETED);
  98 +
88 } catch (Exception e) { 99 } catch (Exception e) {
89 throw handleException(e); 100 throw handleException(e);
90 } 101 }
@@ -25,6 +25,7 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -25,6 +25,7 @@ import org.springframework.web.bind.annotation.RequestParam;
25 import org.springframework.web.bind.annotation.ResponseBody; 25 import org.springframework.web.bind.annotation.ResponseBody;
26 import org.springframework.web.bind.annotation.ResponseStatus; 26 import org.springframework.web.bind.annotation.ResponseStatus;
27 import org.springframework.web.bind.annotation.RestController; 27 import org.springframework.web.bind.annotation.RestController;
  28 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
28 import org.thingsboard.server.common.data.exception.ThingsboardException; 29 import org.thingsboard.server.common.data.exception.ThingsboardException;
29 import org.thingsboard.server.common.data.id.TenantId; 30 import org.thingsboard.server.common.data.id.TenantId;
30 import org.thingsboard.server.common.data.id.WidgetsBundleId; 31 import org.thingsboard.server.common.data.id.WidgetsBundleId;
@@ -68,7 +69,12 @@ public class WidgetsBundleController extends BaseController { @@ -68,7 +69,12 @@ public class WidgetsBundleController extends BaseController {
68 } 69 }
69 70
70 checkEntity(widgetsBundle.getId(), widgetsBundle, Resource.WIDGETS_BUNDLE); 71 checkEntity(widgetsBundle.getId(), widgetsBundle, Resource.WIDGETS_BUNDLE);
71 - return checkNotNull(widgetsBundleService.saveWidgetsBundle(widgetsBundle)); 72 + WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
  73 +
  74 + sendEntityNotificationMsg(getTenantId(), savedWidgetsBundle.getId(),
  75 + widgetsBundle.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
  76 +
  77 + return checkNotNull(savedWidgetsBundle);
72 } catch (Exception e) { 78 } catch (Exception e) {
73 throw handleException(e); 79 throw handleException(e);
74 } 80 }
@@ -83,6 +89,9 @@ public class WidgetsBundleController extends BaseController { @@ -83,6 +89,9 @@ public class WidgetsBundleController extends BaseController {
83 WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); 89 WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
84 checkWidgetsBundleId(widgetsBundleId, Operation.DELETE); 90 checkWidgetsBundleId(widgetsBundleId, Operation.DELETE);
85 widgetsBundleService.deleteWidgetsBundle(getTenantId(), widgetsBundleId); 91 widgetsBundleService.deleteWidgetsBundle(getTenantId(), widgetsBundleId);
  92 +
  93 + sendEntityNotificationMsg(getTenantId(), widgetsBundleId, EdgeEventActionType.DELETED);
  94 +
86 } catch (Exception e) { 95 } catch (Exception e) {
87 throw handleException(e); 96 throw handleException(e);
88 } 97 }
@@ -27,6 +27,7 @@ import org.springframework.security.authentication.LockedException; @@ -27,6 +27,7 @@ import org.springframework.security.authentication.LockedException;
27 import org.springframework.security.core.AuthenticationException; 27 import org.springframework.security.core.AuthenticationException;
28 import org.springframework.security.web.access.AccessDeniedHandler; 28 import org.springframework.security.web.access.AccessDeniedHandler;
29 import org.springframework.stereotype.Component; 29 import org.springframework.stereotype.Component;
  30 +import org.springframework.web.client.HttpClientErrorException;
30 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 31 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
31 import org.thingsboard.server.common.data.exception.ThingsboardException; 32 import org.thingsboard.server.common.data.exception.ThingsboardException;
32 import org.thingsboard.server.common.msg.tools.TbRateLimitsException; 33 import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
@@ -66,7 +67,12 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler { @@ -66,7 +67,12 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
66 response.setContentType(MediaType.APPLICATION_JSON_VALUE); 67 response.setContentType(MediaType.APPLICATION_JSON_VALUE);
67 68
68 if (exception instanceof ThingsboardException) { 69 if (exception instanceof ThingsboardException) {
69 - handleThingsboardException((ThingsboardException) exception, response); 70 + ThingsboardException thingsboardException = (ThingsboardException) exception;
  71 + if (thingsboardException.getErrorCode() == ThingsboardErrorCode.SUBSCRIPTION_VIOLATION) {
  72 + handleSubscriptionException((ThingsboardException) exception, response);
  73 + } else {
  74 + handleThingsboardException((ThingsboardException) exception, response);
  75 + }
70 } else if (exception instanceof TbRateLimitsException) { 76 } else if (exception instanceof TbRateLimitsException) {
71 handleRateLimitException(response, (TbRateLimitsException) exception); 77 handleRateLimitException(response, (TbRateLimitsException) exception);
72 } else if (exception instanceof AccessDeniedException) { 78 } else if (exception instanceof AccessDeniedException) {
@@ -126,6 +132,11 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler { @@ -126,6 +132,11 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
126 ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS)); 132 ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS));
127 } 133 }
128 134
  135 + private void handleSubscriptionException(ThingsboardException subscriptionException, HttpServletResponse response) throws IOException {
  136 + response.setStatus(HttpStatus.FORBIDDEN.value());
  137 + mapper.writeValue(response.getWriter(),
  138 + (new ObjectMapper()).readValue(((HttpClientErrorException) subscriptionException.getCause()).getResponseBodyAsByteArray(), Object.class));
  139 + }
129 140
130 private void handleAccessDeniedException(HttpServletResponse response) throws IOException { 141 private void handleAccessDeniedException(HttpServletResponse response) throws IOException {
131 response.setStatus(HttpStatus.FORBIDDEN.value()); 142 response.setStatus(HttpStatus.FORBIDDEN.value());
@@ -193,7 +193,9 @@ public class ThingsboardInstallService { @@ -193,7 +193,9 @@ public class ThingsboardInstallService {
193 databaseEntitiesUpgradeService.upgradeDatabase("3.2.1"); 193 databaseEntitiesUpgradeService.upgradeDatabase("3.2.1");
194 case "3.2.2": 194 case "3.2.2":
195 log.info("Upgrading ThingsBoard from version 3.2.2 to 3.3.0 ..."); 195 log.info("Upgrading ThingsBoard from version 3.2.2 to 3.3.0 ...");
196 - databaseEntitiesUpgradeService.upgradeDatabase("3.2.2"); 196 + databaseEntitiesUpgradeService.upgradeDatabase("3.2.2");
  197 +
  198 + dataUpdateService.updateData("3.2.2");
197 199
198 log.info("Updating system data..."); 200 log.info("Updating system data...");
199 systemDataLoaderService.updateSystemWidgets(); 201 systemDataLoaderService.updateSystemWidgets();
@@ -34,13 +34,13 @@ import org.thingsboard.rule.engine.api.TbRelationTypes; @@ -34,13 +34,13 @@ import org.thingsboard.rule.engine.api.TbRelationTypes;
34 import org.thingsboard.server.common.data.id.TenantId; 34 import org.thingsboard.server.common.data.id.TenantId;
35 import org.thingsboard.server.common.data.plugin.ComponentDescriptor; 35 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
36 import org.thingsboard.server.common.data.plugin.ComponentType; 36 import org.thingsboard.server.common.data.plugin.ComponentType;
  37 +import org.thingsboard.server.common.data.rule.RuleChainType;
37 import org.thingsboard.server.dao.component.ComponentDescriptorService; 38 import org.thingsboard.server.dao.component.ComponentDescriptorService;
38 39
39 import javax.annotation.PostConstruct; 40 import javax.annotation.PostConstruct;
40 import java.lang.annotation.Annotation; 41 import java.lang.annotation.Annotation;
41 import java.util.ArrayList; 42 import java.util.ArrayList;
42 import java.util.Arrays; 43 import java.util.Arrays;
43 -import java.util.Collection;  
44 import java.util.Collections; 44 import java.util.Collections;
45 import java.util.HashMap; 45 import java.util.HashMap;
46 import java.util.HashSet; 46 import java.util.HashSet;
@@ -65,7 +65,9 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -65,7 +65,9 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
65 65
66 private Map<String, ComponentDescriptor> components = new HashMap<>(); 66 private Map<String, ComponentDescriptor> components = new HashMap<>();
67 67
68 - private Map<ComponentType, List<ComponentDescriptor>> componentsMap = new HashMap<>(); 68 + private Map<ComponentType, List<ComponentDescriptor>> coreComponentsMap = new HashMap<>();
  69 +
  70 + private Map<ComponentType, List<ComponentDescriptor>> edgeComponentsMap = new HashMap<>();
69 71
70 private ObjectMapper mapper = new ObjectMapper(); 72 private ObjectMapper mapper = new ObjectMapper();
71 73
@@ -93,7 +95,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -93,7 +95,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
93 ComponentType type = ruleNodeAnnotation.type(); 95 ComponentType type = ruleNodeAnnotation.type();
94 ComponentDescriptor component = scanAndPersistComponent(def, type); 96 ComponentDescriptor component = scanAndPersistComponent(def, type);
95 components.put(component.getClazz(), component); 97 components.put(component.getClazz(), component);
96 - componentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component); 98 + putComponentIntoMaps(type, ruleNodeAnnotation, component);
97 break; 99 break;
98 } catch (Exception e) { 100 } catch (Exception e) {
99 log.trace("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e); 101 log.trace("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e);
@@ -113,22 +115,35 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -113,22 +115,35 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
113 } 115 }
114 } 116 }
115 117
116 - private void registerComponents(ComponentType type, Class<? extends Annotation> annotation) {  
117 - List<ComponentDescriptor> components = persist(getBeanDefinitions(annotation), type);  
118 - componentsMap.put(type, components);  
119 - registerComponents(components);  
120 - }  
121 -  
122 - private void registerComponents(Collection<ComponentDescriptor> comps) {  
123 - comps.forEach(c -> components.put(c.getClazz(), c)); 118 + private void putComponentIntoMaps(ComponentType type, RuleNode ruleNodeAnnotation, ComponentDescriptor component) {
  119 + boolean ruleChainTypesMethodAvailable;
  120 + try {
  121 + ruleNodeAnnotation.getClass().getMethod("ruleChainTypes");
  122 + ruleChainTypesMethodAvailable = true;
  123 + } catch (NoSuchMethodException exception) {
  124 + log.warn("[{}] does not have ruleChainTypes. Probably extension class compiled before 3.3 release. " +
  125 + "Please update your extensions and compile using latest 3.3 release dependency", ruleNodeAnnotation.name());
  126 + ruleChainTypesMethodAvailable = false;
  127 + }
  128 + if (ruleChainTypesMethodAvailable) {
  129 + if (ruleChainTypeContainsArray(RuleChainType.CORE, ruleNodeAnnotation.ruleChainTypes())) {
  130 + coreComponentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component);
  131 + }
  132 + if (ruleChainTypeContainsArray(RuleChainType.EDGE, ruleNodeAnnotation.ruleChainTypes())) {
  133 + edgeComponentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component);
  134 + }
  135 + } else {
  136 + coreComponentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component);
  137 + }
124 } 138 }
125 139
126 - private List<ComponentDescriptor> persist(Set<BeanDefinition> filterDefs, ComponentType type) {  
127 - List<ComponentDescriptor> result = new ArrayList<>();  
128 - for (BeanDefinition def : filterDefs) {  
129 - result.add(scanAndPersistComponent(def, type)); 140 + private boolean ruleChainTypeContainsArray(RuleChainType ruleChainType, RuleChainType[] array) {
  141 + for (RuleChainType tmp : array) {
  142 + if (ruleChainType.equals(tmp)) {
  143 + return true;
  144 + }
130 } 145 }
131 - return result; 146 + return false;
132 } 147 }
133 148
134 private ComponentDescriptor scanAndPersistComponent(BeanDefinition def, ComponentType type) { 149 private ComponentDescriptor scanAndPersistComponent(BeanDefinition def, ComponentType type) {
@@ -222,25 +237,47 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -222,25 +237,47 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
222 } 237 }
223 238
224 @Override 239 @Override
225 - public List<ComponentDescriptor> getComponents(ComponentType type) {  
226 - if (componentsMap.containsKey(type)) {  
227 - return Collections.unmodifiableList(componentsMap.get(type)); 240 + public List<ComponentDescriptor> getComponents(ComponentType type, RuleChainType ruleChainType) {
  241 + if (RuleChainType.CORE.equals(ruleChainType)) {
  242 + if (coreComponentsMap.containsKey(type)) {
  243 + return Collections.unmodifiableList(coreComponentsMap.get(type));
  244 + } else {
  245 + return Collections.emptyList();
  246 + }
  247 + } else if (RuleChainType.EDGE.equals(ruleChainType)) {
  248 + if (edgeComponentsMap.containsKey(type)) {
  249 + return Collections.unmodifiableList(edgeComponentsMap.get(type));
  250 + } else {
  251 + return Collections.emptyList();
  252 + }
228 } else { 253 } else {
229 - return Collections.emptyList(); 254 + log.error("Unsupported rule chain type {}", ruleChainType);
  255 + throw new RuntimeException("Unsupported rule chain type " + ruleChainType);
230 } 256 }
231 } 257 }
232 258
233 @Override 259 @Override
234 - public List<ComponentDescriptor> getComponents(Set<ComponentType> types) {  
235 - List<ComponentDescriptor> result = new ArrayList<>();  
236 - types.stream().filter(type -> componentsMap.containsKey(type)).forEach(type -> {  
237 - result.addAll(componentsMap.get(type));  
238 - });  
239 - return Collections.unmodifiableList(result); 260 + public List<ComponentDescriptor> getComponents(Set<ComponentType> types, RuleChainType ruleChainType) {
  261 + if (RuleChainType.CORE.equals(ruleChainType)) {
  262 + return getComponents(types, coreComponentsMap);
  263 + } else if (RuleChainType.EDGE.equals(ruleChainType)) {
  264 + return getComponents(types, edgeComponentsMap);
  265 + } else {
  266 + log.error("Unsupported rule chain type {}", ruleChainType);
  267 + throw new RuntimeException("Unsupported rule chain type " + ruleChainType);
  268 + }
240 } 269 }
241 270
242 @Override 271 @Override
243 public Optional<ComponentDescriptor> getComponent(String clazz) { 272 public Optional<ComponentDescriptor> getComponent(String clazz) {
244 return Optional.ofNullable(components.get(clazz)); 273 return Optional.ofNullable(components.get(clazz));
245 } 274 }
  275 +
  276 + private List<ComponentDescriptor> getComponents(Set<ComponentType> types, Map<ComponentType, List<ComponentDescriptor>> componentsMap) {
  277 + List<ComponentDescriptor> result = new ArrayList<>();
  278 + types.stream().filter(componentsMap::containsKey).forEach(type -> {
  279 + result.addAll(componentsMap.get(type));
  280 + });
  281 + return Collections.unmodifiableList(result);
  282 + }
246 } 283 }
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.component; @@ -17,6 +17,7 @@ package org.thingsboard.server.service.component;
17 17
18 import org.thingsboard.server.common.data.plugin.ComponentDescriptor; 18 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
19 import org.thingsboard.server.common.data.plugin.ComponentType; 19 import org.thingsboard.server.common.data.plugin.ComponentType;
  20 +import org.thingsboard.server.common.data.rule.RuleChainType;
20 21
21 import java.util.List; 22 import java.util.List;
22 import java.util.Optional; 23 import java.util.Optional;
@@ -29,10 +30,9 @@ public interface ComponentDiscoveryService { @@ -29,10 +30,9 @@ public interface ComponentDiscoveryService {
29 30
30 void discoverComponents(); 31 void discoverComponents();
31 32
32 - List<ComponentDescriptor> getComponents(ComponentType type); 33 + List<ComponentDescriptor> getComponents(ComponentType type, RuleChainType ruleChainType);
33 34
34 - List<ComponentDescriptor> getComponents(Set<ComponentType> types); 35 + List<ComponentDescriptor> getComponents(Set<ComponentType> types, RuleChainType ruleChainType);
35 36
36 Optional<ComponentDescriptor> getComponent(String clazz); 37 Optional<ComponentDescriptor> getComponent(String clazz);
37 -  
38 } 38 }
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.edge;
  17 +
  18 +import com.fasterxml.jackson.core.JsonProcessingException;
  19 +import com.fasterxml.jackson.databind.JsonNode;
  20 +import com.fasterxml.jackson.databind.ObjectMapper;
  21 +import com.google.common.util.concurrent.FutureCallback;
  22 +import com.google.common.util.concurrent.Futures;
  23 +import com.google.common.util.concurrent.ListenableFuture;
  24 +import lombok.extern.slf4j.Slf4j;
  25 +import org.checkerframework.checker.nullness.qual.Nullable;
  26 +import org.springframework.beans.factory.annotation.Autowired;
  27 +import org.springframework.stereotype.Service;
  28 +import org.thingsboard.server.common.data.EdgeUtils;
  29 +import org.thingsboard.server.common.data.EntityType;
  30 +import org.thingsboard.server.common.data.User;
  31 +import org.thingsboard.server.common.data.alarm.Alarm;
  32 +import org.thingsboard.server.common.data.edge.Edge;
  33 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  34 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  35 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  36 +import org.thingsboard.server.common.data.id.AlarmId;
  37 +import org.thingsboard.server.common.data.id.CustomerId;
  38 +import org.thingsboard.server.common.data.id.EdgeId;
  39 +import org.thingsboard.server.common.data.id.EntityId;
  40 +import org.thingsboard.server.common.data.id.EntityIdFactory;
  41 +import org.thingsboard.server.common.data.id.RuleChainId;
  42 +import org.thingsboard.server.common.data.id.TenantId;
  43 +import org.thingsboard.server.common.data.page.PageData;
  44 +import org.thingsboard.server.common.data.page.PageLink;
  45 +import org.thingsboard.server.common.data.page.TimePageLink;
  46 +import org.thingsboard.server.common.data.relation.EntityRelation;
  47 +import org.thingsboard.server.common.data.rule.RuleChain;
  48 +import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
  49 +import org.thingsboard.server.common.msg.queue.TbCallback;
  50 +import org.thingsboard.server.dao.alarm.AlarmService;
  51 +import org.thingsboard.server.dao.edge.EdgeEventService;
  52 +import org.thingsboard.server.dao.edge.EdgeService;
  53 +import org.thingsboard.server.dao.rule.RuleChainService;
  54 +import org.thingsboard.server.dao.user.UserService;
  55 +import org.thingsboard.server.gen.transport.TransportProtos;
  56 +import org.thingsboard.server.queue.util.TbCoreComponent;
  57 +import org.thingsboard.server.service.executors.DbCallbackExecutorService;
  58 +import org.thingsboard.server.service.queue.TbClusterService;
  59 +
  60 +import javax.annotation.PostConstruct;
  61 +import javax.annotation.PreDestroy;
  62 +import java.io.IOException;
  63 +import java.util.ArrayList;
  64 +import java.util.HashSet;
  65 +import java.util.List;
  66 +import java.util.Set;
  67 +import java.util.UUID;
  68 +import java.util.concurrent.ExecutorService;
  69 +import java.util.concurrent.Executors;
  70 +
  71 +@Service
  72 +@TbCoreComponent
  73 +@Slf4j
  74 +public class DefaultEdgeNotificationService implements EdgeNotificationService {
  75 +
  76 + private static final ObjectMapper mapper = new ObjectMapper();
  77 +
  78 + private static final int DEFAULT_LIMIT = 100;
  79 +
  80 + @Autowired
  81 + private EdgeService edgeService;
  82 +
  83 + @Autowired
  84 + private AlarmService alarmService;
  85 +
  86 + @Autowired
  87 + private UserService userService;
  88 +
  89 + @Autowired
  90 + private RuleChainService ruleChainService;
  91 +
  92 + @Autowired
  93 + private EdgeEventService edgeEventService;
  94 +
  95 + @Autowired
  96 + private TbClusterService clusterService;
  97 +
  98 + @Autowired
  99 + private DbCallbackExecutorService dbCallbackExecutorService;
  100 +
  101 + private ExecutorService tsCallBackExecutor;
  102 +
  103 + @PostConstruct
  104 + public void initExecutor() {
  105 + tsCallBackExecutor = Executors.newSingleThreadExecutor();
  106 + }
  107 +
  108 + @PreDestroy
  109 + public void shutdownExecutor() {
  110 + if (tsCallBackExecutor != null) {
  111 + tsCallBackExecutor.shutdownNow();
  112 + }
  113 + }
  114 +
  115 + @Override
  116 + public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException {
  117 + edge.setRootRuleChainId(ruleChainId);
  118 + Edge savedEdge = edgeService.saveEdge(edge);
  119 + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, EdgeEventActionType.UPDATED, ruleChainId, null);
  120 + return savedEdge;
  121 + }
  122 +
  123 + private void saveEdgeEvent(TenantId tenantId,
  124 + EdgeId edgeId,
  125 + EdgeEventType type,
  126 + EdgeEventActionType action,
  127 + EntityId entityId,
  128 + JsonNode body) {
  129 + log.debug("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]",
  130 + tenantId, edgeId, type, action, entityId, body);
  131 +
  132 + EdgeEvent edgeEvent = new EdgeEvent();
  133 + edgeEvent.setEdgeId(edgeId);
  134 + edgeEvent.setTenantId(tenantId);
  135 + edgeEvent.setType(type);
  136 + edgeEvent.setAction(action);
  137 + if (entityId != null) {
  138 + edgeEvent.setEntityId(entityId.getId());
  139 + }
  140 + edgeEvent.setBody(body);
  141 + ListenableFuture<EdgeEvent> future = edgeEventService.saveAsync(edgeEvent);
  142 + Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
  143 + @Override
  144 + public void onSuccess(@Nullable EdgeEvent result) {
  145 + clusterService.onEdgeEventUpdate(tenantId, edgeId);
  146 + }
  147 +
  148 + @Override
  149 + public void onFailure(Throwable t) {
  150 + log.warn("[{}] Can't save edge event [{}] for edge [{}]", tenantId.getId(), edgeEvent, edgeId.getId(), t);
  151 + }
  152 + }, dbCallbackExecutorService);
  153 +
  154 + }
  155 +
  156 + @Override
  157 + public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) {
  158 + try {
  159 + TenantId tenantId = new TenantId(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB()));
  160 + EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
  161 + switch (type) {
  162 + case EDGE:
  163 + processEdge(tenantId, edgeNotificationMsg);
  164 + break;
  165 + case USER:
  166 + case ASSET:
  167 + case DEVICE:
  168 + case DEVICE_PROFILE:
  169 + case ENTITY_VIEW:
  170 + case DASHBOARD:
  171 + case RULE_CHAIN:
  172 + processEntity(tenantId, edgeNotificationMsg);
  173 + break;
  174 + case CUSTOMER:
  175 + processCustomer(tenantId, edgeNotificationMsg);
  176 + break;
  177 + case WIDGETS_BUNDLE:
  178 + case WIDGET_TYPE:
  179 + processWidgetBundleOrWidgetType(tenantId, edgeNotificationMsg);
  180 + break;
  181 + case ALARM:
  182 + processAlarm(tenantId, edgeNotificationMsg);
  183 + break;
  184 + case RELATION:
  185 + processRelation(tenantId, edgeNotificationMsg);
  186 + break;
  187 + default:
  188 + log.debug("Edge event type [{}] is not designed to be pushed to edge", type);
  189 + }
  190 + } catch (Exception e) {
  191 + callback.onFailure(e);
  192 + log.error("Can't push to edge updates, edgeNotificationMsg [{}]", edgeNotificationMsg, e);
  193 + } finally {
  194 + callback.onSuccess();
  195 + }
  196 + }
  197 +
  198 + private void processEdge(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
  199 + try {
  200 + EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
  201 + EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
  202 + ListenableFuture<Edge> edgeFuture;
  203 + switch (actionType) {
  204 + case ASSIGNED_TO_CUSTOMER:
  205 + CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
  206 + edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);
  207 + Futures.addCallback(edgeFuture, new FutureCallback<Edge>() {
  208 + @Override
  209 + public void onSuccess(@Nullable Edge edge) {
  210 + if (edge != null && !customerId.isNullUid()) {
  211 + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, customerId, null);
  212 + PageLink pageLink = new PageLink(DEFAULT_LIMIT);
  213 + PageData<User> pageData;
  214 + do {
  215 + pageData = userService.findCustomerUsers(tenantId, customerId, pageLink);
  216 + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
  217 + log.trace("[{}] [{}] user(s) are going to be added to edge.", edge.getId(), pageData.getData().size());
  218 + for (User user : pageData.getData()) {
  219 + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, EdgeEventActionType.ADDED, user.getId(), null);
  220 + }
  221 + if (pageData.hasNext()) {
  222 + pageLink = pageLink.nextPageLink();
  223 + }
  224 + }
  225 + } while (pageData != null && pageData.hasNext());
  226 + }
  227 + }
  228 +
  229 + @Override
  230 + public void onFailure(Throwable t) {
  231 + log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);
  232 + }
  233 + }, dbCallbackExecutorService);
  234 + break;
  235 + case UNASSIGNED_FROM_CUSTOMER:
  236 + CustomerId customerIdToDelete = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
  237 + edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);
  238 + Futures.addCallback(edgeFuture, new FutureCallback<Edge>() {
  239 + @Override
  240 + public void onSuccess(@Nullable Edge edge) {
  241 + if (edge != null && !customerIdToDelete.isNullUid()) {
  242 + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.DELETED, customerIdToDelete, null);
  243 + }
  244 + }
  245 +
  246 + @Override
  247 + public void onFailure(Throwable t) {
  248 + log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);
  249 + }
  250 + }, dbCallbackExecutorService);
  251 + break;
  252 + }
  253 + } catch (Exception e) {
  254 + log.error("Exception during processing edge event", e);
  255 + }
  256 + }
  257 +
  258 + private void processWidgetBundleOrWidgetType(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
  259 + EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
  260 + EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
  261 + EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
  262 + switch (actionType) {
  263 + case ADDED:
  264 + case UPDATED:
  265 + case DELETED:
  266 + processActionForAllEdges(tenantId, type, actionType, entityId);
  267 + break;
  268 + }
  269 + }
  270 +
  271 + private void processCustomer(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
  272 + EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
  273 + EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
  274 + UUID uuid = new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB());
  275 + CustomerId customerId = new CustomerId(EntityIdFactory.getByEdgeEventTypeAndUuid(type, uuid).getId());
  276 + switch (actionType) {
  277 + case UPDATED:
  278 + PageLink pageLink = new PageLink(DEFAULT_LIMIT);
  279 + PageData<Edge> pageData;
  280 + do {
  281 + pageData = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink);
  282 + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
  283 + for (Edge edge : pageData.getData()) {
  284 + saveEdgeEvent(tenantId, edge.getId(), type, actionType, customerId, null);
  285 + }
  286 + if (pageData.hasNext()) {
  287 + pageLink = pageLink.nextPageLink();
  288 + }
  289 + }
  290 + } while (pageData != null && pageData.hasNext());
  291 + break;
  292 + case DELETED:
  293 + EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
  294 + saveEdgeEvent(tenantId, edgeId, type, actionType, customerId, null);
  295 + break;
  296 + }
  297 + }
  298 +
  299 + private void processEntity(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
  300 + EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
  301 + EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
  302 + EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type,
  303 + new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
  304 + EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
  305 + ListenableFuture<List<EdgeId>> edgeIdsFuture;
  306 + switch (actionType) {
  307 + case ADDED: // used only for USER entity
  308 + case UPDATED:
  309 + case CREDENTIALS_UPDATED:
  310 + edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);
  311 + Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {
  312 + @Override
  313 + public void onSuccess(@Nullable List<EdgeId> edgeIds) {
  314 + if (edgeIds != null && !edgeIds.isEmpty()) {
  315 + for (EdgeId edgeId : edgeIds) {
  316 + saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
  317 + }
  318 + }
  319 + }
  320 + @Override
  321 + public void onFailure(Throwable throwable) {
  322 + log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable);
  323 + }
  324 + }, dbCallbackExecutorService);
  325 + break;
  326 + case ASSIGNED_TO_CUSTOMER:
  327 + case UNASSIGNED_FROM_CUSTOMER:
  328 + edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);
  329 + Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {
  330 + @Override
  331 + public void onSuccess(@Nullable List<EdgeId> edgeIds) {
  332 + if (edgeIds != null && !edgeIds.isEmpty()) {
  333 + for (EdgeId edgeId : edgeIds) {
  334 + try {
  335 + CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
  336 + ListenableFuture<Edge> future = edgeService.findEdgeByIdAsync(tenantId, edgeId);
  337 + Futures.addCallback(future, new FutureCallback<Edge>() {
  338 + @Override
  339 + public void onSuccess(@Nullable Edge edge) {
  340 + if (edge != null && edge.getCustomerId() != null &&
  341 + !edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(customerId)) {
  342 + saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
  343 + }
  344 + }
  345 + @Override
  346 + public void onFailure(Throwable throwable) {
  347 + log.error("Failed to find edge by id [{}]", edgeNotificationMsg, throwable);
  348 + }
  349 + }, dbCallbackExecutorService);
  350 + } catch (Exception e) {
  351 + log.error("Can't parse customer id from entity body [{}]", edgeNotificationMsg, e);
  352 + }
  353 + }
  354 + }
  355 + }
  356 +
  357 + @Override
  358 + public void onFailure(Throwable throwable) {
  359 + log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable);
  360 + }
  361 + }, dbCallbackExecutorService);
  362 + break;
  363 + case DELETED:
  364 + saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
  365 + break;
  366 + case ASSIGNED_TO_EDGE:
  367 + case UNASSIGNED_FROM_EDGE:
  368 + saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
  369 + if (type.equals(EdgeEventType.RULE_CHAIN)) {
  370 + updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId);
  371 + }
  372 + break;
  373 + }
  374 + }
  375 +
  376 + private void updateDependentRuleChains(TenantId tenantId, RuleChainId processingRuleChainId, EdgeId edgeId) {
  377 + PageLink pageLink = new PageLink(DEFAULT_LIMIT);
  378 + PageData<RuleChain> pageData;
  379 + do {
  380 + pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink);
  381 + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
  382 + for (RuleChain ruleChain : pageData.getData()) {
  383 + if (!ruleChain.getId().equals(processingRuleChainId)) {
  384 + List<RuleChainConnectionInfo> connectionInfos =
  385 + ruleChainService.loadRuleChainMetaData(ruleChain.getTenantId(), ruleChain.getId()).getRuleChainConnections();
  386 + if (connectionInfos != null && !connectionInfos.isEmpty()) {
  387 + for (RuleChainConnectionInfo connectionInfo : connectionInfos) {
  388 + if (connectionInfo.getTargetRuleChainId().equals(processingRuleChainId)) {
  389 + saveEdgeEvent(tenantId,
  390 + edgeId,
  391 + EdgeEventType.RULE_CHAIN_METADATA,
  392 + EdgeEventActionType.UPDATED,
  393 + ruleChain.getId(),
  394 + null);
  395 + }
  396 + }
  397 + }
  398 + }
  399 + }
  400 + if (pageData.hasNext()) {
  401 + pageLink = pageLink.nextPageLink();
  402 + }
  403 + }
  404 + } while (pageData != null && pageData.hasNext());
  405 + }
  406 +
  407 + private void processAlarm(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
  408 + AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
  409 + ListenableFuture<Alarm> alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId);
  410 + Futures.addCallback(alarmFuture, new FutureCallback<Alarm>() {
  411 + @Override
  412 + public void onSuccess(@Nullable Alarm alarm) {
  413 + if (alarm != null) {
  414 + EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(alarm.getOriginator().getEntityType());
  415 + if (type != null) {
  416 + ListenableFuture<List<EdgeId>> relatedEdgeIdsByEntityIdFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator());
  417 + Futures.addCallback(relatedEdgeIdsByEntityIdFuture, new FutureCallback<List<EdgeId>>() {
  418 + @Override
  419 + public void onSuccess(@Nullable List<EdgeId> relatedEdgeIdsByEntityId) {
  420 + if (relatedEdgeIdsByEntityId != null) {
  421 + for (EdgeId edgeId : relatedEdgeIdsByEntityId) {
  422 + saveEdgeEvent(tenantId,
  423 + edgeId,
  424 + EdgeEventType.ALARM,
  425 + EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
  426 + alarmId,
  427 + null);
  428 + }
  429 + }
  430 + }
  431 +
  432 + @Override
  433 + public void onFailure(Throwable t) {
  434 + log.warn("[{}] can't find related edge ids by entity id [{}]", tenantId.getId(), alarm.getOriginator(), t);
  435 + }
  436 + }, dbCallbackExecutorService);
  437 + }
  438 + }
  439 + }
  440 +
  441 + @Override
  442 + public void onFailure(Throwable t) {
  443 + log.warn("[{}] can't find alarm by id [{}]", tenantId.getId(), alarmId.getId(), t);
  444 + }
  445 + }, dbCallbackExecutorService);
  446 + }
  447 +
  448 + private void processRelation(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException {
  449 + EntityRelation relation = mapper.readValue(edgeNotificationMsg.getBody(), EntityRelation.class);
  450 + if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
  451 + !relation.getTo().getEntityType().equals(EntityType.EDGE)) {
  452 + List<ListenableFuture<List<EdgeId>>> futures = new ArrayList<>();
  453 + futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getTo()));
  454 + futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getFrom()));
  455 + ListenableFuture<List<List<EdgeId>>> combinedFuture = Futures.allAsList(futures);
  456 + Futures.addCallback(combinedFuture, new FutureCallback<List<List<EdgeId>>>() {
  457 + @Override
  458 + public void onSuccess(@Nullable List<List<EdgeId>> listOfListsEdgeIds) {
  459 + Set<EdgeId> uniqueEdgeIds = new HashSet<>();
  460 + if (listOfListsEdgeIds != null && !listOfListsEdgeIds.isEmpty()) {
  461 + for (List<EdgeId> listOfListsEdgeId : listOfListsEdgeIds) {
  462 + if (listOfListsEdgeId != null) {
  463 + uniqueEdgeIds.addAll(listOfListsEdgeId);
  464 + }
  465 + }
  466 + }
  467 + if (!uniqueEdgeIds.isEmpty()) {
  468 + for (EdgeId edgeId : uniqueEdgeIds) {
  469 + saveEdgeEvent(tenantId,
  470 + edgeId,
  471 + EdgeEventType.RELATION,
  472 + EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
  473 + null,
  474 + mapper.valueToTree(relation));
  475 + }
  476 + }
  477 + }
  478 +
  479 + @Override
  480 + public void onFailure(Throwable t) {
  481 + log.warn("[{}] can't find related edge ids by relation to id [{}] and relation from id [{}]" ,
  482 + tenantId.getId(), relation.getTo().getId(), relation.getFrom().getId(), t);
  483 + }
  484 + }, dbCallbackExecutorService);
  485 + }
  486 + }
  487 +
  488 + private void processActionForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId) {
  489 + PageLink pageLink = new PageLink(DEFAULT_LIMIT);
  490 + PageData<Edge> pageData;
  491 + do {
  492 + pageData = edgeService.findEdgesByTenantId(tenantId, pageLink);
  493 + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
  494 + for (Edge edge : pageData.getData()) {
  495 + saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null);
  496 + }
  497 + if (pageData.hasNext()) {
  498 + pageLink = pageLink.nextPageLink();
  499 + }
  500 + }
  501 + } while (pageData != null && pageData.hasNext());
  502 + }
  503 +}
  504 +
  505 +
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.edge;
  17 +
  18 +import lombok.Data;
  19 +import lombok.Getter;
  20 +import org.springframework.beans.factory.annotation.Autowired;
  21 +import org.springframework.context.annotation.Lazy;
  22 +import org.springframework.stereotype.Component;
  23 +import org.thingsboard.server.actors.service.ActorService;
  24 +import org.thingsboard.server.dao.alarm.AlarmService;
  25 +import org.thingsboard.server.dao.asset.AssetService;
  26 +import org.thingsboard.server.dao.attributes.AttributesService;
  27 +import org.thingsboard.server.dao.customer.CustomerService;
  28 +import org.thingsboard.server.dao.dashboard.DashboardService;
  29 +import org.thingsboard.server.dao.device.DeviceCredentialsService;
  30 +import org.thingsboard.server.dao.device.DeviceProfileService;
  31 +import org.thingsboard.server.dao.device.DeviceService;
  32 +import org.thingsboard.server.dao.edge.EdgeEventService;
  33 +import org.thingsboard.server.dao.edge.EdgeService;
  34 +import org.thingsboard.server.dao.entityview.EntityViewService;
  35 +import org.thingsboard.server.dao.relation.RelationService;
  36 +import org.thingsboard.server.dao.rule.RuleChainService;
  37 +import org.thingsboard.server.dao.user.UserService;
  38 +import org.thingsboard.server.dao.widget.WidgetTypeService;
  39 +import org.thingsboard.server.dao.widget.WidgetsBundleService;
  40 +import org.thingsboard.server.queue.discovery.PartitionService;
  41 +import org.thingsboard.server.queue.util.TbCoreComponent;
  42 +import org.thingsboard.server.service.edge.rpc.EdgeEventStorageSettings;
  43 +import org.thingsboard.server.service.edge.rpc.constructor.AdminSettingsMsgConstructor;
  44 +import org.thingsboard.server.service.edge.rpc.constructor.AlarmMsgConstructor;
  45 +import org.thingsboard.server.service.edge.rpc.constructor.AssetMsgConstructor;
  46 +import org.thingsboard.server.service.edge.rpc.constructor.CustomerMsgConstructor;
  47 +import org.thingsboard.server.service.edge.rpc.constructor.DashboardMsgConstructor;
  48 +import org.thingsboard.server.service.edge.rpc.constructor.DeviceMsgConstructor;
  49 +import org.thingsboard.server.service.edge.rpc.constructor.DeviceProfileMsgConstructor;
  50 +import org.thingsboard.server.service.edge.rpc.constructor.EntityDataMsgConstructor;
  51 +import org.thingsboard.server.service.edge.rpc.constructor.EntityViewMsgConstructor;
  52 +import org.thingsboard.server.service.edge.rpc.constructor.RelationMsgConstructor;
  53 +import org.thingsboard.server.service.edge.rpc.constructor.RuleChainMsgConstructor;
  54 +import org.thingsboard.server.service.edge.rpc.constructor.UserMsgConstructor;
  55 +import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstructor;
  56 +import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor;
  57 +import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService;
  58 +import org.thingsboard.server.service.edge.rpc.processor.AlarmProcessor;
  59 +import org.thingsboard.server.service.edge.rpc.processor.DeviceProcessor;
  60 +import org.thingsboard.server.service.edge.rpc.processor.RelationProcessor;
  61 +import org.thingsboard.server.service.edge.rpc.processor.TelemetryProcessor;
  62 +import org.thingsboard.server.service.executors.DbCallbackExecutorService;
  63 +import org.thingsboard.server.service.queue.TbClusterService;
  64 +import org.thingsboard.server.service.state.DeviceStateService;
  65 +
  66 +@Component
  67 +@TbCoreComponent
  68 +@Data
  69 +public class EdgeContextComponent {
  70 +
  71 + @Lazy
  72 + @Autowired
  73 + private EdgeService edgeService;
  74 +
  75 + @Autowired
  76 + private PartitionService partitionService;
  77 +
  78 + @Lazy
  79 + @Autowired
  80 + private EdgeEventService edgeEventService;
  81 +
  82 + @Lazy
  83 + @Autowired
  84 + private AssetService assetService;
  85 +
  86 + @Lazy
  87 + @Autowired
  88 + private DeviceService deviceService;
  89 +
  90 + @Lazy
  91 + @Autowired
  92 + private DeviceProfileService deviceProfileService;
  93 +
  94 + @Lazy
  95 + @Autowired
  96 + private DeviceCredentialsService deviceCredentialsService;
  97 +
  98 + @Lazy
  99 + @Autowired
  100 + private EntityViewService entityViewService;
  101 +
  102 + @Lazy
  103 + @Autowired
  104 + private AttributesService attributesService;
  105 +
  106 + @Lazy
  107 + @Autowired
  108 + private CustomerService customerService;
  109 +
  110 + @Lazy
  111 + @Autowired
  112 + private RelationService relationService;
  113 +
  114 + @Lazy
  115 + @Autowired
  116 + private AlarmService alarmService;
  117 +
  118 + @Lazy
  119 + @Autowired
  120 + private DashboardService dashboardService;
  121 +
  122 + @Lazy
  123 + @Autowired
  124 + private RuleChainService ruleChainService;
  125 +
  126 + @Lazy
  127 + @Autowired
  128 + private UserService userService;
  129 +
  130 + @Lazy
  131 + @Autowired
  132 + private ActorService actorService;
  133 +
  134 + @Lazy
  135 + @Autowired
  136 + private WidgetsBundleService widgetsBundleService;
  137 +
  138 + @Lazy
  139 + @Autowired
  140 + private WidgetTypeService widgetTypeService;
  141 +
  142 + @Lazy
  143 + @Autowired
  144 + private DeviceStateService deviceStateService;
  145 +
  146 + @Lazy
  147 + @Autowired
  148 + private TbClusterService tbClusterService;
  149 +
  150 + @Lazy
  151 + @Autowired
  152 + private SyncEdgeService syncEdgeService;
  153 +
  154 + @Lazy
  155 + @Autowired
  156 + private RuleChainMsgConstructor ruleChainMsgConstructor;
  157 +
  158 + @Lazy
  159 + @Autowired
  160 + private AlarmMsgConstructor alarmMsgConstructor;
  161 +
  162 + @Lazy
  163 + @Autowired
  164 + private DeviceMsgConstructor deviceMsgConstructor;
  165 +
  166 + @Lazy
  167 + @Autowired
  168 + private DeviceProfileMsgConstructor deviceProfileMsgConstructor;
  169 +
  170 + @Lazy
  171 + @Autowired
  172 + private AssetMsgConstructor assetMsgConstructor;
  173 +
  174 + @Lazy
  175 + @Autowired
  176 + private EntityViewMsgConstructor entityViewMsgConstructor;
  177 +
  178 + @Lazy
  179 + @Autowired
  180 + private DashboardMsgConstructor dashboardMsgConstructor;
  181 +
  182 + @Lazy
  183 + @Autowired
  184 + private CustomerMsgConstructor customerMsgConstructor;
  185 +
  186 + @Lazy
  187 + @Autowired
  188 + private UserMsgConstructor userMsgConstructor;
  189 +
  190 + @Lazy
  191 + @Autowired
  192 + private RelationMsgConstructor relationMsgConstructor;
  193 +
  194 + @Lazy
  195 + @Autowired
  196 + private WidgetsBundleMsgConstructor widgetsBundleMsgConstructor;
  197 +
  198 + @Lazy
  199 + @Autowired
  200 + private WidgetTypeMsgConstructor widgetTypeMsgConstructor;
  201 +
  202 + @Lazy
  203 + @Autowired
  204 + private AdminSettingsMsgConstructor adminSettingsMsgConstructor;
  205 +
  206 + @Lazy
  207 + @Autowired
  208 + private EntityDataMsgConstructor entityDataMsgConstructor;
  209 +
  210 + @Lazy
  211 + @Autowired
  212 + private AlarmProcessor alarmProcessor;
  213 +
  214 + @Lazy
  215 + @Autowired
  216 + private DeviceProcessor deviceProcessor;
  217 +
  218 + @Lazy
  219 + @Autowired
  220 + private RelationProcessor relationProcessor;
  221 +
  222 + @Lazy
  223 + @Autowired
  224 + private TelemetryProcessor telemetryProcessor;
  225 +
  226 + @Lazy
  227 + @Autowired
  228 + private EdgeEventStorageSettings edgeEventStorageSettings;
  229 +
  230 + @Autowired
  231 + @Getter
  232 + private DbCallbackExecutorService dbCallbackExecutor;
  233 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.edge;
  17 +
  18 +import org.thingsboard.server.common.data.edge.Edge;
  19 +import org.thingsboard.server.common.data.id.RuleChainId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.msg.queue.TbCallback;
  22 +import org.thingsboard.server.gen.transport.TransportProtos;
  23 +
  24 +import java.io.IOException;
  25 +
  26 +public interface EdgeNotificationService {
  27 +
  28 + Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException;
  29 +
  30 + void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback);
  31 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.edge.rpc;
  17 +
  18 +
  19 +import lombok.Data;
  20 +import org.springframework.beans.factory.annotation.Value;
  21 +import org.springframework.stereotype.Component;
  22 +
  23 +@Component
  24 +@Data
  25 +public class EdgeEventStorageSettings {
  26 + @Value("${edges.storage.max_read_records_count}")
  27 + private int maxReadRecordsCount;
  28 + @Value("${edges.storage.no_read_records_sleep}")
  29 + private long noRecordsSleepInterval;
  30 + @Value("${edges.storage.sleep_between_batches}")
  31 + private long sleepIntervalBetweenBatches;
  32 +}