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 33 **/.env
34 34 .instance_id
35 35 rebuild-docker.sh
  36 +*/.run/**
36 37 .run/**
37 38 .run
\ No newline at end of file
... ...
... ... @@ -102,6 +102,10 @@
102 102 <artifactId>stats</artifactId>
103 103 </dependency>
104 104 <dependency>
  105 + <groupId>org.thingsboard.common</groupId>
  106 + <artifactId>edge-api</artifactId>
  107 + </dependency>
  108 + <dependency>
105 109 <groupId>org.thingsboard</groupId>
106 110 <artifactId>dao</artifactId>
107 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 +}
\ No newline at end of file
... ...
  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 455 "dataKeySettingsSchema": "{}\n",
456 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 +}
\ No newline at end of file
... ...
... ... @@ -2,6 +2,7 @@
2 2 "ruleChain": {
3 3 "additionalInfo": null,
4 4 "name": "Root Rule Chain",
  5 + "type": "CORE",
5 6 "firstRuleNodeId": null,
6 7 "root": true,
7 8 "debugMode": false,
... ...
... ... @@ -14,6 +14,38 @@
14 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 49 CREATE TABLE IF NOT EXISTS resource (
18 50 id uuid NOT NULL CONSTRAINT resource_pkey PRIMARY KEY,
19 51 created_time bigint NOT NULL,
... ... @@ -64,3 +96,4 @@ DO $$
64 96 END IF;
65 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 46 import org.thingsboard.server.common.msg.queue.ServiceType;
47 47 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
48 48 import org.thingsboard.server.common.msg.tools.TbRateLimits;
  49 +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
49 50 import org.thingsboard.server.dao.asset.AssetService;
50 51 import org.thingsboard.server.dao.attributes.AttributesService;
51 52 import org.thingsboard.server.dao.audit.AuditLogService;
... ... @@ -54,6 +55,8 @@ import org.thingsboard.server.dao.customer.CustomerService;
54 55 import org.thingsboard.server.dao.dashboard.DashboardService;
55 56 import org.thingsboard.server.dao.device.ClaimDevicesService;
56 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 60 import org.thingsboard.server.dao.entityview.EntityViewService;
58 61 import org.thingsboard.server.dao.event.EventService;
59 62 import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor;
... ... @@ -69,7 +72,7 @@ import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
69 72 import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
70 73 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
71 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 76 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
74 77 import org.thingsboard.server.service.executors.ExternalCallExecutorService;
75 78 import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
... ... @@ -296,6 +299,18 @@ public class ActorSystemContext {
296 299 @Getter
297 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 314 @Value("${actors.session.max_concurrent_sessions_per_device:1}")
300 315 @Getter
301 316 private long maxConcurrentSessionsPerDevice;
... ... @@ -320,6 +335,9 @@ public class ActorSystemContext {
320 335 @Getter
321 336 private long statisticsPersistFrequency;
322 337
  338 + @Value("${edges.enabled}")
  339 + @Getter
  340 + private boolean edgesEnabled;
323 341
324 342 @Scheduled(fixedDelayString = "${actors.statistics.js_print_interval_ms}")
325 343 public void printStats() {
... ...
... ... @@ -35,10 +35,12 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
35 35 import org.thingsboard.server.common.msg.MsgType;
36 36 import org.thingsboard.server.common.msg.TbActorMsg;
37 37 import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
  38 +import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg;
38 39 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
39 40 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
40 41 import org.thingsboard.server.common.msg.queue.RuleEngineException;
41 42 import org.thingsboard.server.common.msg.queue.ServiceType;
  43 +import org.thingsboard.server.dao.model.ModelConstants;
42 44 import org.thingsboard.server.dao.tenant.TenantService;
43 45 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
44 46 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
... ... @@ -89,10 +91,15 @@ public class AppActor extends ContextAwareActor {
89 91 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
90 92 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
91 93 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
  94 + case DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG:
92 95 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
  96 + case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
93 97 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
94 98 onToDeviceActorMsg((TenantAwareMsg) msg, true);
95 99 break;
  100 + case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG:
  101 + onToTenantActorMsg((EdgeEventUpdateMsg) msg);
  102 + break;
96 103 default:
97 104 return false;
98 105 }
... ... @@ -192,6 +199,20 @@ public class AppActor extends ContextAwareActor {
192 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 216 public static class ActorCreator extends ContextBasedCreator {
196 217
197 218 public ActorCreator(ActorSystemContext context) {
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.actors.device;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
  20 +import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
20 21 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
21 22 import org.thingsboard.server.actors.ActorSystemContext;
22 23 import org.thingsboard.server.actors.TbActorCtx;
... ... @@ -26,6 +27,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
26 27 import org.thingsboard.server.common.data.id.TenantId;
27 28 import org.thingsboard.server.common.msg.TbActorMsg;
28 29 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
  30 +import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
29 31 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
30 32 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
31 33
... ... @@ -70,12 +72,18 @@ public class DeviceActor extends ContextAwareActor {
70 72 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
71 73 processor.processRpcRequest(ctx, (ToDeviceRpcRequestActorMsg) msg);
72 74 break;
  75 + case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  76 + processor.processRpcResponsesFromEdge(ctx, (FromDeviceRpcResponseActorMsg) msg);
  77 + break;
73 78 case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG:
74 79 processor.processServerSideRpcTimeout(ctx, (DeviceActorServerSideRpcTimeoutMsg) msg);
75 80 break;
76 81 case SESSION_TIMEOUT_MSG:
77 82 processor.checkSessionsTimeout();
78 83 break;
  84 + case DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG:
  85 + processor.processEdgeUpdate((DeviceEdgeUpdateMsg) msg);
  86 + break;
79 87 default:
80 88 return false;
81 89 }
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.actors.device;
17 17
  18 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 19 import com.google.common.util.concurrent.FutureCallback;
19 20 import com.google.common.util.concurrent.Futures;
20 21 import com.google.common.util.concurrent.ListenableFuture;
... ... @@ -24,6 +25,7 @@ import lombok.extern.slf4j.Slf4j;
24 25 import org.apache.commons.collections.CollectionUtils;
25 26 import org.thingsboard.rule.engine.api.RpcError;
26 27 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
  28 +import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
27 29 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
28 30 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
29 31 import org.thingsboard.server.actors.ActorSystemContext;
... ... @@ -31,11 +33,17 @@ import org.thingsboard.server.actors.TbActorCtx;
31 33 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
32 34 import org.thingsboard.server.common.data.DataConstants;
33 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 39 import org.thingsboard.server.common.data.id.DeviceId;
  40 +import org.thingsboard.server.common.data.id.EdgeId;
35 41 import org.thingsboard.server.common.data.id.TenantId;
36 42 import org.thingsboard.server.common.data.kv.AttributeKey;
37 43 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
38 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 47 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
40 48 import org.thingsboard.server.common.data.security.DeviceCredentials;
41 49 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
... ... @@ -68,6 +76,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceAct
68 76 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
69 77 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
70 78 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
  79 +import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
71 80 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
72 81 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
73 82
... ... @@ -103,6 +112,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
103 112 private String deviceName;
104 113 private String deviceType;
105 114 private TbMsgMetaData defaultMetaData;
  115 + private EdgeId edgeId;
106 116
107 117 DeviceActorMessageProcessor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {
108 118 super(systemContext);
... ... @@ -125,12 +135,32 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
125 135 this.defaultMetaData = new TbMsgMetaData();
126 136 this.defaultMetaData.putValue("deviceName", deviceName);
127 137 this.defaultMetaData.putValue("deviceType", deviceType);
  138 + if (systemContext.isEdgesEnabled()) {
  139 + this.edgeId = findRelatedEdgeId();
  140 + }
128 141 return true;
129 142 } else {
130 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 164 void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) {
135 165 ToDeviceRpcRequest request = msg.getMsg();
136 166 ToDeviceRpcRequestBody body = request.getBody();
... ... @@ -143,15 +173,22 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
143 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 193 if (request.isOneway() && sent) {
157 194 log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
... ... @@ -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 217 private void registerPendingRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {
170 218 toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));
171 219 DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout);
... ... @@ -498,6 +546,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
498 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 554 private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) {
502 555 ToTransportMsg msg = ToTransportMsg.newBuilder()
503 556 .setSessionIdMSB(sessionInfo.getSessionIdMSB())
... ... @@ -530,6 +583,36 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
530 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 617 private List<TsKvProto> toTsKvProtos(@Nullable List<AttributeKvEntry> result) {
535 618 List<TsKvProto> clientAttributes;
... ...
... ... @@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.TenantProfile;
42 42 import org.thingsboard.server.common.data.alarm.Alarm;
43 43 import org.thingsboard.server.common.data.asset.Asset;
44 44 import org.thingsboard.server.common.data.id.DeviceId;
  45 +import org.thingsboard.server.common.data.id.EdgeId;
45 46 import org.thingsboard.server.common.data.id.EntityId;
46 47 import org.thingsboard.server.common.data.id.RuleChainId;
47 48 import org.thingsboard.server.common.data.id.RuleNodeId;
... ... @@ -62,6 +63,8 @@ import org.thingsboard.server.dao.cassandra.CassandraCluster;
62 63 import org.thingsboard.server.dao.customer.CustomerService;
63 64 import org.thingsboard.server.dao.dashboard.DashboardService;
64 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 68 import org.thingsboard.server.dao.entityview.EntityViewService;
66 69 import org.thingsboard.server.dao.nosql.CassandraStatementTask;
67 70 import org.thingsboard.server.dao.nosql.TbResultSetFuture;
... ... @@ -319,6 +322,11 @@ class DefaultTbContext implements TbContext {
319 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 330 public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action) {
323 331 return entityActionMsg(entity, id, ruleNodeId, action, ServiceQueue.MAIN, null);
324 332 }
... ... @@ -478,6 +486,16 @@ class DefaultTbContext implements TbContext {
478 486 }
479 487
480 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 499 public EventLoopGroup getSharedEventLoop() {
482 500 return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup();
483 501 }
... ...
... ... @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
32 32 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
33 33 import org.thingsboard.server.common.data.relation.EntityRelation;
34 34 import org.thingsboard.server.common.data.rule.RuleChain;
  35 +import org.thingsboard.server.common.data.rule.RuleChainType;
35 36 import org.thingsboard.server.common.data.rule.RuleNode;
36 37 import org.thingsboard.server.common.msg.TbMsg;
37 38 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
... ... @@ -99,7 +100,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
99 100 public void start(TbActorCtx context) {
100 101 if (!started) {
101 102 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
102   - if (ruleChain != null) {
  103 + if (ruleChain != null && RuleChainType.CORE.equals(ruleChain.getType())) {
103 104 List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
104 105 log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
105 106 // Creating and starting the actors;
... ... @@ -119,7 +120,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
119 120 @Override
120 121 public void onUpdate(TbActorCtx context) {
121 122 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
122   - if (ruleChain != null) {
  123 + if (ruleChain != null && RuleChainType.CORE.equals(ruleChain.getType())) {
123 124 ruleChainName = ruleChain.getName();
124 125 List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
125 126 log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
... ...
... ... @@ -23,13 +23,13 @@ import org.thingsboard.server.actors.TbEntityActorId;
23 23 import org.thingsboard.server.actors.TbEntityTypeActorIdPredicate;
24 24 import org.thingsboard.server.actors.service.ContextAwareActor;
25 25 import org.thingsboard.server.actors.service.DefaultActorService;
26   -import org.thingsboard.server.actors.tenant.TenantActor;
27 26 import org.thingsboard.server.common.data.EntityType;
28 27 import org.thingsboard.server.common.data.id.EntityId;
29 28 import org.thingsboard.server.common.data.id.RuleChainId;
30 29 import org.thingsboard.server.common.data.id.TenantId;
31 30 import org.thingsboard.server.common.data.page.PageDataIterable;
32 31 import org.thingsboard.server.common.data.rule.RuleChain;
  32 +import org.thingsboard.server.common.data.rule.RuleChainType;
33 33 import org.thingsboard.server.common.msg.TbActorMsg;
34 34 import org.thingsboard.server.dao.rule.RuleChainService;
35 35
... ... @@ -55,7 +55,7 @@ public abstract class RuleChainManagerActor extends ContextAwareActor {
55 55 }
56 56
57 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 59 RuleChainId ruleChainId = ruleChain.getId();
60 60 log.debug("[{}|{}] Creating rule chain actor", ruleChainId.getEntityType(), ruleChain.getId());
61 61 TbActorRef actorRef = getOrCreateActor(ruleChainId, id -> ruleChain);
... ... @@ -65,13 +65,13 @@ public abstract class RuleChainManagerActor extends ContextAwareActor {
65 65 }
66 66
67 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 69 ctx.stop(new TbEntityActorId(ruleChain.getId()));
70 70 }
71 71 }
72 72
73 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 75 rootChain = entity;
76 76 rootChainActor = actorRef;
77 77 }
... ...
... ... @@ -33,21 +33,27 @@ import org.thingsboard.server.common.data.ApiUsageState;
33 33 import org.thingsboard.server.common.data.EntityType;
34 34 import org.thingsboard.server.common.data.Tenant;
35 35 import org.thingsboard.server.common.data.TenantProfile;
  36 +import org.thingsboard.server.common.data.edge.Edge;
36 37 import org.thingsboard.server.common.data.id.DeviceId;
  38 +import org.thingsboard.server.common.data.id.EdgeId;
37 39 import org.thingsboard.server.common.data.id.EntityId;
38 40 import org.thingsboard.server.common.data.id.RuleChainId;
39 41 import org.thingsboard.server.common.data.id.TenantId;
  42 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
40 43 import org.thingsboard.server.common.data.rule.RuleChain;
  44 +import org.thingsboard.server.common.data.rule.RuleChainType;
41 45 import org.thingsboard.server.common.msg.MsgType;
42 46 import org.thingsboard.server.common.msg.TbActorMsg;
43 47 import org.thingsboard.server.common.msg.TbMsg;
44 48 import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
45 49 import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
  50 +import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg;
46 51 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
47 52 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
48 53 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
49 54 import org.thingsboard.server.common.msg.queue.RuleEngineException;
50 55 import org.thingsboard.server.common.msg.queue.ServiceType;
  56 +import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
51 57 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
52 58
53 59 import java.util.List;
... ... @@ -155,13 +161,18 @@ public class TenantActor extends RuleChainManagerActor {
155 161 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
156 162 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
157 163 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
  164 + case DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG:
158 165 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
  166 + case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
159 167 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
160 168 onToDeviceActorMsg((DeviceAwareMsg) msg, true);
161 169 break;
162 170 case RULE_CHAIN_TO_RULE_CHAIN_MSG:
163 171 onRuleChainMsg((RuleChainAwareMsg) msg);
164 172 break;
  173 + case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG:
  174 + onToEdgeSessionMsg((EdgeEventUpdateMsg) msg);
  175 + break;
165 176 default:
166 177 return false;
167 178 }
... ... @@ -230,14 +241,26 @@ public class TenantActor extends RuleChainManagerActor {
230 241 log.info("[{}] Received API state update. Going to ENABLE Rule Engine execution.", tenantId);
231 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 256 TbActorRef target = getEntityActorRef(msg.getEntityId());
236 257 if (target != null) {
237 258 if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) {
238 259 RuleChain ruleChain = systemContext.getRuleChainService().
239 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 265 target.tellWithHighPriority(msg);
243 266 } else {
... ... @@ -252,6 +275,11 @@ public class TenantActor extends RuleChainManagerActor {
252 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 283 public static class ActorCreator extends ContextBasedCreator {
256 284
257 285 private final TenantId tenantId;
... ...
... ... @@ -71,7 +71,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
71 71 public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";
72 72 public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public";
73 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 75 public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";
76 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 21 @Controller
22 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 25 public String redirect() {
26 26 return "forward:/index.html";
27 27 }
... ...
... ... @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
34 34 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
35 35 import org.thingsboard.server.common.data.alarm.AlarmStatus;
36 36 import org.thingsboard.server.common.data.audit.ActionType;
  37 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
37 38 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
38 39 import org.thingsboard.server.common.data.exception.ThingsboardException;
39 40 import org.thingsboard.server.common.data.id.AlarmId;
... ... @@ -45,8 +46,6 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
45 46 import org.thingsboard.server.service.security.permission.Operation;
46 47 import org.thingsboard.server.service.security.permission.Resource;
47 48
48   -import java.util.UUID;
49   -
50 49 @RestController
51 50 @TbCoreComponent
52 51 @RequestMapping("/api")
... ... @@ -93,6 +92,9 @@ public class AlarmController extends BaseController {
93 92 logEntityAction(savedAlarm.getOriginator(), savedAlarm,
94 93 getCurrentUser().getCustomerId(),
95 94 alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
  95 +
  96 + sendEntityNotificationMsg(getTenantId(), savedAlarm.getId(), alarm.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
  97 +
96 98 return savedAlarm;
97 99 } catch (Exception e) {
98 100 logEntityAction(emptyId(EntityType.ALARM), alarm,
... ... @@ -109,8 +111,11 @@ public class AlarmController extends BaseController {
109 111 try {
110 112 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
111 113 checkAlarmId(alarmId, Operation.WRITE);
  114 +
  115 + sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.DELETED);
  116 +
112 117 return alarmService.deleteAlarm(getTenantId(), alarmId);
113   - } catch (Exception e) {
  118 + } catch (Exception e) {
114 119 throw handleException(e);
115 120 }
116 121 }
... ... @@ -128,6 +133,8 @@ public class AlarmController extends BaseController {
128 133 alarm.setAckTs(ackTs);
129 134 alarm.setStatus(alarm.getStatus().isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK);
130 135 logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null);
  136 +
  137 + sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.ALARM_ACK);
131 138 } catch (Exception e) {
132 139 throw handleException(e);
133 140 }
... ... @@ -146,6 +153,8 @@ public class AlarmController extends BaseController {
146 153 alarm.setClearTs(clearTs);
147 154 alarm.setStatus(alarm.getStatus().isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK);
148 155 logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null);
  156 +
  157 + sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.ALARM_CLEAR);
149 158 } catch (Exception e) {
150 159 throw handleException(e);
151 160 }
... ... @@ -166,7 +175,6 @@ public class AlarmController extends BaseController {
166 175 @RequestParam(required = false) String sortOrder,
167 176 @RequestParam(required = false) Long startTime,
168 177 @RequestParam(required = false) Long endTime,
169   - @RequestParam(required = false) String offset,
170 178 @RequestParam(required = false) Boolean fetchOriginator
171 179 ) throws ThingsboardException {
172 180 checkParameter("EntityId", strEntityId);
... ... @@ -180,12 +188,9 @@ public class AlarmController extends BaseController {
180 188 }
181 189 checkEntityId(entityId, Operation.READ);
182 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 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 194 } catch (Exception e) {
190 195 throw handleException(e);
191 196 }
... ...
... ... @@ -33,14 +33,18 @@ import org.thingsboard.server.common.data.asset.Asset;
33 33 import org.thingsboard.server.common.data.asset.AssetInfo;
34 34 import org.thingsboard.server.common.data.asset.AssetSearchQuery;
35 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 38 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
  39 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
37 40 import org.thingsboard.server.common.data.exception.ThingsboardException;
38 41 import org.thingsboard.server.common.data.id.AssetId;
39 42 import org.thingsboard.server.common.data.id.CustomerId;
  43 +import org.thingsboard.server.common.data.id.EdgeId;
40 44 import org.thingsboard.server.common.data.id.TenantId;
41 45 import org.thingsboard.server.common.data.page.PageData;
42 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 48 import org.thingsboard.server.dao.exception.IncorrectParameterException;
45 49 import org.thingsboard.server.dao.model.ModelConstants;
46 50 import org.thingsboard.server.queue.util.TbCoreComponent;
... ... @@ -54,6 +58,8 @@ import java.util.stream.Collectors;
54 58
55 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 63 @RestController
58 64 @TbCoreComponent
59 65 @RequestMapping("/api")
... ... @@ -98,7 +104,7 @@ public class AssetController extends BaseController {
98 104
99 105 asset.setTenantId(getCurrentUser().getTenantId());
100 106
101   - checkEntity(asset.getId(), asset, Resource.ASSET);
  107 + checkEntity(asset.getId(), asset, Resource.ASSET);
102 108
103 109 Asset savedAsset = checkNotNull(assetService.saveAsset(asset));
104 110
... ... @@ -106,6 +112,10 @@ public class AssetController extends BaseController {
106 112 savedAsset.getCustomerId(),
107 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 119 return savedAsset;
110 120 } catch (Exception e) {
111 121 logEntityAction(emptyId(EntityType.ASSET), asset,
... ... @@ -122,12 +132,16 @@ public class AssetController extends BaseController {
122 132 try {
123 133 AssetId assetId = new AssetId(toUUID(strAssetId));
124 134 Asset asset = checkAssetId(assetId, Operation.DELETE);
  135 +
  136 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), assetId);
  137 +
125 138 assetService.deleteAsset(getTenantId(), assetId);
126 139
127 140 logEntityAction(assetId, asset,
128 141 asset.getCustomerId(),
129 142 ActionType.DELETED, null, strAssetId);
130 143
  144 + sendDeleteNotificationMsg(getTenantId(), assetId, relatedEdgeIds);
131 145 } catch (Exception e) {
132 146 logEntityAction(emptyId(EntityType.ASSET),
133 147 null,
... ... @@ -157,6 +171,9 @@ public class AssetController extends BaseController {
157 171 savedAsset.getCustomerId(),
158 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 177 return savedAsset;
161 178 } catch (Exception e) {
162 179
... ... @@ -188,6 +205,9 @@ public class AssetController extends BaseController {
188 205 asset.getCustomerId(),
189 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 211 return savedAsset;
192 212 } catch (Exception e) {
193 213
... ... @@ -401,4 +421,113 @@ public class AssetController extends BaseController {
401 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 37 import org.thingsboard.rule.engine.api.MailService;
38 38 import org.thingsboard.server.common.data.User;
39 39 import org.thingsboard.server.common.data.audit.ActionType;
  40 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
40 41 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
41 42 import org.thingsboard.server.common.data.exception.ThingsboardException;
42 43 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -110,6 +111,8 @@ public class AuthController extends BaseController {
110 111 userCredentials.setPassword(passwordEncoder.encode(newPassword));
111 112 userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials);
112 113
  114 + sendEntityNotificationMsg(getTenantId(), userCredentials.getUserId(), EdgeEventActionType.CREDENTIALS_UPDATED);
  115 +
113 116 eventPublisher.publishEvent(new UserAuthDataChangedEvent(securityUser.getId()));
114 117 ObjectNode response = JacksonUtil.newObjectNode();
115 118 response.put("token", tokenFactory.createAccessJwtToken(securityUser).getToken());
... ... @@ -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 232 JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
228 233 JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
229 234
... ...
... ... @@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.DataConstants;
35 35 import org.thingsboard.server.common.data.Device;
36 36 import org.thingsboard.server.common.data.DeviceInfo;
37 37 import org.thingsboard.server.common.data.DeviceProfile;
  38 +import org.thingsboard.server.common.data.EdgeUtils;
38 39 import org.thingsboard.server.common.data.EntityType;
39 40 import org.thingsboard.server.common.data.EntityView;
40 41 import org.thingsboard.server.common.data.EntityViewInfo;
... ... @@ -53,6 +54,10 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo;
53 54 import org.thingsboard.server.common.data.asset.Asset;
54 55 import org.thingsboard.server.common.data.asset.AssetInfo;
55 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 61 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
57 62 import org.thingsboard.server.common.data.exception.ThingsboardException;
58 63 import org.thingsboard.server.common.data.id.AlarmId;
... ... @@ -61,6 +66,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
61 66 import org.thingsboard.server.common.data.id.DashboardId;
62 67 import org.thingsboard.server.common.data.id.DeviceId;
63 68 import org.thingsboard.server.common.data.id.DeviceProfileId;
  69 +import org.thingsboard.server.common.data.id.EdgeId;
64 70 import org.thingsboard.server.common.data.id.EntityId;
65 71 import org.thingsboard.server.common.data.id.EntityIdFactory;
66 72 import org.thingsboard.server.common.data.id.EntityViewId;
... ... @@ -82,7 +88,9 @@ import org.thingsboard.server.common.data.page.SortOrder;
82 88 import org.thingsboard.server.common.data.page.TimePageLink;
83 89 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
84 90 import org.thingsboard.server.common.data.plugin.ComponentType;
  91 +import org.thingsboard.server.common.data.relation.EntityRelation;
85 92 import org.thingsboard.server.common.data.rule.RuleChain;
  93 +import org.thingsboard.server.common.data.rule.RuleChainType;
86 94 import org.thingsboard.server.common.data.rule.RuleNode;
87 95 import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
88 96 import org.thingsboard.server.common.data.widget.WidgetsBundle;
... ... @@ -98,6 +106,7 @@ import org.thingsboard.server.dao.device.ClaimDevicesService;
98 106 import org.thingsboard.server.dao.device.DeviceCredentialsService;
99 107 import org.thingsboard.server.dao.device.DeviceProfileService;
100 108 import org.thingsboard.server.dao.device.DeviceService;
  109 +import org.thingsboard.server.dao.edge.EdgeService;
101 110 import org.thingsboard.server.dao.entityview.EntityViewService;
102 111 import org.thingsboard.server.dao.exception.DataValidationException;
103 112 import org.thingsboard.server.dao.exception.IncorrectParameterException;
... ... @@ -106,7 +115,6 @@ import org.thingsboard.server.dao.model.ModelConstants;
106 115 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
107 116 import org.thingsboard.server.dao.oauth2.OAuth2Service;
108 117 import org.thingsboard.server.dao.relation.RelationService;
109   -import org.thingsboard.server.dao.resource.TbResourceService;
110 118 import org.thingsboard.server.dao.rule.RuleChainService;
111 119 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
112 120 import org.thingsboard.server.dao.tenant.TenantProfileService;
... ... @@ -115,14 +123,19 @@ import org.thingsboard.server.dao.user.UserService;
115 123 import org.thingsboard.server.dao.widget.WidgetTypeService;
116 124 import org.thingsboard.server.dao.widget.WidgetsBundleService;
117 125 import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
  126 +import org.thingsboard.server.gen.transport.TransportProtos;
118 127 import org.thingsboard.server.queue.discovery.PartitionService;
119 128 import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
120 129 import org.thingsboard.server.queue.util.TbCoreComponent;
121 130 import org.thingsboard.server.service.component.ComponentDiscoveryService;
122 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 135 import org.thingsboard.server.service.lwm2m.LwM2MModelsRepository;
124 136 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
125 137 import org.thingsboard.server.service.queue.TbClusterService;
  138 +import org.thingsboard.server.service.resource.TbResourceService;
126 139 import org.thingsboard.server.service.security.model.SecurityUser;
127 140 import org.thingsboard.server.service.security.permission.AccessControlService;
128 141 import org.thingsboard.server.service.security.permission.Operation;
... ... @@ -256,10 +269,25 @@ public abstract class BaseController {
256 269 @Autowired
257 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 284 @Value("${server.log_controller_error_stack_trace}")
260 285 @Getter
261 286 private boolean logControllerErrorStackTrace;
262 287
  288 + @Value("${edges.enabled}")
  289 + @Getter
  290 + protected boolean edgesEnabled;
263 291
264 292 @ExceptionHandler(ThingsboardException.class)
265 293 public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) {
... ... @@ -473,6 +501,9 @@ public abstract class BaseController {
473 501 case ENTITY_VIEW:
474 502 checkEntityViewId(new EntityViewId(entityId.getId()), operation);
475 503 return;
  504 + case EDGE:
  505 + checkEdgeId(new EdgeId(entityId.getId()), operation);
  506 + return;
476 507 case WIDGETS_BUNDLE:
477 508 checkWidgetsBundleId(new WidgetsBundleId(entityId.getId()), operation);
478 509 return;
... ... @@ -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 695 DashboardInfo checkDashboardInfoId(DashboardId dashboardId, Operation operation) throws ThingsboardException {
641 696 try {
642 697 validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
... ... @@ -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 717 try {
663 718 log.debug("[{}] Lookup component descriptors", type);
664   - return componentDescriptorService.getComponents(type);
  719 + return componentDescriptorService.getComponents(type, ruleChainType);
665 720 } catch (Exception e) {
666 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 726 try {
672 727 log.debug("[{}] Lookup component descriptors", types);
673   - return componentDescriptorService.getComponents(types);
  728 + return componentDescriptorService.getComponents(types, ruleChainType);
674 729 } catch (Exception e) {
675 730 throw handleException(e, false);
676 731 }
... ... @@ -815,6 +870,12 @@ public abstract class BaseController {
815 870 case TIMESERIES_DELETED:
816 871 msgType = DataConstants.TIMESERIES_DELETED;
817 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 880 if (!StringUtils.isEmpty(msgType)) {
820 881 try {
... ... @@ -844,6 +905,16 @@ public abstract class BaseController {
844 905 String strTenantName = extractParameter(String.class, 1, additionalInfo);
845 906 metaData.putValue("assignedToTenantId", strTenantId);
846 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 919 ObjectNode entityNode;
849 920 if (entity != null) {
... ... @@ -937,6 +1008,93 @@ public abstract class BaseController {
937 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 1098 private void addTimeseries(ObjectNode entityNode, List<TsKvEntry> timeseries) throws Exception {
941 1099 if (timeseries != null && !timeseries.isEmpty()) {
942 1100 ArrayNode result = entityNode.putArray("timeseries");
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.controller;
17 17
  18 +import org.apache.commons.lang3.StringUtils;
18 19 import org.springframework.security.access.prepost.PreAuthorize;
19 20 import org.springframework.web.bind.annotation.PathVariable;
20 21 import org.springframework.web.bind.annotation.RequestMapping;
... ... @@ -25,6 +26,7 @@ import org.springframework.web.bind.annotation.RestController;
25 26 import org.thingsboard.server.common.data.exception.ThingsboardException;
26 27 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
27 28 import org.thingsboard.server.common.data.plugin.ComponentType;
  29 +import org.thingsboard.server.common.data.rule.RuleChainType;
28 30 import org.thingsboard.server.queue.util.TbCoreComponent;
29 31
30 32 import java.util.HashSet;
... ... @@ -51,10 +53,11 @@ public class ComponentDescriptorController extends BaseController {
51 53 @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
52 54 @RequestMapping(value = "/components/{componentType}", method = RequestMethod.GET)
53 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 58 checkParameter("componentType", strComponentType);
56 59 try {
57   - return checkComponentDescriptorsByType(ComponentType.valueOf(strComponentType));
  60 + return checkComponentDescriptorsByType(ComponentType.valueOf(strComponentType), getRuleChainType(strRuleChainType));
58 61 } catch (Exception e) {
59 62 throw handleException(e);
60 63 }
... ... @@ -63,17 +66,28 @@ public class ComponentDescriptorController extends BaseController {
63 66 @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
64 67 @RequestMapping(value = "/components", params = {"componentTypes"}, method = RequestMethod.GET)
65 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 71 checkArrayParameter("componentTypes", strComponentTypes);
68 72 try {
69 73 Set<ComponentType> componentTypes = new HashSet<>();
70 74 for (String strComponentType : strComponentTypes) {
71 75 componentTypes.add(ComponentType.valueOf(strComponentType));
72 76 }
73   - return checkComponentDescriptorsByTypes(componentTypes);
  77 + return checkComponentDescriptorsByTypes(componentTypes, getRuleChainType(strRuleChainType));
74 78 } catch (Exception e) {
75 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 31 import org.thingsboard.server.common.data.Customer;
32 32 import org.thingsboard.server.common.data.EntityType;
33 33 import org.thingsboard.server.common.data.audit.ActionType;
  34 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
34 35 import org.thingsboard.server.common.data.exception.ThingsboardException;
35 36 import org.thingsboard.server.common.data.id.CustomerId;
  37 +import org.thingsboard.server.common.data.id.EdgeId;
36 38 import org.thingsboard.server.common.data.id.TenantId;
37 39 import org.thingsboard.server.common.data.page.PageData;
38 40 import org.thingsboard.server.common.data.page.PageLink;
... ... @@ -40,6 +42,8 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
40 42 import org.thingsboard.server.service.security.permission.Operation;
41 43 import org.thingsboard.server.service.security.permission.Resource;
42 44
  45 +import java.util.List;
  46 +
43 47 @RestController
44 48 @TbCoreComponent
45 49 @RequestMapping("/api")
... ... @@ -112,6 +116,10 @@ public class CustomerController extends BaseController {
112 116 savedCustomer.getId(),
113 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 123 return savedCustomer;
116 124 } catch (Exception e) {
117 125
... ... @@ -130,12 +138,16 @@ public class CustomerController extends BaseController {
130 138 try {
131 139 CustomerId customerId = new CustomerId(toUUID(strCustomerId));
132 140 Customer customer = checkCustomerId(customerId, Operation.DELETE);
  141 +
  142 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), customerId);
  143 +
133 144 customerService.deleteCustomer(getTenantId(), customerId);
134 145
135 146 logEntityAction(customerId, customer,
136 147 customer.getId(),
137 148 ActionType.DELETED, null, strCustomerId);
138 149
  150 + sendDeleteNotificationMsg(getTenantId(), customerId, relatedEdgeIds);
139 151 } catch (Exception e) {
140 152
141 153 logEntityAction(emptyId(EntityType.CUSTOMER),
... ...
... ... @@ -38,20 +38,26 @@ import org.thingsboard.server.common.data.ShortCustomerInfo;
38 38 import org.thingsboard.server.common.data.Tenant;
39 39 import org.thingsboard.server.common.data.User;
40 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 43 import org.thingsboard.server.common.data.exception.ThingsboardException;
42 44 import org.thingsboard.server.common.data.id.CustomerId;
43 45 import org.thingsboard.server.common.data.id.DashboardId;
  46 +import org.thingsboard.server.common.data.id.EdgeId;
44 47 import org.thingsboard.server.common.data.id.TenantId;
45 48 import org.thingsboard.server.common.data.page.PageData;
46 49 import org.thingsboard.server.common.data.page.PageLink;
47 50 import org.thingsboard.common.util.JacksonUtil;
  51 +import org.thingsboard.server.common.data.page.TimePageLink;
48 52 import org.thingsboard.server.queue.util.TbCoreComponent;
49 53 import org.thingsboard.server.service.security.model.SecurityUser;
50 54 import org.thingsboard.server.service.security.permission.Operation;
51 55 import org.thingsboard.server.service.security.permission.Resource;
52 56
53 57 import java.util.HashSet;
  58 +import java.util.List;
54 59 import java.util.Set;
  60 +import java.util.stream.Collectors;
55 61
56 62 @RestController
57 63 @TbCoreComponent
... ... @@ -122,6 +128,10 @@ public class DashboardController extends BaseController {
122 128 null,
123 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 135 return savedDashboard;
126 136 } catch (Exception e) {
127 137 logEntityAction(emptyId(EntityType.DASHBOARD), dashboard,
... ... @@ -139,12 +149,16 @@ public class DashboardController extends BaseController {
139 149 try {
140 150 DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
141 151 Dashboard dashboard = checkDashboardId(dashboardId, Operation.DELETE);
  152 +
  153 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), dashboardId);
  154 +
142 155 dashboardService.deleteDashboard(getCurrentUser().getTenantId(), dashboardId);
143 156
144 157 logEntityAction(dashboardId, dashboard,
145 158 null,
146 159 ActionType.DELETED, null, strDashboardId);
147 160
  161 + sendDeleteNotificationMsg(getTenantId(), dashboardId, relatedEdgeIds);
148 162 } catch (Exception e) {
149 163
150 164 logEntityAction(emptyId(EntityType.DASHBOARD),
... ... @@ -176,6 +190,7 @@ public class DashboardController extends BaseController {
176 190 customerId,
177 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 195 return savedDashboard;
181 196 } catch (Exception e) {
... ... @@ -207,6 +222,8 @@ public class DashboardController extends BaseController {
207 222 customerId,
208 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 227 return savedDashboard;
211 228 } catch (Exception e) {
212 229
... ... @@ -262,6 +279,7 @@ public class DashboardController extends BaseController {
262 279 logEntityAction(dashboardId, savedDashboard,
263 280 customerId,
264 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 284 for (CustomerId customerId : removedCustomerIds) {
267 285 ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
... ... @@ -269,7 +287,7 @@ public class DashboardController extends BaseController {
269 287 logEntityAction(dashboardId, dashboard,
270 288 customerId,
271 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 292 return savedDashboard;
275 293 }
... ... @@ -313,6 +331,7 @@ public class DashboardController extends BaseController {
313 331 logEntityAction(dashboardId, savedDashboard,
314 332 customerId,
315 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 336 return savedDashboard;
318 337 }
... ... @@ -356,7 +375,7 @@ public class DashboardController extends BaseController {
356 375 logEntityAction(dashboardId, dashboard,
357 376 customerId,
358 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 380 return savedDashboard;
362 381 }
... ... @@ -578,4 +597,106 @@ public class DashboardController extends BaseController {
578 597 } catch (Exception e) {}
579 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 32 import org.springframework.web.bind.annotation.RestController;
33 33 import org.springframework.web.context.request.async.DeferredResult;
34 34 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
  35 +import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
35 36 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
36 37 import org.thingsboard.server.common.data.ClaimRequest;
37 38 import org.thingsboard.server.common.data.Customer;
... ... @@ -43,14 +44,18 @@ import org.thingsboard.server.common.data.EntityType;
43 44 import org.thingsboard.server.common.data.Tenant;
44 45 import org.thingsboard.server.common.data.audit.ActionType;
45 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 49 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
47 50 import org.thingsboard.server.common.data.exception.ThingsboardException;
48 51 import org.thingsboard.server.common.data.id.CustomerId;
49 52 import org.thingsboard.server.common.data.id.DeviceId;
50 53 import org.thingsboard.server.common.data.id.DeviceProfileId;
  54 +import org.thingsboard.server.common.data.id.EdgeId;
51 55 import org.thingsboard.server.common.data.id.TenantId;
52 56 import org.thingsboard.server.common.data.page.PageData;
53 57 import org.thingsboard.server.common.data.page.PageLink;
  58 +import org.thingsboard.server.common.data.page.TimePageLink;
54 59 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
55 60 import org.thingsboard.server.common.data.security.DeviceCredentials;
56 61 import org.thingsboard.server.common.msg.TbMsg;
... ... @@ -73,6 +78,8 @@ import java.util.List;
73 78 import java.util.Objects;
74 79 import java.util.stream.Collectors;
75 80
  81 +import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
  82 +
76 83 @RestController
77 84 @TbCoreComponent
78 85 @RequestMapping("/api")
... ... @@ -138,6 +145,10 @@ public class DeviceController extends BaseController {
138 145 savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null);
139 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 152 logEntityAction(savedDevice.getId(), savedDevice,
142 153 savedDevice.getCustomerId(),
143 154 device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
... ... @@ -170,6 +181,9 @@ public class DeviceController extends BaseController {
170 181 try {
171 182 DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
172 183 Device device = checkDeviceId(deviceId, Operation.DELETE);
  184 +
  185 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), deviceId);
  186 +
173 187 deviceService.deleteDevice(getCurrentUser().getTenantId(), deviceId);
174 188
175 189 tbClusterService.onDeviceDeleted(device, null);
... ... @@ -179,6 +193,8 @@ public class DeviceController extends BaseController {
179 193 device.getCustomerId(),
180 194 ActionType.DELETED, null, strDeviceId);
181 195
  196 + sendDeleteNotificationMsg(getTenantId(), deviceId, relatedEdgeIds);
  197 +
182 198 deviceStateService.onDeviceDeleted(device);
183 199 } catch (Exception e) {
184 200 logEntityAction(emptyId(EntityType.DEVICE),
... ... @@ -209,6 +225,9 @@ public class DeviceController extends BaseController {
209 225 savedDevice.getCustomerId(),
210 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 231 return savedDevice;
213 232 } catch (Exception e) {
214 233 logEntityAction(emptyId(EntityType.DEVICE), null,
... ... @@ -237,6 +256,9 @@ public class DeviceController extends BaseController {
237 256 device.getCustomerId(),
238 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 262 return savedDevice;
241 263 } catch (Exception e) {
242 264 logEntityAction(emptyId(EntityType.DEVICE), null,
... ... @@ -300,6 +322,9 @@ public class DeviceController extends BaseController {
300 322 Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS);
301 323 DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials));
302 324 tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId(), result), null);
  325 +
  326 + sendEntityNotificationMsg(getTenantId(), device.getId(), EdgeEventActionType.CREDENTIALS_UPDATED);
  327 +
303 328 logEntityAction(device.getId(), device,
304 329 device.getCustomerId(),
305 330 ActionType.CREDENTIALS_UPDATED, null, deviceCredentials);
... ... @@ -651,4 +676,115 @@ public class DeviceController extends BaseController {
651 676 metaData.putValue("assignedFromTenantName", tenant.getName());
652 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 32 import org.thingsboard.server.common.data.DeviceProfileInfo;
33 33 import org.thingsboard.server.common.data.EntityType;
34 34 import org.thingsboard.server.common.data.audit.ActionType;
  35 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
35 36 import org.thingsboard.server.common.data.exception.ThingsboardException;
36 37 import org.thingsboard.server.common.data.id.DeviceProfileId;
37 38 import org.thingsboard.server.common.data.page.PageData;
... ... @@ -166,7 +167,8 @@ public class DeviceProfileController extends BaseController {
166 167 if (isFirmwareChanged) {
167 168 firmwareStateService.update(savedDeviceProfile);
168 169 }
169   -
  170 + sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(),
  171 + deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
170 172 return savedDeviceProfile;
171 173 } catch (Exception e) {
172 174 logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile,
... ... @@ -192,6 +194,7 @@ public class DeviceProfileController extends BaseController {
192 194 null,
193 195 ActionType.DELETED, null, strDeviceProfileId);
194 196
  197 + sendEntityNotificationMsg(getTenantId(), deviceProfile.getId(), EdgeEventActionType.DELETED);
195 198 } catch (Exception e) {
196 199 logEntityAction(emptyId(EntityType.DEVICE_PROFILE),
197 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 25 import org.springframework.web.bind.annotation.ResponseStatus;
26 26 import org.springframework.web.bind.annotation.RestController;
27 27 import org.thingsboard.server.common.data.audit.ActionType;
  28 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
28 29 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
29 30 import org.thingsboard.server.common.data.exception.ThingsboardException;
30 31 import org.thingsboard.server.common.data.id.EntityId;
... ... @@ -63,10 +64,13 @@ public class EntityRelationController extends BaseController {
63 64 relation.setTypeGroup(RelationTypeGroup.COMMON);
64 65 }
65 66 relationService.saveRelation(getTenantId(), relation);
  67 +
66 68 logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
67 69 ActionType.RELATION_ADD_OR_UPDATE, null, relation);
68 70 logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
69 71 ActionType.RELATION_ADD_OR_UPDATE, null, relation);
  72 +
  73 + sendRelationNotificationMsg(getTenantId(), relation, EdgeEventActionType.RELATION_ADD_OR_UPDATE);
70 74 } catch (Exception e) {
71 75 logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
72 76 ActionType.RELATION_ADD_OR_UPDATE, e, relation);
... ... @@ -104,6 +108,8 @@ public class EntityRelationController extends BaseController {
104 108 ActionType.RELATION_DELETED, null, relation);
105 109 logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
106 110 ActionType.RELATION_DELETED, null, relation);
  111 +
  112 + sendRelationNotificationMsg(getTenantId(), relation, EdgeEventActionType.RELATION_DELETED);
107 113 } catch (Exception e) {
108 114 logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
109 115 ActionType.RELATION_DELETED, e, relation);
... ...
... ... @@ -32,11 +32,20 @@ import org.springframework.web.bind.annotation.RequestParam;
32 32 import org.springframework.web.bind.annotation.ResponseBody;
33 33 import org.springframework.web.bind.annotation.ResponseStatus;
34 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 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 45 import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
38 46 import org.thingsboard.server.common.data.exception.ThingsboardException;
39 47 import org.thingsboard.server.common.data.id.CustomerId;
  48 +import org.thingsboard.server.common.data.id.EdgeId;
40 49 import org.thingsboard.server.common.data.id.EntityId;
41 50 import org.thingsboard.server.common.data.id.EntityViewId;
42 51 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -47,6 +56,7 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
47 56 import org.thingsboard.server.common.data.kv.TsKvEntry;
48 57 import org.thingsboard.server.common.data.page.PageData;
49 58 import org.thingsboard.server.common.data.page.PageLink;
  59 +import org.thingsboard.server.common.data.page.TimePageLink;
50 60 import org.thingsboard.server.dao.exception.IncorrectParameterException;
51 61 import org.thingsboard.server.dao.model.ModelConstants;
52 62 import org.thingsboard.server.dao.timeseries.TimeseriesService;
... ... @@ -65,6 +75,7 @@ import java.util.stream.Collectors;
65 75
66 76 import static org.apache.commons.lang3.StringUtils.isBlank;
67 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 81 * Created by Victor Basanets on 8/28/2017.
... ... @@ -150,6 +161,11 @@ public class EntityViewController extends BaseController {
150 161
151 162 logEntityAction(savedEntityView.getId(), savedEntityView, null,
152 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 169 return savedEntityView;
154 170 } catch (Exception e) {
155 171 logEntityAction(emptyId(EntityType.ENTITY_VIEW), entityView, null,
... ... @@ -243,7 +259,7 @@ public class EntityViewController extends BaseController {
243 259 private ListenableFuture<List<Void>> copyLatestFromEntityToEntityView(EntityView entityView, SecurityUser user) {
244 260 EntityViewId entityId = entityView.getId();
245 261 List<String> keys = entityView.getKeys() != null && entityView.getKeys().getTimeseries() != null ?
246   - entityView.getKeys().getTimeseries() : Collections.emptyList();
  262 + entityView.getKeys().getTimeseries() : Collections.emptyList();
247 263 long startTs = entityView.getStartTimeMs();
248 264 long endTs = entityView.getEndTimeMs() == 0 ? Long.MAX_VALUE : entityView.getEndTimeMs();
249 265 ListenableFuture<List<String>> keysFuture;
... ... @@ -345,9 +361,14 @@ public class EntityViewController extends BaseController {
345 361 try {
346 362 EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
347 363 EntityView entityView = checkEntityViewId(entityViewId, Operation.DELETE);
  364 +
  365 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), entityViewId);
  366 +
348 367 entityViewService.deleteEntityView(getTenantId(), entityViewId);
349 368 logEntityAction(entityViewId, entityView, entityView.getCustomerId(),
350 369 ActionType.DELETED, null, strEntityViewId);
  370 +
  371 + sendDeleteNotificationMsg(getTenantId(), entityViewId, relatedEdgeIds);
351 372 } catch (Exception e) {
352 373 logEntityAction(emptyId(EntityType.ENTITY_VIEW),
353 374 null,
... ... @@ -388,6 +409,10 @@ public class EntityViewController extends BaseController {
388 409 logEntityAction(entityViewId, savedEntityView,
389 410 savedEntityView.getCustomerId(),
390 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 416 return savedEntityView;
392 417 } catch (Exception e) {
393 418 logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
... ... @@ -414,6 +439,9 @@ public class EntityViewController extends BaseController {
414 439 entityView.getCustomerId(),
415 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 445 return savedEntityView;
418 446 } catch (Exception e) {
419 447 logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
... ... @@ -585,4 +613,107 @@ public class EntityViewController extends BaseController {
585 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 18 import org.springframework.beans.factory.annotation.Autowired;
19 19 import org.springframework.security.access.prepost.PreAuthorize;
20 20 import org.springframework.web.bind.annotation.PathVariable;
  21 +import org.springframework.web.bind.annotation.RequestBody;
21 22 import org.springframework.web.bind.annotation.RequestMapping;
22 23 import org.springframework.web.bind.annotation.RequestMethod;
23 24 import org.springframework.web.bind.annotation.RequestParam;
24 25 import org.springframework.web.bind.annotation.ResponseBody;
25 26 import org.springframework.web.bind.annotation.RestController;
26 27 import org.thingsboard.server.common.data.Event;
  28 +import org.thingsboard.server.common.data.event.EventFilter;
27 29 import org.thingsboard.server.common.data.exception.ThingsboardException;
28 30 import org.thingsboard.server.common.data.id.EntityId;
29 31 import org.thingsboard.server.common.data.id.EntityIdFactory;
... ... @@ -31,6 +33,7 @@ import org.thingsboard.server.common.data.id.TenantId;
31 33 import org.thingsboard.server.common.data.page.PageData;
32 34 import org.thingsboard.server.common.data.page.TimePageLink;
33 35 import org.thingsboard.server.dao.event.EventService;
  36 +import org.thingsboard.server.dao.model.ModelConstants;
34 37 import org.thingsboard.server.queue.util.TbCoreComponent;
35 38 import org.thingsboard.server.service.security.permission.Operation;
36 39
... ... @@ -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 42 import org.thingsboard.server.common.data.EntityType;
43 43 import org.thingsboard.server.common.data.Event;
44 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 47 import org.thingsboard.server.common.data.exception.ThingsboardException;
  48 +import org.thingsboard.server.common.data.id.EdgeId;
46 49 import org.thingsboard.server.common.data.id.RuleChainId;
47 50 import org.thingsboard.server.common.data.id.RuleNodeId;
48 51 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -54,6 +57,7 @@ import org.thingsboard.server.common.data.rule.RuleChain;
54 57 import org.thingsboard.server.common.data.rule.RuleChainData;
55 58 import org.thingsboard.server.common.data.rule.RuleChainImportResult;
56 59 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
  60 +import org.thingsboard.server.common.data.rule.RuleChainType;
57 61 import org.thingsboard.server.common.data.rule.RuleNode;
58 62 import org.thingsboard.server.common.msg.TbMsg;
59 63 import org.thingsboard.server.common.msg.TbMsgDataType;
... ... @@ -138,13 +142,21 @@ public class RuleChainController extends BaseController {
138 142
139 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 150 logEntityAction(savedRuleChain.getId(), savedRuleChain,
145 151 null,
146 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 160 return savedRuleChain;
149 161 } catch (Exception e) {
150 162
... ... @@ -232,14 +244,22 @@ public class RuleChainController extends BaseController {
232 244 }
233 245
234 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 254 logEntityAction(ruleChain.getId(), ruleChain,
240 255 null,
241 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 263 return savedRuleChainMetaData;
244 264 } catch (Exception e) {
245 265
... ... @@ -256,13 +276,18 @@ public class RuleChainController extends BaseController {
256 276 public PageData<RuleChain> getRuleChains(
257 277 @RequestParam int pageSize,
258 278 @RequestParam int page,
  279 + @RequestParam(value = "type", required = false) String typeStr,
259 280 @RequestParam(required = false) String textSearch,
260 281 @RequestParam(required = false) String sortProperty,
261 282 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
262 283 try {
263 284 TenantId tenantId = getCurrentUser().getTenantId();
264 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 291 } catch (Exception e) {
267 292 throw handleException(e);
268 293 }
... ... @@ -281,19 +306,30 @@ public class RuleChainController extends BaseController {
281 306
282 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 314 ruleChainService.deleteRuleChainById(getTenantId(), ruleChainId);
285 315
286 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 325 logEntityAction(ruleChainId, ruleChain,
294 326 null,
295 327 ActionType.DELETED, null, strRuleChainId);
296 328
  329 + if (RuleChainType.EDGE.equals(ruleChain.getType())) {
  330 + sendDeleteNotificationMsg(ruleChain.getTenantId(), ruleChain.getId(), relatedEdgeIds);
  331 + }
  332 +
297 333 } catch (Exception e) {
298 334 logEntityAction(emptyId(EntityType.RULE_CHAIN),
299 335 null,
... ... @@ -411,7 +447,7 @@ public class RuleChainController extends BaseController {
411 447 public void importRuleChains(@RequestBody RuleChainData ruleChainData, @RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException {
412 448 try {
413 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 451 if (!CollectionUtils.isEmpty(importResults)) {
416 452 for (RuleChainImportResult importResult : importResults) {
417 453 tbClusterService.onEntityStateChange(importResult.getTenantId(), importResult.getRuleChainId(), importResult.getLifecycleEvent());
... ... @@ -452,5 +488,160 @@ public class RuleChainController extends BaseController {
452 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 30 import org.springframework.web.bind.annotation.RestController;
31 31 import org.thingsboard.server.common.data.TbResource;
32 32 import org.thingsboard.server.common.data.TbResourceInfo;
33   -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
34 33 import org.thingsboard.server.common.data.exception.ThingsboardException;
35 34 import org.thingsboard.server.common.data.id.TbResourceId;
36 35 import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
37 36 import org.thingsboard.server.common.data.page.PageData;
38 37 import org.thingsboard.server.common.data.page.PageLink;
39 38 import org.thingsboard.server.common.data.security.Authority;
40   -import org.thingsboard.server.dao.resource.TbResourceService;
41 39 import org.thingsboard.server.queue.util.TbCoreComponent;
  40 +import org.thingsboard.server.service.resource.TbResourceService;
42 41 import org.thingsboard.server.service.security.permission.Operation;
43 42 import org.thingsboard.server.service.security.permission.Resource;
44 43
45   -import java.util.ArrayList;
46 44 import java.util.Base64;
47 45 import java.util.List;
48   -import java.util.StringJoiner;
49 46
50 47 @Slf4j
51 48 @RestController
... ... @@ -105,25 +102,13 @@ public class TbResourceController extends BaseController {
105 102 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
106 103 @RequestMapping(value = "/resource", method = RequestMethod.POST)
107 104 @ResponseBody
108   - public List<TbResource> saveResources(@RequestBody List<TbResource> resources) throws ThingsboardException {
  105 + public TbResource saveResource(@RequestBody TbResource resource) throws ThingsboardException {
109 106 try {
110   - List<TbResource> addResources = new ArrayList<>();
111   - StringJoiner noSaveResources = new StringJoiner("; ");
112   - resources.forEach(resource -> {
113   - try {
114 107 resource.setTenantId(getTenantId());
115 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 112 throw handleException(e);
128 113 }
129 114 }
... ...
... ... @@ -95,6 +95,9 @@ public class TenantController extends BaseController {
95 95 tenant = checkNotNull(tenantService.saveTenant(tenant));
96 96 if (newTenant) {
97 97 installScripts.createDefaultRuleChains(tenant.getId());
  98 + if (edgesEnabled) {
  99 + installScripts.createDefaultEdgeRuleChains(tenant.getId());
  100 + }
98 101 }
99 102 tenantProfileCache.evict(tenant.getId());
100 103 tbClusterService.onTenantChange(tenant, null);
... ...
... ... @@ -36,9 +36,11 @@ import org.thingsboard.rule.engine.api.MailService;
36 36 import org.thingsboard.server.common.data.EntityType;
37 37 import org.thingsboard.server.common.data.User;
38 38 import org.thingsboard.server.common.data.audit.ActionType;
  39 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
39 40 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
40 41 import org.thingsboard.server.common.data.exception.ThingsboardException;
41 42 import org.thingsboard.server.common.data.id.CustomerId;
  43 +import org.thingsboard.server.common.data.id.EdgeId;
42 44 import org.thingsboard.server.common.data.id.TenantId;
43 45 import org.thingsboard.server.common.data.id.UserId;
44 46 import org.thingsboard.server.common.data.page.PageData;
... ... @@ -57,6 +59,7 @@ import org.thingsboard.server.service.security.permission.Resource;
57 59 import org.thingsboard.server.service.security.system.SystemSecurityService;
58 60
59 61 import javax.servlet.http.HttpServletRequest;
  62 +import java.util.List;
60 63
61 64 @RequiredArgsConstructor
62 65 @RestController
... ... @@ -178,6 +181,9 @@ public class UserController extends BaseController {
178 181 savedUser.getCustomerId(),
179 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 187 return savedUser;
182 188 } catch (Exception e) {
183 189
... ... @@ -247,12 +253,17 @@ public class UserController extends BaseController {
247 253 try {
248 254 UserId userId = new UserId(toUUID(strUserId));
249 255 User user = checkUserId(userId, Operation.DELETE);
  256 +
  257 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), userId);
  258 +
250 259 userService.deleteUser(getCurrentUser().getTenantId(), userId);
251 260
252 261 logEntityAction(userId, user,
253 262 user.getCustomerId(),
254 263 ActionType.DELETED, null, strUserId);
255 264
  265 + sendDeleteNotificationMsg(getTenantId(), userId, relatedEdgeIds);
  266 +
256 267 } catch (Exception e) {
257 268 logEntityAction(emptyId(EntityType.USER),
258 269 null,
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.controller;
17 17
  18 +import lombok.extern.slf4j.Slf4j;
18 19 import org.springframework.http.HttpStatus;
19 20 import org.springframework.security.access.prepost.PreAuthorize;
20 21 import org.springframework.web.bind.annotation.PathVariable;
... ... @@ -25,6 +26,8 @@ import org.springframework.web.bind.annotation.RequestParam;
25 26 import org.springframework.web.bind.annotation.ResponseBody;
26 27 import org.springframework.web.bind.annotation.ResponseStatus;
27 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 31 import org.thingsboard.server.common.data.exception.ThingsboardException;
29 32 import org.thingsboard.server.common.data.id.TenantId;
30 33 import org.thingsboard.server.common.data.id.WidgetTypeId;
... ... @@ -39,6 +42,7 @@ import org.thingsboard.server.service.security.permission.Resource;
39 42
40 43 import java.util.List;
41 44
  45 +@Slf4j
42 46 @RestController
43 47 @TbCoreComponent
44 48 @RequestMapping("/api")
... ... @@ -69,8 +73,12 @@ public class WidgetTypeController extends BaseController {
69 73 }
70 74
71 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 82 } catch (Exception e) {
75 83 throw handleException(e);
76 84 }
... ... @@ -85,6 +93,9 @@ public class WidgetTypeController extends BaseController {
85 93 WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId));
86 94 checkWidgetTypeId(widgetTypeId, Operation.DELETE);
87 95 widgetTypeService.deleteWidgetType(getCurrentUser().getTenantId(), widgetTypeId);
  96 +
  97 + sendEntityNotificationMsg(getTenantId(), widgetTypeId, EdgeEventActionType.DELETED);
  98 +
88 99 } catch (Exception e) {
89 100 throw handleException(e);
90 101 }
... ...
... ... @@ -25,6 +25,7 @@ import org.springframework.web.bind.annotation.RequestParam;
25 25 import org.springframework.web.bind.annotation.ResponseBody;
26 26 import org.springframework.web.bind.annotation.ResponseStatus;
27 27 import org.springframework.web.bind.annotation.RestController;
  28 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
28 29 import org.thingsboard.server.common.data.exception.ThingsboardException;
29 30 import org.thingsboard.server.common.data.id.TenantId;
30 31 import org.thingsboard.server.common.data.id.WidgetsBundleId;
... ... @@ -68,7 +69,12 @@ public class WidgetsBundleController extends BaseController {
68 69 }
69 70
70 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 78 } catch (Exception e) {
73 79 throw handleException(e);
74 80 }
... ... @@ -83,6 +89,9 @@ public class WidgetsBundleController extends BaseController {
83 89 WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
84 90 checkWidgetsBundleId(widgetsBundleId, Operation.DELETE);
85 91 widgetsBundleService.deleteWidgetsBundle(getTenantId(), widgetsBundleId);
  92 +
  93 + sendEntityNotificationMsg(getTenantId(), widgetsBundleId, EdgeEventActionType.DELETED);
  94 +
86 95 } catch (Exception e) {
87 96 throw handleException(e);
88 97 }
... ...
... ... @@ -27,6 +27,7 @@ import org.springframework.security.authentication.LockedException;
27 27 import org.springframework.security.core.AuthenticationException;
28 28 import org.springframework.security.web.access.AccessDeniedHandler;
29 29 import org.springframework.stereotype.Component;
  30 +import org.springframework.web.client.HttpClientErrorException;
30 31 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
31 32 import org.thingsboard.server.common.data.exception.ThingsboardException;
32 33 import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
... ... @@ -66,7 +67,12 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
66 67 response.setContentType(MediaType.APPLICATION_JSON_VALUE);
67 68
68 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 76 } else if (exception instanceof TbRateLimitsException) {
71 77 handleRateLimitException(response, (TbRateLimitsException) exception);
72 78 } else if (exception instanceof AccessDeniedException) {
... ... @@ -126,6 +132,11 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
126 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 141 private void handleAccessDeniedException(HttpServletResponse response) throws IOException {
131 142 response.setStatus(HttpStatus.FORBIDDEN.value());
... ...
... ... @@ -193,7 +193,9 @@ public class ThingsboardInstallService {
193 193 databaseEntitiesUpgradeService.upgradeDatabase("3.2.1");
194 194 case "3.2.2":
195 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 200 log.info("Updating system data...");
199 201 systemDataLoaderService.updateSystemWidgets();
... ...
... ... @@ -34,13 +34,13 @@ import org.thingsboard.rule.engine.api.TbRelationTypes;
34 34 import org.thingsboard.server.common.data.id.TenantId;
35 35 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
36 36 import org.thingsboard.server.common.data.plugin.ComponentType;
  37 +import org.thingsboard.server.common.data.rule.RuleChainType;
37 38 import org.thingsboard.server.dao.component.ComponentDescriptorService;
38 39
39 40 import javax.annotation.PostConstruct;
40 41 import java.lang.annotation.Annotation;
41 42 import java.util.ArrayList;
42 43 import java.util.Arrays;
43   -import java.util.Collection;
44 44 import java.util.Collections;
45 45 import java.util.HashMap;
46 46 import java.util.HashSet;
... ... @@ -65,7 +65,9 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
65 65
66 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 72 private ObjectMapper mapper = new ObjectMapper();
71 73
... ... @@ -93,7 +95,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
93 95 ComponentType type = ruleNodeAnnotation.type();
94 96 ComponentDescriptor component = scanAndPersistComponent(def, type);
95 97 components.put(component.getClazz(), component);
96   - componentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component);
  98 + putComponentIntoMaps(type, ruleNodeAnnotation, component);
97 99 break;
98 100 } catch (Exception e) {
99 101 log.trace("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e);
... ... @@ -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 149 private ComponentDescriptor scanAndPersistComponent(BeanDefinition def, ComponentType type) {
... ... @@ -222,25 +237,47 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
222 237 }
223 238
224 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 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 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 271 @Override
243 272 public Optional<ComponentDescriptor> getComponent(String clazz) {
244 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 17
18 18 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
19 19 import org.thingsboard.server.common.data.plugin.ComponentType;
  20 +import org.thingsboard.server.common.data.rule.RuleChainType;
20 21
21 22 import java.util.List;
22 23 import java.util.Optional;
... ... @@ -29,10 +30,9 @@ public interface ComponentDiscoveryService {
29 30
30 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 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 +}
... ...