Commit 4bbcffdf637f7fca0aa2caa03c514f31a3872561

Authored by Igor Kulikov
1 parent 784de083

Rule Node Debug UI

... ... @@ -25,6 +25,7 @@ import com.typesafe.config.Config;
25 25 import com.typesafe.config.ConfigFactory;
26 26 import lombok.Getter;
27 27 import lombok.Setter;
  28 +import lombok.extern.slf4j.Slf4j;
28 29 import org.springframework.beans.factory.annotation.Autowired;
29 30 import org.springframework.beans.factory.annotation.Value;
30 31 import org.springframework.stereotype.Component;
... ... @@ -38,6 +39,7 @@ import org.thingsboard.server.common.data.id.EntityId;
38 39 import org.thingsboard.server.common.data.id.TenantId;
39 40 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
40 41 import org.thingsboard.server.common.msg.TbMsg;
  42 +import org.thingsboard.server.common.msg.TbMsgDataType;
41 43 import org.thingsboard.server.common.msg.cluster.ServerAddress;
42 44 import org.thingsboard.server.common.transport.auth.DeviceAuthService;
43 45 import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;
... ... @@ -60,11 +62,13 @@ import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
60 62 import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
61 63 import org.thingsboard.server.service.component.ComponentDiscoveryService;
62 64
  65 +import java.io.IOException;
63 66 import java.io.PrintWriter;
64 67 import java.io.StringWriter;
65 68 import java.nio.charset.StandardCharsets;
66 69 import java.util.Optional;
67 70
  71 +@Slf4j
68 72 @Component
69 73 public class ActorSystemContext {
70 74 private static final String AKKA_CONF_FILE_NAME = "actor-system.conf";
... ... @@ -292,38 +296,49 @@ public class ActorSystemContext {
292 296 }
293 297
294 298 private void persistDebug(TenantId tenantId, EntityId entityId, String type, TbMsg tbMsg, Throwable error) {
295   - Event event = new Event();
296   - event.setTenantId(tenantId);
297   - event.setEntityId(entityId);
298   - event.setType(DataConstants.DEBUG);
299   -
300   - ObjectNode node = mapper.createObjectNode()
301   - .put("type", type)
302   - .put("server", getServerAddress())
303   - .put("entityId", tbMsg.getOriginator().getId().toString())
304   - .put("entityName", tbMsg.getOriginator().getEntityType().name())
305   - .put("msgId", tbMsg.getId().toString())
306   - .put("msgType", tbMsg.getType())
307   - .put("dataType", tbMsg.getDataType().name());
308   -
309   - ObjectNode mdNode = node.putObject("metadata");
310   - tbMsg.getMetaData().getData().forEach(mdNode::put);
  299 + try {
  300 + Event event = new Event();
  301 + event.setTenantId(tenantId);
  302 + event.setEntityId(entityId);
  303 + event.setType(DataConstants.DEBUG_RULE_NODE);
  304 +
  305 + String metadata = mapper.writeValueAsString(tbMsg.getMetaData().getData());
  306 +
  307 + ObjectNode node = mapper.createObjectNode()
  308 + .put("type", type)
  309 + .put("server", getServerAddress())
  310 + .put("entityId", tbMsg.getOriginator().getId().toString())
  311 + .put("entityName", tbMsg.getOriginator().getEntityType().name())
  312 + .put("msgId", tbMsg.getId().toString())
  313 + .put("msgType", tbMsg.getType())
  314 + .put("dataType", tbMsg.getDataType().name())
  315 + .put("data", convertToString(tbMsg.getDataType(), tbMsg.getData()))
  316 + .put("metadata", metadata);
  317 +
  318 + if (error != null) {
  319 + node = node.put("error", toString(error));
  320 + }
  321 +
  322 + event.setBody(node);
  323 + eventService.save(event);
  324 + } catch (IOException ex) {
  325 + log.warn("Failed to persist rule node debug message", ex);
  326 + }
  327 + }
311 328
312   - switch (tbMsg.getDataType()) {
  329 + private String convertToString(TbMsgDataType messageType, byte[] data) {
  330 + if (data == null) {
  331 + return null;
  332 + }
  333 + switch (messageType) {
  334 + case JSON:
  335 + case TEXT:
  336 + return new String(data, StandardCharsets.UTF_8);
313 337 case BINARY:
314   - node.put("data", Base64Utils.encodeUrlSafe(tbMsg.getData()));
315   - break;
  338 + return Base64Utils.encodeToString(data);
316 339 default:
317   - node.put("data", new String(tbMsg.getData(), StandardCharsets.UTF_8));
318   - break;
319   - }
320   -
321   - if (error != null) {
322   - node = node.put("error", toString(error));
  340 + throw new RuntimeException("Message type: " + messageType + " is not supported!");
323 341 }
324   -
325   - event.setBody(node);
326   - eventService.save(event);
327 342 }
328 343
329 344 public static Exception toException(Throwable error) {
... ...
... ... @@ -51,6 +51,6 @@ public class AbstractRuleEngineControllerTest extends AbstractControllerTest {
51 51 TimePageLink pageLink = new TimePageLink(limit);
52 52 return doGetTypedWithTimePageLink("/api/events/{entityType}/{entityId}/{eventType}?tenantId={tenantId}&",
53 53 new TypeReference<TimePageData<Event>>() {
54   - }, pageLink, entityId.getEntityType(), entityId.getId(), DataConstants.DEBUG, tenantId.getId());
  54 + }, pageLink, entityId.getEntityType(), entityId.getId(), DataConstants.DEBUG_RULE_NODE, tenantId.getId());
55 55 }
56 56 }
... ...
... ... @@ -37,7 +37,7 @@ public class DataConstants {
37 37 public static final String ERROR = "ERROR";
38 38 public static final String LC_EVENT = "LC_EVENT";
39 39 public static final String STATS = "STATS";
40   - public static final String DEBUG = "DEBUG";
  40 + public static final String DEBUG_RULE_NODE = "DEBUG_RULE_NODE";
41 41
42 42 public static final String ONEWAY = "ONEWAY";
43 43 public static final String TWOWAY = "TWOWAY";
... ...
... ... @@ -279,6 +279,23 @@ export default angular.module('thingsboard.types', [])
279 279 function: "function",
280 280 alarm: "alarm"
281 281 },
  282 + contentType: {
  283 + "JSON": {
  284 + value: "JSON",
  285 + name: "content-type.json",
  286 + code: "json"
  287 + },
  288 + "TEXT": {
  289 + value: "TEXT",
  290 + name: "content-type.text",
  291 + code: "text"
  292 + },
  293 + "BINARY": {
  294 + value: "BINARY",
  295 + name: "content-type.binary",
  296 + code: "text"
  297 + }
  298 + },
282 299 componentType: {
283 300 filter: "FILTER",
284 301 processor: "PROCESSOR",
... ... @@ -295,7 +312,8 @@ export default angular.module('thingsboard.types', [])
295 312 user: "USER",
296 313 dashboard: "DASHBOARD",
297 314 alarm: "ALARM",
298   - rulechain: "RULE_CHAIN"
  315 + rulechain: "RULE_CHAIN",
  316 + rulenode: "RULE_NODE"
299 317 },
300 318 aliasEntityType: {
301 319 current_customer: "CURRENT_CUSTOMER"
... ... @@ -388,6 +406,16 @@ export default angular.module('thingsboard.types', [])
388 406 name: "event.type-stats"
389 407 }
390 408 },
  409 + debugEventType: {
  410 + debugRuleNode: {
  411 + value: "DEBUG_RULE_NODE",
  412 + name: "event.type-debug-rule-node"
  413 + },
  414 + debugRuleChain: {
  415 + value: "DEBUG_RULE_CHAIN",
  416 + name: "event.type-debug-rule-chain"
  417 + }
  418 + },
391 419 extensionType: {
392 420 http: "HTTP",
393 421 mqtt: "MQTT",
... ...
... ... @@ -26,7 +26,7 @@ export default angular.module('thingsboard.directives.detailsSidenav', [])
26 26 .name;
27 27
28 28 /*@ngInject*/
29   -function DetailsSidenav($timeout) {
  29 +function DetailsSidenav($timeout, $window) {
30 30
31 31 var linker = function (scope, element, attrs) {
32 32
... ... @@ -42,6 +42,23 @@ function DetailsSidenav($timeout) {
42 42 scope.isEdit = true;
43 43 }
44 44
  45 + if (angular.isDefined(attrs.closeOnClickOutside && attrs.closeOnClickOutside)) {
  46 + scope.closeOnClickOutside = true;
  47 + var clickOutsideHandler = function() {
  48 + scope.closeDetails();
  49 + };
  50 + angular.element($window).click(clickOutsideHandler);
  51 + scope.$on("$destroy", function () {
  52 + angular.element($window).unbind('click', clickOutsideHandler);
  53 + });
  54 + }
  55 +
  56 + scope.onClick = function($event) {
  57 + if (scope.closeOnClickOutside) {
  58 + $event.stopPropagation();
  59 + }
  60 + };
  61 +
45 62 scope.toggleDetailsEditMode = function () {
46 63 if (!scope.isAlwaysEdit) {
47 64 if (!scope.isEdit) {
... ...
... ... @@ -19,6 +19,7 @@
19 19 md-disable-backdrop="true"
20 20 md-is-open="isOpen"
21 21 md-component-id="right"
  22 + ng-click="onClick($event)"
22 23 layout="column">
23 24 <header>
24 25 <md-toolbar class="md-theme-light" ng-style="{'height':headerHeightPx+'px'}">
... ...
... ... @@ -17,11 +17,14 @@ import $ from 'jquery';
17 17 import 'brace/ext/language_tools';
18 18 import 'brace/mode/java';
19 19 import 'brace/theme/github';
  20 +import beautify from 'js-beautify';
20 21
21 22 /* eslint-disable angular/angularelement */
22 23
  24 +const js_beautify = beautify.js;
  25 +
23 26 /*@ngInject*/
24   -export default function EventContentDialogController($mdDialog, content, title, showingCallback) {
  27 +export default function EventContentDialogController($mdDialog, types, content, contentType, title, showingCallback) {
25 28
26 29 var vm = this;
27 30
... ... @@ -32,9 +35,19 @@ export default function EventContentDialogController($mdDialog, content, title,
32 35 vm.content = content;
33 36 vm.title = title;
34 37
  38 + var mode;
  39 + if (contentType) {
  40 + mode = types.contentType[contentType].code;
  41 + if (contentType == types.contentType.JSON.value && vm.content) {
  42 + vm.content = js_beautify(vm.content, {indent_size: 4});
  43 + }
  44 + } else {
  45 + mode = 'java';
  46 + }
  47 +
35 48 vm.contentOptions = {
36 49 useWrapMode: false,
37   - mode: 'java',
  50 + mode: mode,
38 51 showGutter: false,
39 52 showPrintMargin: false,
40 53 theme: 'github',
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2018 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<div hide-xs hide-sm translate class="tb-cell" flex="30">event.event-time</div>
  19 +<div translate class="tb-cell" flex="20">event.server</div>
  20 +<div translate class="tb-cell" flex="20">event.type</div>
  21 +<div translate class="tb-cell" flex="20">event.entity</div>
  22 +<div translate class="tb-cell" flex="20">event.message-id</div>
  23 +<div translate class="tb-cell" flex="20">event.message-type</div>
  24 +<div translate class="tb-cell" flex="20">event.data-type</div>
  25 +<div translate class="tb-cell" flex="20">event.data</div>
  26 +<div translate class="tb-cell" flex="20">event.metadata</div>
  27 +<div translate class="tb-cell" flex="20">event.error</div>
... ...
... ... @@ -18,6 +18,7 @@
18 18 import eventHeaderLcEventTemplate from './event-header-lc-event.tpl.html';
19 19 import eventHeaderStatsTemplate from './event-header-stats.tpl.html';
20 20 import eventHeaderErrorTemplate from './event-header-error.tpl.html';
  21 +import eventHeaderDebugRuleNodeTemplate from './event-header-debug-rulenode.tpl.html';
21 22
22 23 /* eslint-enable import/no-unresolved, import/default */
23 24
... ... @@ -38,6 +39,12 @@ export default function EventHeaderDirective($compile, $templateCache, types) {
38 39 case types.eventType.error.value:
39 40 template = eventHeaderErrorTemplate;
40 41 break;
  42 + case types.debugEventType.debugRuleNode.value:
  43 + template = eventHeaderDebugRuleNodeTemplate;
  44 + break;
  45 + case types.debugEventType.debugRuleChain.value:
  46 + template = eventHeaderDebugRuleNodeTemplate;
  47 + break;
41 48 }
42 49 return $templateCache.get(template);
43 50 }
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2018 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<div hide-xs hide-sm class="tb-cell" flex="30">{{event.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}</div>
  19 +<div class="tb-cell" flex="20">{{event.body.server}}</div>
  20 +<div class="tb-cell" flex="20">{{event.body.type}}</div>
  21 +<div class="tb-cell" flex="20">{{event.body.entityName}}</div>
  22 +<div class="tb-cell" flex="20">{{event.body.msgId}}</div>
  23 +<div class="tb-cell" flex="20">{{event.body.msgType}}</div>
  24 +<div class="tb-cell" flex="20">{{event.body.dataType}}</div>
  25 +<div class="tb-cell" flex="20">
  26 + <md-button ng-if="event.body.data" class="md-icon-button md-primary"
  27 + ng-click="showContent($event, event.body.data, 'event.data', event.body.msgType)"
  28 + aria-label="{{ 'action.view' | translate }}">
  29 + <md-tooltip md-direction="top">
  30 + {{ 'action.view' | translate }}
  31 + </md-tooltip>
  32 + <md-icon aria-label="{{ 'action.view' | translate }}"
  33 + class="material-icons">
  34 + more_horiz
  35 + </md-icon>
  36 + </md-button>
  37 +</div>
  38 +<div class="tb-cell" flex="20">
  39 + <md-button ng-if="event.body.metadata" class="md-icon-button md-primary"
  40 + ng-click="showContent($event, event.body.metadata, 'event.metadata', 'JSON')"
  41 + aria-label="{{ 'action.view' | translate }}">
  42 + <md-tooltip md-direction="top">
  43 + {{ 'action.view' | translate }}
  44 + </md-tooltip>
  45 + <md-icon aria-label="{{ 'action.view' | translate }}"
  46 + class="material-icons">
  47 + more_horiz
  48 + </md-icon>
  49 + </md-button>
  50 +</div>
  51 +<div class="tb-cell" flex="20">
  52 + <md-button ng-if="event.body.error" class="md-icon-button md-primary"
  53 + ng-click="showContent($event, event.body.error, 'event.error')"
  54 + aria-label="{{ 'action.view' | translate }}">
  55 + <md-tooltip md-direction="top">
  56 + {{ 'action.view' | translate }}
  57 + </md-tooltip>
  58 + <md-icon aria-label="{{ 'action.view' | translate }}"
  59 + class="material-icons">
  60 + more_horiz
  61 + </md-icon>
  62 + </md-button>
  63 +</div>
... ...
... ... @@ -20,6 +20,7 @@ import eventErrorDialogTemplate from './event-content-dialog.tpl.html';
20 20 import eventRowLcEventTemplate from './event-row-lc-event.tpl.html';
21 21 import eventRowStatsTemplate from './event-row-stats.tpl.html';
22 22 import eventRowErrorTemplate from './event-row-error.tpl.html';
  23 +import eventRowDebugRuleNodeTemplate from './event-row-debug-rulenode.tpl.html';
23 24
24 25 /* eslint-enable import/no-unresolved, import/default */
25 26
... ... @@ -40,6 +41,12 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $
40 41 case types.eventType.error.value:
41 42 template = eventRowErrorTemplate;
42 43 break;
  44 + case types.debugEventType.debugRuleNode.value:
  45 + template = eventRowDebugRuleNodeTemplate;
  46 + break;
  47 + case types.debugEventType.debugRuleChain.value:
  48 + template = eventRowDebugRuleNodeTemplate;
  49 + break;
43 50 }
44 51 return $templateCache.get(template);
45 52 }
... ... @@ -53,17 +60,22 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $
53 60 scope.loadTemplate();
54 61 });
55 62
  63 + scope.types = types;
  64 +
56 65 scope.event = attrs.event;
57 66
58   - scope.showContent = function($event, content, title) {
  67 + scope.showContent = function($event, content, title, contentType) {
59 68 var onShowingCallback = {
60 69 onShowing: function(){}
61 70 }
  71 + if (!contentType) {
  72 + contentType = null;
  73 + }
62 74 $mdDialog.show({
63 75 controller: 'EventContentDialogController',
64 76 controllerAs: 'vm',
65 77 templateUrl: eventErrorDialogTemplate,
66   - locals: {content: content, title: title, showingCallback: onShowingCallback},
  78 + locals: {content: content, title: title, contentType: contentType, showingCallback: onShowingCallback},
67 79 parent: angular.element($document[0].body),
68 80 fullscreen: true,
69 81 targetEvent: $event,
... ...
... ... @@ -36,8 +36,8 @@ export default function EventTableDirective($compile, $templateCache, $rootScope
36 36 for (var type in types.eventType) {
37 37 var eventType = types.eventType[type];
38 38 var enabled = true;
39   - for (var disabledType in disabledEventTypes) {
40   - if (eventType.value === disabledEventTypes[disabledType]) {
  39 + for (var i=0;i<disabledEventTypes.length;i++) {
  40 + if (eventType.value === disabledEventTypes[i]) {
41 41 enabled = false;
42 42 break;
43 43 }
... ... @@ -47,7 +47,19 @@ export default function EventTableDirective($compile, $templateCache, $rootScope
47 47 }
48 48 }
49 49 } else {
50   - scope.eventTypes = types.eventType;
  50 + scope.eventTypes = angular.copy(types.eventType);
  51 + }
  52 +
  53 + if (attrs.debugEventTypes) {
  54 + var debugEventTypes = attrs.debugEventTypes.split(',');
  55 + for (i=0;i<debugEventTypes.length;i++) {
  56 + for (type in types.debugEventType) {
  57 + eventType = types.debugEventType[type];
  58 + if (eventType.value === debugEventTypes[i]) {
  59 + scope.eventTypes[type] = eventType;
  60 + }
  61 + }
  62 + }
51 63 }
52 64
53 65 scope.eventType = attrs.defaultEventType;
... ...
... ... @@ -341,6 +341,11 @@ export default angular.module('thingsboard.locale', [])
341 341 "enter-password": "Enter password",
342 342 "enter-search": "Enter search"
343 343 },
  344 + "content-type": {
  345 + "json": "Json",
  346 + "text": "Text",
  347 + "binary": "Binary (Base64)"
  348 + },
344 349 "customer": {
345 350 "customer": "Customer",
346 351 "customers": "Customers",
... ... @@ -762,6 +767,8 @@ export default angular.module('thingsboard.locale', [])
762 767 "type-error": "Error",
763 768 "type-lc-event": "Lifecycle event",
764 769 "type-stats": "Statistics",
  770 + "type-debug-rule-node": "Debug",
  771 + "type-debug-rule-chain": "Debug",
765 772 "no-events-prompt": "No events found",
766 773 "error": "Error",
767 774 "alarm": "Alarm",
... ... @@ -769,6 +776,13 @@ export default angular.module('thingsboard.locale', [])
769 776 "server": "Server",
770 777 "body": "Body",
771 778 "method": "Method",
  779 + "type": "Type",
  780 + "entity": "Entity",
  781 + "message-id": "Message Id",
  782 + "message-type": "Message Type",
  783 + "data-type": "Data Type",
  784 + "metadata": "Metadata",
  785 + "data": "Data",
772 786 "event": "Event",
773 787 "status": "Status",
774 788 "success": "Success",
... ... @@ -1172,6 +1186,7 @@ export default angular.module('thingsboard.locale', [])
1172 1186 },
1173 1187 "rulenode": {
1174 1188 "details": "Details",
  1189 + "events": "Events",
1175 1190 "add": "Add rule node",
1176 1191 "name": "Name",
1177 1192 "name-required": "Name is required.",
... ...
... ... @@ -28,7 +28,7 @@ import addRuleNodeLinkTemplate from './add-link.tpl.html';
28 28 /* eslint-enable import/no-unresolved, import/default */
29 29
30 30 /*@ngInject*/
31   -export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, $timeout, $mdExpansionPanel, $document, $mdDialog,
  31 +export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, $timeout, $mdExpansionPanel, $window, $document, $mdDialog,
32 32 $filter, $translate, hotkeys, types, ruleChainService, Modelfactory, flowchartConstants,
33 33 ruleChain, ruleChainMetaData, ruleNodeComponents) {
34 34
... ... @@ -77,6 +77,8 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
77 77 vm.objectsSelected = objectsSelected;
78 78 vm.deleteSelected = deleteSelected;
79 79
  80 + vm.triggerResize = triggerResize;
  81 +
80 82 initHotKeys();
81 83
82 84 function initHotKeys() {
... ... @@ -129,18 +131,17 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
129 131 }
130 132
131 133 vm.onEditRuleNodeClosed = function() {
132   - vm.editingRuleNode = null;
  134 + //vm.editingRuleNode = null;
133 135 };
134 136
135 137 vm.onEditRuleNodeLinkClosed = function() {
136   - vm.editingRuleNodeLink = null;
  138 + //vm.editingRuleNodeLink = null;
137 139 };
138 140
139 141 vm.saveRuleNode = function(theForm) {
140 142 $scope.$broadcast('form-submit');
141 143 if (theForm.$valid) {
142 144 theForm.$setPristine();
143   - vm.isEditingRuleNode = false;
144 145 vm.ruleChainModel.nodes[vm.editingRuleNodeIndex] = vm.editingRuleNode;
145 146 vm.editingRuleNode = angular.copy(vm.editingRuleNode);
146 147 }
... ... @@ -148,7 +149,6 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
148 149
149 150 vm.saveRuleNodeLink = function(theForm) {
150 151 theForm.$setPristine();
151   - vm.isEditingRuleNodeLink = false;
152 152 vm.ruleChainModel.edges[vm.editingRuleNodeLinkIndex] = vm.editingRuleNodeLink;
153 153 vm.editingRuleNodeLink = angular.copy(vm.editingRuleNodeLink);
154 154 };
... ... @@ -663,6 +663,11 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
663 663 function deleteSelected() {
664 664 vm.modelservice.deleteSelected();
665 665 }
  666 +
  667 + function triggerResize() {
  668 + var w = angular.element($window);
  669 + w.triggerHandler('resize');
  670 + }
666 671 }
667 672
668 673 /*@ngInject*/
... ...
... ... @@ -67,8 +67,9 @@
67 67 header-title="{{vm.editingRuleNode.name}}"
68 68 header-subtitle="{{(vm.types.ruleNodeType[vm.editingRuleNode.component.type].name | translate)
69 69 + ' - ' + vm.editingRuleNode.component.name}}"
70   - is-read-only="false"
  70 + is-read-only="vm.selectedRuleNodeTabIndex > 0"
71 71 is-open="vm.isEditingRuleNode"
  72 + close-on-click-outside="true"
72 73 is-always-edit="true"
73 74 on-close-details="vm.onEditRuleNodeClosed()"
74 75 on-toggle-details-edit-mode="vm.onRevertRuleNodeEdit(vm.ruleNodeForm)"
... ... @@ -77,9 +78,10 @@
77 78 <details-buttons tb-help="vm.helpLinkIdForRuleNodeType()" help-container-id="help-container">
78 79 <div id="help-container"></div>
79 80 </details-buttons>
80   - <md-tabs id="ruleNodeTabs" md-border-bottom flex class="tb-absolute-fill">
  81 + <md-tabs md-selected="vm.selectedRuleNodeTabIndex"
  82 + id="ruleNodeTabs" md-border-bottom flex class="tb-absolute-fill" ng-if="vm.isEditingRuleNode">
81 83 <md-tab label="{{ 'rulenode.details' | translate }}">
82   - <form name="vm.ruleNodeForm" ng-if="vm.isEditingRuleNode">
  84 + <form name="vm.ruleNodeForm">
83 85 <tb-rule-node
84 86 rule-node="vm.editingRuleNode"
85 87 rule-chain-id="vm.ruleChain.id.id"
... ... @@ -90,6 +92,15 @@
90 92 </tb-rule-node>
91 93 </form>
92 94 </md-tab>
  95 + <md-tab ng-if="vm.isEditingRuleNode && vm.editingRuleNode.ruleNodeId"
  96 + md-on-select="vm.triggerResize()" label="{{ 'rulenode.events' | translate }}">
  97 + <tb-event-table flex entity-type="vm.types.entityType.rulenode"
  98 + entity-id="vm.editingRuleNode.ruleNodeId.id"
  99 + tenant-id="vm.ruleChain.tenantId.id"
  100 + debug-event-types="{{vm.types.debugEventType.debugRuleNode.value}}"
  101 + default-event-type="{{vm.types.debugEventType.debugRuleNode.value}}">
  102 + </tb-event-table>
  103 + </md-tab>
93 104 </md-tabs>
94 105 </tb-details-sidenav>
95 106 <tb-details-sidenav class="tb-rulenode-link-details-sidenav"
... ... @@ -97,6 +108,7 @@
97 108 header-subtitle="{{'rulenode.link-details' | translate}}"
98 109 is-read-only="false"
99 110 is-open="vm.isEditingRuleNodeLink"
  111 + close-on-click-outside="true"
100 112 is-always-edit="true"
101 113 on-close-details="vm.onEditRuleNodeLinkClosed()"
102 114 on-toggle-details-edit-mode="vm.onRevertRuleNodeLinkEdit(vm.ruleNodeLinkForm)"
... ...
... ... @@ -55,7 +55,8 @@
55 55 <tb-event-table flex entity-type="vm.types.entityType.rulechain"
56 56 entity-id="vm.grid.operatingItem().id.id"
57 57 tenant-id="vm.grid.operatingItem().tenantId.id"
58   - default-event-type="{{vm.types.eventType.lcEvent.value}}">
  58 + debug-event-types="{{vm.types.debugEventType.debugRuleChain.value}}"
  59 + default-event-type="{{vm.types.debugEventType.debugRuleChain.value}}">
59 60 </tb-event-table>
60 61 </md-tab>
61 62 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleChainEditable(vm.grid.operatingItem())" md-on-select="vm.grid.triggerResize()" label="{{ 'relation.relations' | translate }}">
... ...