Commit a42bd5d7eeaf744aa58df15d5b943a3b7cc36d1a

Authored by Igor Kulikov
1 parent 70e81e84

UI: RuleNode configuration

Showing 101 changed files with 1151 additions and 118 deletions
... ... @@ -41,7 +41,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS;
41 41 nodeDescription = "Periodically generates messages",
42 42 nodeDetails = "Generates messages with configurable period. Javascript function used for message generation.",
43 43 inEnabled = false,
44   - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
  44 + uiResources = {"static/rulenode/rulenode-core-config.js"},
45 45 configDirective = "tbActionNodeGeneratorConfig",
46 46 icon = "repeat"
47 47 )
... ...
... ... @@ -32,7 +32,7 @@ import org.thingsboard.server.common.msg.TbMsg;
32 32 relationTypes = {"True", "False"},
33 33 nodeDescription = "Filter incoming messages by Message Type",
34 34 nodeDetails = "If incoming MessageType is expected - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.",
35   - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
  35 + uiResources = {"static/rulenode/rulenode-core-config.js"},
36 36 configDirective = "tbFilterNodeMessageTypeConfig")
37 37 public class TbMsgTypeFilterNode implements TbNode {
38 38
... ...
... ... @@ -30,7 +30,7 @@ import org.thingsboard.server.common.msg.TbMsg;
30 30 relationTypes = {"True", "False"},
31 31 nodeDescription = "Filter incoming messages by message Originator Type",
32 32 nodeDetails = "If Originator Type of incoming message is expected - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.",
33   - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
  33 + uiResources = {"static/rulenode/rulenode-core-config.js"},
34 34 configDirective = "tbFilterNodeOriginatorTypeConfig")
35 35 public class TbOriginatorTypeFilterNode implements TbNode {
36 36
... ...
... ... @@ -32,7 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
32 32 "If Latest Telemetry enrichment configured, latest telemetry added into metadata. " +
33 33 "To access those attributes in other nodes this template can be used " +
34 34 "<code>metadata.temperature</code>.",
35   - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
  35 + uiResources = {"static/rulenode/rulenode-core-config.js"},
36 36 configDirective = "tbEnrichmentNodeCustomerAttributesConfig")
37 37 public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode<CustomerId> {
38 38
... ...
... ... @@ -34,7 +34,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
34 34 "If Latest Telemetry enrichment configured, latest telemetry added into metadata. " +
35 35 "To access those attributes in other nodes this template can be used " +
36 36 "<code>metadata.temperature</code>.",
37   - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
  37 + uiResources = {"static/rulenode/rulenode-core-config.js"},
38 38 configDirective = "tbEnrichmentNodeRelatedAttributesConfig")
39 39
40 40 public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> {
... ...
... ... @@ -34,7 +34,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
34 34 "If Latest Telemetry enrichment configured, latest telemetry added into metadata. " +
35 35 "To access those attributes in other nodes this template can be used " +
36 36 "<code>metadata.temperature</code>.",
37   - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
  37 + uiResources = {"static/rulenode/rulenode-core-config.js"},
38 38 configDirective = "tbEnrichmentNodeTenantAttributesConfig")
39 39 public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> {
40 40
... ...
... ... @@ -47,7 +47,7 @@ import java.util.concurrent.TimeoutException;
47 47 configClazz = TbMqttNodeConfiguration.class,
48 48 nodeDescription = "Publish messages to the MQTT broker",
49 49 nodeDetails = "Will publish message payload to the MQTT broker with QoS <b>AT_LEAST_ONCE</b>.",
50   - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
  50 + uiResources = {"static/rulenode/rulenode-core-config.js"},
51 51 configDirective = "tbActionNodeMqttConfig",
52 52 icon = "call_split"
53 53 )
... ...
... ... @@ -42,7 +42,7 @@ import java.util.Set;
42 42 configClazz = TbMsgAttributesNodeConfiguration.class,
43 43 nodeDescription = "Saves attributes data",
44 44 nodeDetails = "Saves entity attributes based on configurable scope parameter. Expects messages with 'POST_ATTRIBUTES_REQUEST' message type",
45   - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
  45 + uiResources = {"static/rulenode/rulenode-core-config.js"},
46 46 configDirective = "tbActionNodeAttributesConfig",
47 47 icon = "file_upload"
48 48 )
... ...
... ... @@ -43,7 +43,7 @@ import java.util.Map;
43 43 configClazz = TbMsgTimeseriesNodeConfiguration.class,
44 44 nodeDescription = "Saves timeseries data",
45 45 nodeDetails = "Saves timeseries telemetry data based on configurable TTL parameter. Expects messages with 'POST_TELEMETRY_REQUEST' message type",
46   - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
  46 + uiResources = {"static/rulenode/rulenode-core-config.js"},
47 47 configDirective = "tbActionNodeTimeseriesConfig",
48 48 icon = "file_upload"
49 49 )
... ...
... ... @@ -44,7 +44,7 @@ import java.util.HashSet;
44 44 nodeDetails = "Related Entity found using configured relation direction and Relation Type. " +
45 45 "If multiple Related Entities are found, only first Entity is used as new Originator, other entities are discarded.<br/>" +
46 46 "Alarm Originator found only in case original Originator is <code>Alarm</code> entity.",
47   - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
  47 + uiResources = {"static/rulenode/rulenode-core-config.js"},
48 48 configDirective = "tbTransformationNodeChangeOriginatorConfig",
49 49 icon = "find_replace"
50 50 )
... ...
... ... @@ -33,7 +33,7 @@ import org.thingsboard.server.common.msg.TbMsg;
33 33 "Should return the following structure:<br/>" +
34 34 "<code>{ msg: <i style=\"color: #666;\">new payload</i>,<br/>&nbsp&nbsp&nbspmetadata: <i style=\"color: #666;\">new metadata</i>,<br/>&nbsp&nbsp&nbspmsgType: <i style=\"color: #666;\">new msgType</i> }</code><br/>" +
35 35 "All fields in resulting object are optional and will be taken from original message if not specified.",
36   - uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
  36 + uiResources = {"static/rulenode/rulenode-core-config.js"},
37 37 configDirective = "tbTransformationNodeScriptConfig")
38 38 public class TbTransformMsgNode extends TbAbstractTransformNode {
39 39
... ...
1   -.tb-message-type-autocomplete .tb-not-found{display:block;line-height:1.5;height:48px}.tb-message-type-autocomplete .tb-not-found .tb-no-entries{line-height:48px}.tb-message-type-autocomplete li{height:auto!important;white-space:normal!important}.tb-generator-config tb-json-content.tb-message-body,.tb-generator-config tb-json-object-edit.tb-metadata-json{height:200px;display:block}.tb-mqtt-config .tb-credentials-panel-group .tb-panel-title{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;min-width:90px}@media (min-width:960px){.tb-mqtt-config .tb-credentials-panel-group .tb-panel-title{min-width:180px}}.tb-mqtt-config .tb-credentials-panel-group .tb-panel-prompt{font-size:14px;color:rgba(0,0,0,.87);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tb-mqtt-config .tb-credentials-panel-group.disabled .tb-panel-prompt,.tb-mqtt-config .tb-credentials-panel-group.disabled .tb-panel-title{color:rgba(0,0,0,.38)}.tb-mqtt-config .tb-credentials-panel-group md-icon.md-expansion-panel-icon{margin-right:0}.tb-mqtt-config .tb-container{width:100%}.tb-mqtt-config .dropdown-messages .tb-error-message{padding:5px 0 0}.tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}.tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:rgba(0,0,0,.54);font-size:12px;font-weight:700;white-space:nowrap}.tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:20px;max-height:300px;overflow:auto}.tb-kv-map-config .body .row{padding-top:5px;max-height:40px}.tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}.tb-kv-map-config .body md-input-container.cell{margin:0;max-height:40px}.tb-kv-map-config .body .md-button{margin:0}
2   -/*# sourceMappingURL=rulenode-core-config.css.map*/
\ No newline at end of file
1   -!function(e){function t(i){if(n[i])return n[i].exports;var a=n[i]={exports:{},id:i,loaded:!1};return e[i].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),i=e[t[0]];return function(e,t,a){i.apply(this,[e,t,a].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(101)},function(e,t){},1,1,1,1,function(e,t){e.exports=" <section ng-form name=assignCustomerConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.customer-name-pattern</label> <input ng-required=true name=customerNamePattern ng-model=configuration.customerNamePattern> <div ng-messages=assignCustomerConfigForm.customerNamePattern.$error> <div ng-message=required translate>tb.rulenode.customer-name-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.customer-name-pattern-hint</div> </md-input-container> <md-checkbox aria-label=\"{{ 'tb.rulenode.create-group-if-not-exists' | translate }}\" ng-model=configuration.createCustomerIfNotExists>{{ 'tb.rulenode.create-customer-if-not-exists' | translate }} </md-checkbox> <md-input-container class=md-block> <label translate>tb.rulenode.customer-cache-expiration</label> <input type=number step=1 min=0 ng-required=true name=customerCacheExpiration ng-model=configuration.customerCacheExpiration> <div ng-messages=assignCustomerConfigForm.customerCacheExpiration.$error> <div translate ng-message=required>tb.rulenode.customer-cache-expiration-required</div> <div translate ng-message=min>tb.rulenode.customer-cache-expiration-range</div> </div> <div class=tb-hint translate>tb.rulenode.customer-cache-expiration-hint</div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=attributesConfigForm layout=column> <md-input-container class=md-block> <label translate>attribute.attributes-scope</label> <md-select ng-model=configuration.scope ng-disabled=$root.loading> <md-option ng-repeat="scope in types.attributesScope" ng-value=scope.value> {{scope.name | translate}} </md-option> </md-select> </md-input-container> </section> '},function(e,t){e.exports=" <section class=tb-alarm-config ng-form name=alarmConfigForm layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.alarm-details-builder</label> <tb-js-func ng-model=configuration.alarmDetailsBuildJs function-name=Details function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testDetailsBuildJs($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-details-function' | translate }} </md-button> </div> <md-input-container class=md-block> <label translate>tb.rulenode.alarm-type</label> <input ng-required=true name=alarmType ng-model=configuration.alarmType> <div ng-messages=alarmConfigForm.alarmType.$error> <div ng-message=required translate>tb.rulenode.alarm-type-required</div> </div> <div class=tb-hint translate>tb.rulenode.entity-type-pattern-hint</div> </md-input-container> </section> "},function(e,t){e.exports=" <section class=tb-alarm-config ng-form name=alarmConfigForm layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.alarm-details-builder</label> <tb-js-func ng-model=configuration.alarmDetailsBuildJs function-name=Details function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testDetailsBuildJs($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-details-function' | translate }} </md-button> </div> <md-checkbox aria-label=\"{{ 'tb.rulenode.use-metadata-interval-patterns' | translate }}\" ng-model=configuration.useMessageAlarmData>{{ 'tb.rulenode.use-message-alarm-data' | translate }} </md-checkbox> <section layout=column layout-gt-sm=row ng-if=!configuration.useMessageAlarmData> <md-input-container flex class=md-block> <label translate>tb.rulenode.alarm-type</label> <input ng-required=true name=alarmType ng-model=configuration.alarmType> <div ng-messages=alarmConfigForm.alarmType.$error> <div ng-message=required translate>tb.rulenode.alarm-type-required</div> </div> <div class=tb-hint translate>tb.rulenode.entity-type-pattern-hint</div> </md-input-container> <md-input-container flex class=md-block> <label translate>tb.rulenode.alarm-severity</label> <md-select required name=severity ng-model=configuration.severity> <md-option ng-repeat=\"(severityKey, severity) in types.alarmSeverity\" ng-value=severityKey> {{ severity.name | translate}} </md-option> </md-select> <div ng-messages=alarmConfigForm.severity.$error> <div ng-message=required translate>tb.rulenode.alarm-severity-required</div> </div> </md-input-container> </section> <section layout=column layout-gt-sm=row ng-if=!configuration.useMessageAlarmData> <md-checkbox aria-label=\"{{ 'tb.rulenode.propagate' | translate }}\" ng-model=configuration.propagate>{{ 'tb.rulenode.propagate' | translate }} </md-checkbox> </section> </section> "},function(e,t){e.exports=" <section ng-form name=createRelationConfigForm layout=column style=min-width:650px> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=configuration.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <div layout=row class=tb-entity-select> <md-input-container class=md-block> <tb-entity-type-select style=min-width:100px the-form=createRelationConfigForm tb-required=true ng-model=configuration.entityType> </tb-entity-type-select> </md-input-container> <md-input-container class=md-block flex ng-if=configuration.entityType style=margin-top:38px> <label translate>tb.rulenode.entity-name-pattern</label> <input ng-required=true name=entityNamePattern ng-model=configuration.entityNamePattern> <div ng-messages=createRelationConfigForm.entityNamePattern.$error> <div ng-message=required translate>tb.rulenode.entity-name-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.entity-name-pattern-hint</div> </md-input-container> <md-input-container class=md-block flex ng-if=\"configuration.entityType == 'DEVICE' || configuration.entityType == 'ASSET'\" style=margin-top:38px> <label translate>tb.rulenode.entity-type-pattern</label> <input ng-required=true name=entityTypePattern ng-model=configuration.entityTypePattern> <div ng-messages=createRelationConfigForm.entityTypePattern.$error> <div ng-message=required translate>tb.rulenode.entity-type-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.entity-type-pattern-hint</div> </md-input-container> </div> <md-input-container class=md-block flex style=margin-top:0> <label translate>tb.rulenode.relation-type-pattern</label> <input ng-required=true name=relationType ng-model=configuration.relationType> <div ng-messages=createRelationConfigForm.relationType.$error> <div ng-message=required translate>tb.rulenode.relation-type-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.relation-type-pattern-hint</div> </md-input-container> <md-checkbox flex ng-if=\"configuration.entityType == 'CUSTOMER' || configuration.entityType == 'ASSET' || configuration.entityType == 'DEVICE'\" aria-label=\"{{ 'tb.rulenode.create-entity-if-not-exists' | translate }}\" ng-model=configuration.createEntityIfNotExists>{{ 'tb.rulenode.create-entity-if-not-exists' | translate }} </md-checkbox> <div class=tb-hint ng-if=\"configuration.entityType == 'CUSTOMER' || configuration.entityType == 'ASSET' || configuration.entityType == 'DEVICE'\" translate>tb.rulenode.create-entity-if-not-exists-hint</div> <md-checkbox flex aria-label=\"{{ 'tb.rulenode.remove-current-relations' | translate }}\" ng-model=configuration.removeCurrentRelations>{{ 'tb.rulenode.remove-current-relations' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.remove-current-relations-hint</div> <md-checkbox flex aria-label=\"{{ 'tb.rulenode.change-originator-to-related-entity' | translate }}\" ng-model=configuration.changeOriginatorToRelatedEntity>{{ 'tb.rulenode.change-originator-to-related-entity' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.change-originator-to-related-entity-hint</div> <md-input-container class=md-block> <label translate>tb.rulenode.entity-cache-expiration</label> <input type=number step=1 min=0 ng-required=true name=entityCacheExpiration ng-model=configuration.entityCacheExpiration> <div ng-messages=createRelationConfigForm.entityCacheExpiration.$error> <div translate ng-message=required>tb.rulenode.entity-cache-expiration-required</div> <div translate ng-message=min>tb.rulenode.entity-cache-expiration-range</div> </div> <div class=tb-hint translate>tb.rulenode.entity-cache-expiration-hint</div> </md-input-container> </section> "},function(e,t){e.exports=" <section ng-form name=deleteRelationConfigForm layout=column> <md-checkbox aria-label=\"{{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }}\" ng-model=configuration.deleteForSingleEntity> {{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.delete-relation-hint</div> <md-input-container class=md-block style=min-width:100px;margin-bottom:38px> <label translate>relation.direction</label> <md-select required ng-model=configuration.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <div layout=row class=tb-entity-select ng-if=configuration.deleteForSingleEntity> <md-input-container class=md-block> <tb-entity-type-select style=min-width:100px the-form=deleteRelationConfigForm tb-required=true ng-model=configuration.entityType> </tb-entity-type-select> </md-input-container> <md-input-container class=md-block flex ng-if=configuration.entityType style=margin-top:38px> <label translate>tb.rulenode.entity-name-pattern</label> <input ng-required=true name=entityNamePattern ng-model=configuration.entityNamePattern> <div ng-messages=deleteRelationConfigForm.entityNamePattern.$error> <div ng-message=required translate>tb.rulenode.entity-name-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.entity-name-pattern-hint</div> </md-input-container> </div> <md-input-container class=md-block flex> <label translate>tb.rulenode.relation-type-pattern</label> <input ng-required=true name=relationType ng-model=configuration.relationType> <div ng-messages=createRelationConfigForm.relationType.$error> <div ng-message=required translate>tb.rulenode.relation-type-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.relation-type-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.entity-cache-expiration</label> <input type=number step=1 min=0 ng-required=true name=entityCacheExpiration ng-model=configuration.entityCacheExpiration> <div ng-messages=deleteRelationConfigForm.entityCacheExpiration.$error> <div translate ng-message=required>tb.rulenode.entity-cache-expiration-required</div> <div translate ng-message=min>tb.rulenode.entity-cache-expiration-range</div> </div> <div class=tb-hint translate>tb.rulenode.entity-cache-expiration-hint</div> </md-input-container> </section> "},function(e,t){e.exports=" <section class=tb-generator-config ng-form name=generatorConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.message-count</label> <input ng-required=true type=number step=1 name=messageCount ng-model=configuration.msgCount min=0> <div ng-messages=generatorConfigForm.messageCount.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.message-count-required</div> <div ng-message=min translate>tb.rulenode.min-message-count-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.period-seconds</label> <input ng-required=true type=number step=1 name=periodInSeconds ng-model=configuration.periodInSeconds min=1> <div ng-messages=generatorConfigForm.periodInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.period-seconds-required</div> <div ng-message=min translate>tb.rulenode.min-period-seconds-message</div> </div> </md-input-container> <div layout=column> <label class=tb-small>{{ 'tb.rulenode.originator' | translate }}</label> <tb-entity-select the-form=generatorConfigForm tb-required=false ng-model=originator> </tb-entity-select> </div> <label translate class=\"tb-title no-padding\">tb.rulenode.generate</label> <tb-js-func ng-model=configuration.jsScript function-name=Generate function-args=\"{{ ['prevMsg', 'prevMetadata', 'prevMsgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-generator-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section ng-form name=geoActionConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.latitude-key-name</label> <input ng-required=true name=latitudeKeyName ng-model=configuration.latitudeKeyName> <div ng-messages=geoActionConfigForm.latitudeKeyName.$error> <div ng-message=required translate>tb.rulenode.latitude-key-name-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.longitude-key-name</label> <input ng-required=true name=longitudeKeyName ng-model=configuration.longitudeKeyName> <div ng-messages=geoActionConfigForm.longitudeKeyName.$error> <div ng-message=required translate>tb.rulenode.longitude-key-name-required</div> </div> </md-input-container> <md-checkbox flex aria-label="{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}" ng-model=configuration.fetchPerimeterInfoFromMessageMetadata>{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }} </md-checkbox> <div layout=row class=tb-entity-select ng-if="configuration.fetchPerimeterInfoFromMessageMetadata === false"> <md-input-container class=md-block flex=100> <label translate>tb.rulenode.perimeter-type</label> <md-select required ng-model=configuration.perimeterType flex> <md-option ng-repeat="type in ruleNodeTypes.perimeterType" ng-value=type.value> {{ type.name | translate}} </md-option> </md-select> </md-input-container> </div> <div layout=row layout-wrap ng-if="configuration.perimeterType===ruleNodeTypes.perimeterType.CIRCLE.value && configuration.fetchPerimeterInfoFromMessageMetadata === false"> <div layout=column flex=50> <md-input-container class=md-block flex layout=column style=margin-top:44px> <label translate>tb.rulenode.circle-center-latitude</label> <input type=number min=0 step=0.1 ng-required=true name=centerLatitude ng-model=configuration.centerLatitude> <div ng-messages=geoActionConfigForm.centerLatitude.$error> <div ng-message=required translate>tb.rulenode.circle-center-latitude-required</div> </div> </md-input-container> </div> <div layout=column flex=50> <md-input-container class=md-block flex style=margin-top:44px> <label translate>tb.rulenode.circle-center-longitude</label> <input type=number min=0 step=0.1 ng-required=true name=centerLongitude ng-model=configuration.centerLongitude> <div ng-messages=geoActionConfigForm.centerLongitude.$error> <div ng-message=required translate>tb.rulenode.circle-center-longitude-required</div> </div> </md-input-container> </div> <div layout=column flex=50> <md-input-container class=md-block style=margin-top:28px> <label translate>tb.rulenode.range</label> <input type=number min=0 step=0.1 ng-required=true name=range ng-model=configuration.range> <div ng-messages=geoActionConfigForm.range.$error> <div ng-message=required translate>tb.rulenode.range-required</div> </div> </md-input-container> </div> <div layout=column flex=50> <md-input-container class=md-block style=margin-top:28px> <label translate>tb.rulenode.range-units</label> <md-select required ng-model=configuration.rangeUnit> <md-option ng-repeat="type in ruleNodeTypes.rangeUnit" ng-value=type.value> {{ type.name | translate}} </md-option> </md-select> </md-input-container> </div> </div> <div layout=row layout-wrap ng-if="configuration.perimeterType===ruleNodeTypes.perimeterType.POLYGON.value && configuration.fetchPerimeterInfoFromMessageMetadata === false"> <div layout=column flex=100> <md-input-container class=md-block style=margin-top:44px> <label translate>tb.rulenode.polygon-definition</label> <input ng-required=true name=polygonsDefinition ng-model=configuration.polygonsDefinition> <div ng-messages=geoActionConfigForm.polygonsDefinition.$error> <div ng-message=required translate>tb.rulenode.polygon-definition-required</div> </div> <div class=tb-hint style=margin-top:5px translate>tb.rulenode.polygon-definition-hint</div> </md-input-container> </div> </div> <div layout=column layout-gt-sm=row> <md-input-container flex class="md-block tb-time-value" flex> <label translate class="tb-title no-padding">tb.rulenode.min-inside-duration</label> <input required type=number step=1 min=1 max=2147483647 name=minInsideDuration ng-model=configuration.minInsideDuration> <div ng-messages=geoActionConfigForm.minInsideDuration.$error> <div translate ng-message=required>tb.rulenode.min-inside-duration-value-required</div> <div ng-message=min translate>tb.rulenode.time-value-range</div> <div ng-message=max translate>tb.rulenode.time-value-range</div> </div> </md-input-container> <md-input-container flex class="md-block tb-time-unit" flex> <label translate class="tb-title no-padding">tb.rulenode.min-inside-duration-time-unit</label> <md-select required name=minInsideDurationTimeUnit aria-label="{{ \'tb.rulenode.min-inside-duration-time-unit\' | translate }}" ng-model=configuration.minInsideDurationTimeUnit> <md-option ng-repeat="timeUnit in ruleNodeTypes.timeUnit" ng-value=timeUnit.value> {{timeUnit.name | translate}} </md-option> </md-select> </md-input-container> </div> <div layout=column layout-gt-sm=row> <md-input-container flex class="md-block tb-time-value" flex> <label translate class="tb-title no-padding">tb.rulenode.min-outside-duration</label> <input required type=number step=1 min=1 max=2147483647 name=minOutsideDuration ng-model=configuration.minOutsideDuration> <div ng-messages=geoActionConfigForm.minOutsideDuration.$error> <div translate ng-message=required>tb.rulenode.min-outside-duration-value-required</div> <div ng-message=min translate>tb.rulenode.time-value-range</div> <div ng-message=max translate>tb.rulenode.time-value-range</div> </div> </md-input-container> <md-input-container flex class="md-block tb-time-unit" flex> <label translate class="tb-title no-padding">tb.rulenode.min-outside-duration-time-unit</label> <md-select required name=minOutsideDurationTimeUnit aria-label="{{ \'tb.rulenode.min-outside-duration-time-unit\' | translate }}" ng-model=configuration.minOutsideDurationTimeUnit> <md-option ng-repeat="timeUnit in ruleNodeTypes.timeUnit" ng-value=timeUnit.value> {{timeUnit.name | translate}} </md-option> </md-select> </md-input-container> </div> </section> '},function(e,t){e.exports=' <section ng-form name=kafkaConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-pattern</label> <input ng-required=true name=topicPattern ng-model=configuration.topicPattern> <div ng-messages=kafkaConfigForm.topicPattern.$error> <div ng-message=required translate>tb.rulenode.topic-pattern-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.bootstrap-servers</label> <input ng-required=true name=bootstrapServers ng-model=configuration.bootstrapServers> <div ng-messages=kafkaConfigForm.bootstrapServers.$error> <div ng-message=required translate>tb.rulenode.bootstrap-servers-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.retries</label> <input type=number step=1 name=retries ng-model=configuration.retries min=0> <div ng-messages=kafkaConfigForm.retries.$error> <div ng-message=min translate>tb.rulenode.min-retries-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.batch-size-bytes</label> <input type=number step=1 name=batchSize ng-model=configuration.batchSize min=0> <div ng-messages=kafkaConfigForm.batchSize.$error> <div ng-message=min translate>tb.rulenode.min-batch-size-bytes-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.linger-ms</label> <input type=number step=1 name=linger ng-model=configuration.linger min=0> <div ng-messages=kafkaConfigForm.linger.$error> <div ng-message=min translate>tb.rulenode.min-linger-ms-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.buffer-memory-bytes</label> <input type=number step=1 name=bufferMemory ng-model=configuration.bufferMemory min=0> <div ng-messages=kafkaConfigForm.bufferMemory.$error> <div ng-message=min translate>tb.rulenode.min-buffer-memory-bytes-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.acks</label> <md-select ng-model=configuration.acks ng-disabled=$root.loading> <md-option ng-repeat="ackValue in ackValues" ng-value=ackValue> {{ ackValue }} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.key-serializer</label> <input ng-required=true name=keySerializer ng-model=configuration.keySerializer> <div ng-messages=kafkaConfigForm.keySerializer.$error> <div ng-message=required translate>tb.rulenode.key-serializer-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.value-serializer</label> <input ng-required=true name=valueSerializer ng-model=configuration.valueSerializer> <div ng-messages=kafkaConfigForm.valueSerializer.$error> <div ng-message=required translate>tb.rulenode.value-serializer-required</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.other-properties</label> <tb-kv-map-config ng-model=configuration.otherProperties ng-required=false key-text="\'tb.rulenode.key\'" key-required-text="\'tb.rulenode.key-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.to-string</label> <tb-js-func ng-model=configuration.jsScript function-name=ToString function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-to-string-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section class=tb-mqtt-config ng-form name=mqttConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-pattern</label> <input ng-required=true name=topicPattern ng-model=configuration.topicPattern> <div ng-messages=mqttConfigForm.topicPattern.$error> <div translate ng-message=required>tb.rulenode.topic-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.mqtt-topic-pattern-hint</div> </md-input-container> <div flex layout=column layout-gt-sm=row> <md-input-container flex=60 class=md-block> <label translate>tb.rulenode.host</label> <input ng-required=true name=host ng-model=configuration.host> <div ng-messages=mqttConfigForm.host.$error> <div translate ng-message=required>tb.rulenode.host-required</div> </div> </md-input-container> <md-input-container flex=40 class=md-block> <label translate>tb.rulenode.port</label> <input type=number step=1 min=1 max=65535 ng-required=true name=port ng-model=configuration.port> <div ng-messages=mqttConfigForm.port.$error> <div translate ng-message=required>tb.rulenode.port-required</div> <div translate ng-message=min>tb.rulenode.port-range</div> <div translate ng-message=max>tb.rulenode.port-range</div> </div> </md-input-container> <md-input-container flex=40 class=md-block> <label translate>tb.rulenode.connect-timeout</label> <input type=number step=1 min=1 max=200 ng-required=true name=connectTimeoutSec ng-model=configuration.connectTimeoutSec> <div ng-messages=mqttConfigForm.connectTimeoutSec.$error> <div translate ng-message=required>tb.rulenode.connect-timeout-required</div> <div translate ng-message=min>tb.rulenode.connect-timeout-range</div> <div translate ng-message=max>tb.rulenode.connect-timeout-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.client-id</label> <input name=clientId ng-model=configuration.clientId> </md-input-container> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.clean-session\' | translate }}" ng-model=configuration.cleanSession> {{ \'tb.rulenode.clean-session\' | translate }} </md-checkbox> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.enable-ssl\' | translate }}" ng-model=configuration.ssl> {{ \'tb.rulenode.enable-ssl\' | translate }} </md-checkbox> <md-expansion-panel-group class=tb-credentials-panel-group ng-class="{\'disabled\': $root.loading || readonly}" md-component-id=credentialsPanelGroup> <md-expansion-panel md-component-id=credentialsPanel> <md-expansion-panel-collapsed> <div class=tb-panel-title>{{ \'tb.rulenode.credentials\' | translate }}</div> <div class=tb-panel-prompt>{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}</div> <span flex></span> <md-expansion-panel-icon></md-expansion-panel-icon> </md-expansion-panel-collapsed> <md-expansion-panel-expanded> <md-expansion-panel-header ng-click="$mdExpansionPanel(\'credentialsPanel\').collapse()"> <div class=tb-panel-title>{{ \'tb.rulenode.credentials\' | translate }}</div> <div class=tb-panel-prompt>{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}</div> <span flex></span> <md-expansion-panel-icon></md-expansion-panel-icon> </md-expansion-panel-header> <md-expansion-panel-content> <div layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.credentials-type</label> <md-select ng-required=true name=credentialsType ng-model=configuration.credentials.type ng-disabled="$root.loading || readonly" ng-change=credentialsTypeChanged()> <md-option ng-repeat="(credentialsType, credentialsValue) in ruleNodeTypes.mqttCredentialTypes" ng-value=credentialsValue.value> {{credentialsValue.name | translate}} </md-option> </md-select> <div ng-messages=mqttConfigForm.credentialsType.$error> <div translate ng-message=required>tb.rulenode.credentials-type-required</div> </div> </md-input-container> <section flex layout=column ng-if="configuration.credentials.type == ruleNodeTypes.mqttCredentialTypes.basic.value"> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input ng-required=true name=mqttUsername ng-model=configuration.credentials.username> <div ng-messages=mqttConfigForm.mqttUsername.$error> <div translate ng-message=required>tb.rulenode.username-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input type=password ng-required=true name=mqttPassword ng-model=configuration.credentials.password> <div ng-messages=mqttConfigForm.mqttPassword.$error> <div translate ng-message=required>tb.rulenode.password-required</div> </div> </md-input-container> </section> <section flex layout=column ng-if="configuration.credentials.type == ruleNodeTypes.mqttCredentialTypes[\'cert.PEM\'].value" class=dropdown-section> <div class=tb-container ng-class="configuration.credentials.caCertFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.ca-cert</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'caCert\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'caCert\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=caCertSelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=caCertSelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.caCertFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.caCertFileName>{{configuration.credentials.caCertFileName}}</div> </div> <div class=tb-container ng-class="configuration.credentials.certFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.cert</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'Cert\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'Cert\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=CertSelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=CertSelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.certFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.certFileName>{{configuration.credentials.certFileName}}</div> </div> <div class=tb-container ng-class="configuration.credentials.privateKeyFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.private-key</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'privateKey\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'privateKey\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=privateKeySelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=privateKeySelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.privateKeyFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.privateKeyFileName>{{configuration.credentials.privateKeyFileName}}</div> </div> <md-input-container class=md-block> <label translate>tb.rulenode.private-key-password</label> <input type=password name=privateKeyPassword ng-model=configuration.credentials.password> </md-input-container> </section> </div> </md-expansion-panel-content> </md-expansion-panel-expanded> </md-expansion-panel> </md-expansion-panel-group> </section>'},function(e,t){e.exports=" <section ng-form name=msgCountConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.interval-seconds</label> <input ng-required=true type=number step=1 name=interval ng-model=configuration.interval min=1> <div ng-messages=msgCountConfigForm.interval.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.interval-seconds-required</div> <div ng-message=min translate>tb.rulenode.min-interval-seconds-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.output-timeseries-key-prefix</label> <input ng-required=true name=telemetryPrefix ng-model=configuration.telemetryPrefix> <div ng-messages=msgCountConfigForm.telemetryPrefix.$error> <div translate ng-message=required>tb.rulenode.output-timeseries-key-prefix-required</div> </div> </md-input-container> </section> ";
2   -},function(e,t){e.exports=' <section ng-form name=msgDelayConfigForm layout=column> <md-checkbox aria-label="{{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}" ng-model=configuration.useMetadataPeriodInSecondsPatterns>{{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.use-metadata-period-in-seconds-patterns-hint</div> <md-input-container class=md-block ng-if="configuration.useMetadataPeriodInSecondsPatterns === undefined || configuration.useMetadataPeriodInSecondsPatterns == false"> <label translate>tb.rulenode.period-seconds</label> <input ng-required=true type=number step=1 name=periodInSeconds ng-model=configuration.periodInSeconds min=0> <div ng-messages=msgDelayConfigForm.periodInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.period-seconds-required</div> <div ng-message=min translate>tb.rulenode.min-period-0-seconds-message</div> </div> </md-input-container> <md-input-container class=md-block flex ng-if="configuration.useMetadataPeriodInSecondsPatterns === true"> <label translate>tb.rulenode.period-in-seconds-pattern</label> <input ng-required=true name=periodInSecondsPattern ng-model=configuration.periodInSecondsPattern> <div ng-messages=msgDelayConfigForm.periodInSecondsPattern.$error> <div ng-message=required translate>tb.rulenode.period-in-seconds-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.period-in-seconds-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-pending-messages</label> <input ng-required=true type=number step=1 name=maxPendingMsgs ng-model=configuration.maxPendingMsgs min=1 max=100000> <div ng-messages=msgDelayConfigForm.maxPendingMsgs.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.max-pending-messages-required</div> <div ng-message=min translate>tb.rulenode.max-pending-messages-range</div> <div ng-message=max translate>tb.rulenode.max-pending-messages-range</div> </div> </md-input-container> </section> '},function(e,t){e.exports=" <section ng-form name=pubsubConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.gcp-project-id</label> <input ng-required=true name=projectId ng-model=configuration.projectId> <div ng-messages=pubsubConfigForm.projectId.$error> <div ng-message=required translate>tb.rulenode.gcp-project-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.pubsub-topic-name</label> <input ng-required=true name=topicName ng-model=configuration.topicName> <div ng-messages=pubsubConfigForm.topicName.$error> <div ng-message=required translate>tb.rulenode.pubsub-topic-name-required</div> </div> </md-input-container> <div class=tb-container ng-class=\"configuration.serviceAccountKeyFileName ? 'ng-valid' : 'ng-invalid'\"> <label class=tb-label translate>tb.rulenode.gcp-service-account-key</label> <div flow-init={singleFile:true} flow-file-added=serviceAccountFileAdded($file) class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click=clearServiceAccountFile() class=\"tb-file-clear-btn md-icon-button md-primary\" aria-label=\"{{ 'action.remove' | translate }}\"> <md-tooltip md-direction=top> {{ 'action.remove' | translate }} </md-tooltip> <md-icon aria-label=\"{{ 'action.remove' | translate }}\" class=material-icons>close</md-icon> </md-button> </div> <div class=\"alert tb-flow-drop\" flow-drop> <label for=serviceAccountKeySelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=serviceAccountKeySelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.serviceAccountKeyFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.serviceAccountKeyFileName>{{configuration.serviceAccountKeyFileName}}</div> </div> <label translate class=tb-title>tb.rulenode.message-attributes</label> <div class=tb-hint translate>tb.rulenode.message-attributes-hint</div> <tb-kv-map-config ng-model=configuration.messageAttributes ng-required=false key-text=\"'tb.rulenode.name'\" key-required-text=\"'tb.rulenode.name-required'\" val-text=\"'tb.rulenode.value'\" val-required-text=\"'tb.rulenode.value-required'\"> </tb-kv-map-config> </section> "},function(e,t){e.exports=' <section ng-form name=rabbitMqConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.exchange-name-pattern</label> <input name=exchangeNamePattern ng-model=configuration.exchangeNamePattern> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.routing-key-pattern</label> <input name=routingKeyPattern ng-model=configuration.routingKeyPattern> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.message-properties</label> <md-select ng-model=configuration.messageProperties ng-disabled="$root.loading || readonly"> <md-option ng-repeat="property in messageProperties" ng-value=property> {{ property }} </md-option> </md-select> </md-input-container> <div layout-gt-sm=row> <md-input-container class=md-block flex=100 flex-gt-sm=60> <label translate>tb.rulenode.host</label> <input ng-required=true name=host ng-model=configuration.host> <div ng-messages=rabbitMqConfigForm.host.$error> <div ng-message=required translate>tb.rulenode.host-required</div> </div> </md-input-container> <md-input-container class=md-block flex=100 flex-gt-sm=40> <label translate>tb.rulenode.port</label> <input ng-required=true type=number step=1 name=port ng-model=configuration.port min=0 max=65535> <div ng-messages=rabbitMqConfigForm.port.$error> <div ng-message=required translate>tb.rulenode.port-required</div> <div ng-message=min translate>tb.rulenode.port-range</div> <div ng-message=max translate>tb.rulenode.port-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.virtual-host</label> <input name=virtualHost ng-model=configuration.virtualHost> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input name=virtualHost ng-model=configuration.username> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input name=virtualHost type=password ng-model=configuration.password> </md-input-container> <md-input-container class=md-block> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.automatic-recovery\' | translate }}" ng-model=ruleNode.automaticRecoveryEnabled>{{ \'tb.rulenode.automatic-recovery\' | translate }} </md-checkbox> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.connection-timeout-ms</label> <input type=number step=1 name=connectionTimeout ng-model=configuration.connectionTimeout min=0> <div ng-messages=rabbitMqConfigForm.connectionTimeout.$error> <div ng-message=min translate>tb.rulenode.min-connection-timeout-ms-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.handshake-timeout-ms</label> <input type=number step=1 name=handshakeTimeout ng-model=configuration.handshakeTimeout min=0> <div ng-messages=rabbitMqConfigForm.handshakeTimeout.$error> <div ng-message=min translate>tb.rulenode.min-handshake-timeout-ms-message</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.client-properties</label> <tb-kv-map-config ng-model=configuration.clientProperties ng-required=false key-text="\'tb.rulenode.key\'" key-required-text="\'tb.rulenode.key-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=' <section ng-form name=restApiCallConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.endpoint-url-pattern</label> <input ng-required=true name=endpointUrlPattern ng-model=configuration.restEndpointUrlPattern> <div ng-messages=restApiCallConfigForm.endpointUrlPattern.$error> <div ng-message=required translate>tb.rulenode.endpoint-url-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.endpoint-url-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.request-method</label> <md-select ng-model=configuration.requestMethod ng-disabled=$root.loading> <md-option ng-repeat="type in ruleNodeTypes.httpRequestType" ng-value=type> {{ type }} </md-option> </md-select> </md-input-container> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}" ng-model=configuration.useSimpleClientHttpFactory> {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }} </md-checkbox> <label translate class=tb-title>tb.rulenode.headers</label> <div class=tb-hint translate>tb.rulenode.headers-hint</div> <tb-kv-map-config ng-model=configuration.headers ng-required=false key-text="\'tb.rulenode.header\'" key-required-text="\'tb.rulenode.header-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section ng-form name=rpcReplyConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.request-id-metadata-attribute</label> <input name=requestIdMetaDataAttribute ng-model=configuration.requestIdMetaDataAttribute> </md-input-container> </section> "},function(e,t){e.exports=" <section ng-form name=rpcRequestConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.timeout-sec</label> <input ng-required=true type=number step=1 name=timeoutInSeconds ng-model=configuration.timeoutInSeconds min=0> <div ng-messages=rpcRequestConfigForm.timeoutInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.timeout-required</div> <div ng-message=min translate>tb.rulenode.min-timeout-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.custom-table-name</label> <input ng-required=true name=tableName ng-model=configuration.tableName> <div ng-messages=saveToCustomTableConfigForm.tableName.$error> <div ng-message=required translate>tb.rulenode.custom-table-name-required</div> </div> <div class=tb-hint style=margin-top:5px translate>tb.rulenode.custom-table-hint</div> </md-input-container> <label translate class="tb-title tb-required">tb.rulenode.fields-mapping</label> <tb-kv-map-config ng-model=configuration.fieldsMapping ng-required=true required-text="\'tb.rulenode.fields-mapping-required\'" key-text="\'tb.rulenode.message-field\'" key-required-text="\'tb.rulenode.message-field-required\'" val-text="\'tb.rulenode.table-col\'" val-required-text="\'tb.rulenode.table-col-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=' <section ng-form name=sendEmailConfigForm layout=column> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}" ng-model=configuration.useSystemSmtpSettings> {{ \'tb.rulenode.use-system-smtp-settings\' | translate }} </md-checkbox> <section layout=column ng-if=!configuration.useSystemSmtpSettings> <md-input-container class=md-block> <label translate>tb.rulenode.smtp-protocol</label> <md-select ng-disabled="$root.loading || readonly" ng-model=configuration.smtpProtocol> <md-option ng-repeat="smtpProtocol in smtpProtocols" value={{smtpProtocol}}> {{smtpProtocol.toUpperCase()}} </md-option> </md-select> </md-input-container> <div layout-gt-sm=row> <md-input-container class=md-block flex=100 flex-gt-sm=60> <label translate>tb.rulenode.smtp-host</label> <input ng-required=true name=smtpHost ng-model=configuration.smtpHost> <div ng-messages=sendEmailConfigForm.smtpHost.$error> <div translate ng-message=required>tb.rulenode.smtp-host-required</div> </div> </md-input-container> <md-input-container class=md-block flex=100 flex-gt-sm=40> <label translate>tb.rulenode.smtp-port</label> <input type=number step=1 min=1 max=65535 ng-required=true name=port ng-model=configuration.smtpPort> <div ng-messages=sendEmailConfigForm.port.$error> <div translate ng-message=required>tb.rulenode.smtp-port-required</div> <div translate ng-message=min>tb.rulenode.smtp-port-range</div> <div translate ng-message=max>tb.rulenode.smtp-port-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.timeout-msec</label> <input type=number step=1 min=0 ng-required=true name=timeout ng-model=configuration.timeout> <div ng-messages=sendEmailConfigForm.timeout.$error> <div translate ng-message=required>tb.rulenode.timeout-required</div> <div translate ng-message=min>tb.rulenode.min-timeout-msec-message</div> </div> </md-input-container> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.enable-tls\' | translate }}" ng-model=configuration.enableTls>{{ \'tb.rulenode.enable-tls\' | translate }}</md-checkbox> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input name=username placeholder="{{ \'tb.rulenode.enter-username\' | translate }}" ng-model=configuration.username> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input name=password placeholder="{{ \'tb.rulenode.enter-password\' | translate }}" type=password ng-model=configuration.password> </md-input-container> </section> </section> '},function(e,t){e.exports=" <section ng-form name=snsConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-arn-pattern</label> <input ng-required=true name=topicArnPattern ng-model=configuration.topicArnPattern> <div ng-messages=snsConfigForm.topicArnPattern.$error> <div ng-message=required translate>tb.rulenode.topic-arn-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.topic-arn-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-access-key-id</label> <input ng-required=true name=accessKeyId ng-model=configuration.accessKeyId> <div ng-messages=snsConfigForm.accessKeyId.$error> <div ng-message=required translate>tb.rulenode.aws-access-key-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-secret-access-key</label> <input ng-required=true name=secretAccessKey ng-model=configuration.secretAccessKey> <div ng-messages=snsConfigForm.secretAccessKey.$error> <div ng-message=required translate>tb.rulenode.aws-secret-access-key-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-region</label> <input ng-required=true name=region ng-model=configuration.region> <div ng-messages=snsConfigForm.region.$error> <div ng-message=required translate>tb.rulenode.aws-region-required</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=sqsConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.queue-type</label> <md-select ng-model=configuration.queueType ng-disabled="$root.loading || readonly"> <md-option ng-repeat="type in ruleNodeTypes.sqsQueueType" ng-value=type.value> {{ type.name | translate }} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.queue-url-pattern</label> <input ng-required=true name=queueUrlPattern ng-model=configuration.queueUrlPattern> <div ng-messages=sqsConfigForm.queueUrlPattern.$error> <div ng-message=required translate>tb.rulenode.queue-url-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.queue-url-pattern-hint</div> </md-input-container> <md-input-container class=md-block ng-if="configuration.queueType == ruleNodeTypes.sqsQueueType.STANDARD.value"> <label translate>tb.rulenode.delay-seconds</label> <input type=number step=1 name=delaySeconds ng-model=configuration.delaySeconds min=0 max=900> <div ng-messages=sqsConfigForm.delaySeconds.$error> <div ng-message=min translate>tb.rulenode.min-delay-seconds-message</div> <div ng-message=max translate>tb.rulenode.max-delay-seconds-message</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.message-attributes</label> <div class=tb-hint translate>tb.rulenode.message-attributes-hint</div> <tb-kv-map-config ng-model=configuration.messageAttributes ng-required=false key-text="\'tb.rulenode.name\'" key-required-text="\'tb.rulenode.name-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> <md-input-container class=md-block> <label translate>tb.rulenode.aws-access-key-id</label> <input ng-required=true name=accessKeyId ng-model=configuration.accessKeyId> <div ng-messages=snsConfigForm.accessKeyId.$error> <div ng-message=required translate>tb.rulenode.aws-access-key-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-secret-access-key</label> <input ng-required=true name=secretAccessKey ng-model=configuration.secretAccessKey> <div ng-messages=snsConfigForm.secretAccessKey.$error> <div ng-message=required translate>tb.rulenode.aws-secret-access-key-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-region</label> <input ng-required=true name=region ng-model=configuration.region> <div ng-messages=snsConfigForm.region.$error> <div ng-message=required translate>tb.rulenode.aws-region-required</div> </div> </md-input-container> </section> '},function(e,t){e.exports=" <section ng-form name=timeseriesConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.default-ttl</label> <input ng-required=true type=number step=1 name=defaultTTL ng-model=configuration.defaultTTL min=0> <div ng-messages=timeseriesConfigForm.defaultTTL.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.default-ttl-required</div> <div ng-message=min translate>tb.rulenode.min-default-ttl-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=" <section ng-form name=unAssignCustomerConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.customer-name-pattern</label> <input ng-required=true name=customerNamePattern ng-model=configuration.customerNamePattern> <div ng-messages=unAssignCustomerConfigForm.customerNamePattern.$error> <div ng-message=required translate>tb.rulenode.customer-name-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.customer-name-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.customer-cache-expiration</label> <input type=number step=1 min=0 ng-required=true name=customerCacheExpiration ng-model=configuration.customerCacheExpiration> <div ng-messages=unAssignCustomerConfigForm.customerCacheExpiration.$error> <div translate ng-message=required>tb.rulenode.customer-cache-expiration-required</div> <div translate ng-message=min>tb.rulenode.customer-cache-expiration-range</div> </div> <div class=tb-hint translate>tb.rulenode.customer-cache-expiration-hint</div> </md-input-container> </section> "},function(e,t){e.exports=' <section layout=column> <div layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat="direction in types.entitySearchDirection" ng-value=direction> {{ (\'relation.search-direction.\' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder="{{ \'tb.rulenode.unlimited-level\' | translate }}" ng-model=query.maxLevel aria-label="{{ \'tb.rulenode.max-relation-level\' | translate }}"> </md-input-container> </div> <div class=md-caption style=color:rgba(0,0,0,.57) translate>relation.relation-type</div> <tb-relation-type-autocomplete flex hide-label ng-model=query.relationType tb-required=false> </tb-relation-type-autocomplete> <div class="md-caption tb-required" style=color:rgba(0,0,0,.57) translate>device.device-types</div> <tb-entity-subtype-list tb-required=true entity-type=types.entityType.device ng-model=query.deviceTypes> </tb-entity-subtype-list> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> "},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title tb-required">tb.rulenode.device-relations-query</label> <tb-device-relations-query-config style=padding-bottom:15px ng-model=configuration.deviceRelationsQuery> </tb-device-relations-query-config> <md-checkbox aria-label="{{ \'tb.rulenode.tell-failure-if-absent\' | translate }}" ng-model=configuration.tellFailureIfAbsent> {{ \'tb.rulenode.tell-failure-if-absent\' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.tell-failure-if-absent-hint</div> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys md-add-on-blur=true> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys md-add-on-blur=true> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys md-add-on-blur=true> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys md-add-on-blur=true> </md-chips> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" class=required>tb.rulenode.entity-details</label> <md-chips readonly=disabled style=margin-bottom:28px id=entityDetailsListChips ng-required=tbRequired ng-model=configuration.detailsList placeholder={{placeholder}} secondary-placeholder={{secondaryPlaceholder}} md-autocomplete-snap md-require-match=true> <md-autocomplete md-no-cache=true id=entityDetails md-selected-item=selectedEntityDetail md-selected-item-change=selectedItemChange(item) md-search-text=entityDetailsSearchText md-items="item in entityDetailsList" md-item-text=item md-min-length=0 placeholder="{{ (!ruleNodeTypes.entityDetails || !ruleNodeTypes.entityDetails.length) ? placeholder : secondaryPlaceholder }}"> <md-item-template> <span md-highlight-text=entityDetailsSearchText md-highlight-flags=^i> {{\'tb.rulenode.entity-details-\'+item.toLowerCase() | translate}} </span> </md-item-template> <md-not-found> <span translate translate-values="{ entityDetails: entityDetailsSearchText }">tb.rulenode.no-entity-details-matching</span> </md-not-found> </md-autocomplete> <md-chip-template> <span> <strong>{{\'tb.rulenode.entity-details-\'+$chip.toLowerCase() | translate}}</strong> </span> </md-chip-template> </md-chips> <div class=tb-error-messages ng-messages=ngModelCtrl.$error ng-if="inputTouched && tbRequired" role=alert> <div translate ng-message=configuration.detailsList class=tb-error-message>tb.rulenode.entity-details-list-empty</div> </div> <md-checkbox aria-label="{{ \'tb.rulenode.add-to-metadata\' | translate }}" ng-model=configuration.addToMetadata> {{ \'tb.rulenode.add-to-metadata\' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.add-to-metadata-hint</div> </section> '},function(e,t){e.exports=' <section class=tb-telemetry-from-database-config ng-form name=getTelemetryConfigForm layout=column> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <md-input-container style=margin-bottom:18px;margin-top:58px> <label translate class="tb-title no-padding">tb.rulenode.fetch-mode</label> <md-select required ng-model=configuration.fetchMode> <md-option ng-repeat="type in ruleNodeTypes.fetchModeType" ng-value=type> {{ type }} </md-option> </md-select> </md-input-container> <div class=tb-hint translate>tb.rulenode.fetch-mode-hint</div> <md-input-container flex ng-if="configuration.fetchMode === \'ALL\' "> <label translate class="tb-title no-padding">tb.rulenode.order-by</label> <md-select required ng-model=configuration.orderBy> <md-option ng-repeat="type in ruleNodeTypes.samplingOrder" ng-value=type> {{ type }} </md-option> </md-select> </md-input-container> <div class=tb-hint translate flex ng-if="configuration.fetchMode === \'ALL\' ">tb.rulenode.order-by-hint</div> <md-checkbox aria-label="{{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}" ng-model=configuration.useMetadataIntervalPatterns>{{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.use-metadata-interval-patterns-hint</div> <div layout=column layout-gt-sm=row> <md-input-container flex class="md-block tb-time-value" flex ng-if="configuration.useMetadataIntervalPatterns == false"> <label translate class="tb-title no-padding">tb.rulenode.start-interval</label> <input required type=number step=1 min=1 max=2147483647 name=startInterval ng-model=configuration.startInterval> <div ng-messages=getTelemetryConfigForm.startInterval.$error> <div translate ng-message=required>tb.rulenode.start-interval-value-required</div> <div ng-message=min translate>tb.rulenode.time-value-range</div> <div ng-message=max translate>tb.rulenode.time-value-range</div> </div> </md-input-container> <md-input-container flex class="md-block tb-time-unit" flex ng-if="configuration.useMetadataIntervalPatterns == false "> <label translate class="tb-title no-padding">tb.rulenode.start-interval-time-unit</label> <md-select required name=startIntervalTimeUnit aria-label="{{ \'tb.rulenode.start-interval-time-unit\' | translate }}" ng-model=configuration.startIntervalTimeUnit> <md-option ng-repeat="timeUnit in ruleNodeTypes.timeUnit" ng-value=timeUnit.value> {{timeUnit.name | translate}} </md-option> </md-select> </md-input-container> </div> <div layout=column layout-gt-sm=row> <md-input-container flex class="md-block tb-time-value" flex ng-if="configuration.useMetadataIntervalPatterns == false"> <label translate class="tb-title no-padding">tb.rulenode.end-interval</label> <input required type=number step=1 min=1 max=2147483647 name=endInterval ng-model=configuration.endInterval> <div ng-messages=getTelemetryConfigForm.endInterval.$error> <div translate ng-message=required>tb.rulenode.end-interval-value-required</div> <div ng-message=min translate>tb.rulenode.time-value-range</div> <div ng-message=max translate>tb.rulenode.time-value-range</div> </div> </md-input-container> <md-input-container flex class="md-block tb-time-unit" flex ng-if="configuration.useMetadataIntervalPatterns === false"> <label translate class="tb-title no-padding">tb.rulenode.end-interval-time-unit</label> <md-select required name=endIntervalTimeUnit aria-label="{{ \'tb.rulenode.end-interval-time-unit\' | translate }}" ng-model=configuration.endIntervalTimeUnit> <md-option ng-repeat="timeUnit in ruleNodeTypes.timeUnit" ng-value=timeUnit.value> {{timeUnit.name | translate}} </md-option> </md-select> </md-input-container> </div> <md-input-container class=md-block flex ng-if="configuration.useMetadataIntervalPatterns === true" style=margin-top:38px> <label translate>tb.rulenode.start-interval-pattern</label> <input ng-required=true name=startIntervalPattern ng-model=configuration.startIntervalPattern> <div ng-messages=getTelemetryConfigForm.startIntervalPattern.$error> <div ng-message=required translate>tb.rulenode.start-interval-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.start-interval-pattern-hint</div> </md-input-container> <md-input-container class=md-block flex ng-if="configuration.useMetadataIntervalPatterns === true"> <label translate>tb.rulenode.end-interval-pattern</label> <input ng-required=true name=endIntervalPattern ng-model=configuration.endIntervalPattern> <div ng-messages=getTelemetryConfigForm.endIntervalPattern.$error> <div ng-message=required translate>tb.rulenode.end-interval-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.end-interval-pattern-hint</div> </md-input-container> </section>'},function(e,t){e.exports=' <section layout=column> <md-checkbox aria-label="{{ \'tb.rulenode.tell-failure-if-absent\' | translate }}" ng-model=configuration.tellFailureIfAbsent> {{ \'tb.rulenode.tell-failure-if-absent\' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.tell-failure-if-absent-hint</div> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys md-add-on-blur=true> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys md-add-on-blur=true> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys md-add-on-blur=true> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys md-add-on-blur=true> </md-chips> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title tb-required">tb.rulenode.fields-mapping</label> <tb-kv-map-config ng-model=configuration.fieldsMapping ng-required=true required-text="\'tb.rulenode.fields-mapping-required\'" key-text="\'tb.rulenode.source-field\'" key-required-text="\'tb.rulenode.source-field-required\'" val-text="\'tb.rulenode.target-attribute\'" val-required-text="\'tb.rulenode.target-attribute-required\'"> </tb-kv-map-config> </section> ';
3   -},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> "},31,function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding tb-required">tb.rulenode.data-keys</label> <md-chips style=padding-bottom:15px ng-required=!(configuration.metadataNames).length readonly=readonly ng-model=configuration.messageNames placeholder="{{\'tb.rulenode.data-keys\' | translate}}" md-separator-keys=separatorKeys md-add-on-blur=true> </md-chips> <div class=tb-hint translate>tb.rulenode.separator-hint</div> <label translate class="tb-title no-padding tb-required">tb.rulenode.metadata-keys</label> <md-chips style=padding-bottom:15px ng-required=!(configuration.messageNames).length readonly=readonly ng-model=configuration.metadataNames placeholder="{{\'tb.rulenode.metadata-keys\' | translate}}" md-separator-keys=separatorKeys md-add-on-blur=true> </md-chips> <div class=tb-hint translate>tb.rulenode.separator-hint</div> <md-checkbox aria-label="{{ \'tb.rulenode.check-all-keys\' | translate }}" ng-model=configuration.checkAllKeys>{{ \'tb.rulenode.check-all-keys\' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.check-all-keys-hint</div> </section> '},function(e,t){e.exports=" <section ng-form name=checkRelationConfigForm> <md-checkbox aria-label=\"{{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}\" ng-model=configuration.checkForSingleEntity> {{ 'tb.rulenode.check-relation-to-specific-entity' | translate }} </md-checkbox> <div class=tb-hint translate>tb.rulenode.check-relation-hint</div> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=configuration.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <div layout=row class=tb-entity-select ng-if=configuration.checkForSingleEntity style=padding-top:20px> <tb-entity-type-select style=min-width:100px;padding-bottom:20px the-form=checkRelationConfigForm tb-required=true ng-model=configuration.entityType> </tb-entity-type-select> <tb-entity-autocomplete flex ng-if=configuration.entityType the-form=checkRelationConfigForm tb-required=true entity-type=configuration.entityType ng-model=configuration.entityId> </tb-entity-autocomplete> </div> <tb-relation-type-autocomplete hide-label ng-model=configuration.relationType tb-required=true> </tb-relation-type-autocomplete> </section> "},function(e,t){e.exports=' <section ng-form name=geoFilterConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.latitude-key-name</label> <input ng-required=true name=latitudeKeyName ng-model=configuration.latitudeKeyName> <div ng-messages=geoFilterConfigForm.latitudeKeyName.$error> <div ng-message=required translate>tb.rulenode.latitude-key-name-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.longitude-key-name</label> <input ng-required=true name=longitudeKeyName ng-model=configuration.longitudeKeyName> <div ng-messages=geoFilterConfigForm.longitudeKeyName.$error> <div ng-message=required translate>tb.rulenode.longitude-key-name-required</div> </div> </md-input-container> <md-checkbox flex aria-label="{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}" ng-model=configuration.fetchPerimeterInfoFromMessageMetadata>{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }} </md-checkbox> <div layout=row class=tb-entity-select ng-if="configuration.fetchPerimeterInfoFromMessageMetadata === false"> <md-input-container class=md-block flex=100> <label translate>tb.rulenode.perimeter-type</label> <md-select required ng-model=configuration.perimeterType flex> <md-option ng-repeat="type in ruleNodeTypes.perimeterType" ng-value=type.value> {{ type.name | translate}} </md-option> </md-select> </md-input-container> </div> <div layout=row layout-wrap ng-if="configuration.perimeterType === ruleNodeTypes.perimeterType.CIRCLE.value && configuration.fetchPerimeterInfoFromMessageMetadata === false"> <div layout=column flex=50> <md-input-container class=md-block flex layout=column style=margin-top:44px> <label translate>tb.rulenode.circle-center-latitude</label> <input type=number min=0 step=0.1 ng-required=true name=centerLatitude ng-model=configuration.centerLatitude> <div ng-messages=geoFilterConfigForm.centerLatitude.$error> <div ng-message=required translate>tb.rulenode.circle-center-latitude-required</div> </div> </md-input-container> </div> <div layout=column flex=50> <md-input-container class=md-block flex style=margin-top:44px> <label translate>tb.rulenode.circle-center-longitude</label> <input type=number min=0 step=0.1 ng-required=true name=centerLongitude ng-model=configuration.centerLongitude> <div ng-messages=geoFilterConfigForm.centerLongitude.$error> <div ng-message=required translate>tb.rulenode.circle-center-longitude-required</div> </div> </md-input-container> </div> <div layout=column flex=50> <md-input-container class=md-block style=margin-top:28px> <label translate>tb.rulenode.range</label> <input type=number min=0 step=0.1 ng-required=true name=range ng-model=configuration.range> <div ng-messages=geoFilterConfigForm.range.$error> <div ng-message=required translate>tb.rulenode.range-required</div> </div> </md-input-container> </div> <div layout=column flex=50> <md-input-container class=md-block style=margin-top:28px> <label translate>tb.rulenode.range-units</label> <md-select required ng-model=configuration.rangeUnit> <md-option ng-repeat="type in ruleNodeTypes.rangeUnit" ng-value=type.value> {{ type.name | translate}} </md-option> </md-select> </md-input-container> </div> </div> <div layout=row layout-wrap ng-if="configuration.perimeterType === ruleNodeTypes.perimeterType.POLYGON.value && configuration.fetchPerimeterInfoFromMessageMetadata === false"> <div layout=column flex=100> <md-input-container class=md-block style=margin-top:44px> <label translate>tb.rulenode.polygon-definition</label> <input ng-required=true name=polygonsDefinition ng-model=configuration.polygonsDefinition> <div ng-messages=geoFilterConfigForm.polygonsDefinition.$error> <div ng-message=required translate>tb.rulenode.polygon-definition-required</div> </div> <div class=tb-hint style=margin-top:5px translate>tb.rulenode.polygon-definition-hint</div> </md-input-container> </div> </div> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" ng-class="{\'tb-required\': required}">tb.rulenode.message-types-filter</label> <md-chips id=message_type_chips ng-required=required readonly=readonly ng-model=messageTypes md-autocomplete-snap md-transform-chip=transformMessageTypeChip($chip) md-require-match=false> <md-autocomplete id=message_type md-no-cache=true md-selected-item=selectedMessageType md-search-text=messageTypeSearchText md-items="item in messageTypesSearch(messageTypeSearchText)" md-item-text=item.name md-min-length=0 placeholder="{{\'tb.rulenode.message-type\' | translate }}" md-menu-class=tb-message-type-autocomplete> <span md-highlight-text=messageTypeSearchText md-highlight-flags=^i>{{item}}</span> <md-not-found> <div class=tb-not-found> <div class=tb-no-entries ng-if="!messageTypeSearchText || !messageTypeSearchText.length"> <span translate>tb.rulenode.no-message-types-found</span> </div> <div ng-if="messageTypeSearchText && messageTypeSearchText.length"> <span translate translate-values=\'{ messageType: "{{messageTypeSearchText | truncate:true:6:&apos;...&apos;}}" }\'>tb.rulenode.no-message-type-matching</span> <span> <a translate ng-click="createMessageType($event, \'#message_type_chips\')">tb.rulenode.create-new-message-type</a> </span> </div> </div> </md-not-found> </md-autocomplete> <md-chip-template> <span>{{$chip.name}}</span> </md-chip-template> </md-chips> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=messageTypes class=tb-error-message>tb.rulenode.message-types-required</div> </div> </section>'},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" class=required>tb.rulenode.originator-types-filter</label> <tb-entity-type-list flex ng-model=configuration.originatorTypes allowed-entity-types=allowedEntityTypes ignore-authority-filter=true tb-required=true> </tb-entity-type-list> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.filter</label> <tb-js-func ng-model=configuration.jsScript function-name=Filter function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-filter-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.switch</label> <tb-js-func ng-model=configuration.jsScript function-name=Switch function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-switch-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section class=tb-kv-map-config layout=column> <div class=header flex layout=row> <span class=cell flex translate>{{ keyText }}</span> <span class=cell flex translate>{{ valText }}</span> <span ng-show=!disabled style=width:52px>&nbsp</span> </div> <div class=body> <div class=row ng-form name=kvForm flex layout=row layout-align="start center" ng-repeat="keyVal in kvList track by $index"> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ keyText | translate }}" ng-required=true name=key ng-model=keyVal.key> <div ng-messages=kvForm.key.$error> <div translate ng-message=required>{{keyRequiredText}}</div> </div> </md-input-container> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ valText | translate }}" ng-required=true name=value ng-model=keyVal.value> <div ng-messages=kvForm.value.$error> <div translate ng-message=required>{{valRequiredText}}</div> </div> </md-input-container> <md-button ng-show=!disabled ng-disabled=loading class="md-icon-button md-primary" ng-click=removeKeyVal($index) aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.remove-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.delete\' | translate }}" class=material-icons> close </md-icon> </md-button> </div> </div> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=kvMap class=tb-error-message>{{requiredText}}</div> </div> <div> <md-button ng-show=!disabled ng-disabled=loading class="md-primary md-raised" ng-click=addKeyVal() aria-label="{{ \'action.add\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.add-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.add\' | translate }}" class=material-icons> add </md-icon> {{ \'action.add\' | translate }} </md-button> </div> </section> '},function(e,t){e.exports=" <section layout=column> <div layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder=\"{{ 'tb.rulenode.unlimited-level' | translate }}\" ng-model=query.maxLevel aria-label=\"{{ 'tb.rulenode.max-relation-level' | translate }}\"> </md-input-container> </div> <div class=md-caption style=padding-bottom:10px;color:rgba(0,0,0,.57) translate>relation.relation-filters</div> <tb-relation-filters ng-model=query.filters> </tb-relation-filters> </section> "},function(e,t){e.exports=' <section layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.originator-source</label> <md-select required ng-model=configuration.originatorSource> <md-option ng-repeat="source in ruleNodeTypes.originatorSource" ng-value=source.value> {{ source.name | translate}} </md-option> </md-select> </md-input-container> <section layout=column ng-if="configuration.originatorSource == ruleNodeTypes.originatorSource.RELATED.value"> <label translate class="tb-title tb-required">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> </section> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.transform</label> <tb-js-func ng-model=configuration.jsScript function-name=Transform function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-transformer-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section ng-form name=toEmailConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.from-template</label> <textarea ng-required=true name=fromTemplate ng-model=configuration.fromTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.fromTemplate.$error> <div ng-message=required translate>tb.rulenode.from-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.from-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.to-template</label> <textarea ng-required=true name=toTemplate ng-model=configuration.toTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.toTemplate.$error> <div ng-message=required translate>tb.rulenode.to-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.cc-template</label> <textarea name=ccTemplate ng-model=configuration.ccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.bcc-template</label> <textarea name=ccTemplate ng-model=configuration.bccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.subject-template</label> <textarea ng-required=true name=subjectTemplate ng-model=configuration.subjectTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.subjectTemplate.$error> <div ng-message=required translate>tb.rulenode.subject-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.subject-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.body-template</label> <textarea ng-required=true name=bodyTemplate ng-model=configuration.bodyTemplate rows=6></textarea> <div ng-messages=toEmailConfigForm.bodyTemplate.$error> <div ng-message=required translate>tb.rulenode.body-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.body-template-hint</div> </md-input-container> </section> "},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(6),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(7),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,i){var a=function(a,r,l,s){var d=o.default;r.html(d),a.types=n,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue},a.testDetailsBuildJs=function(e){var n=angular.copy(a.configuration.alarmDetailsBuildJs);i.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(8),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,i){var a=function(a,r,l,s){var d=o.default;r.html(d),a.types=n,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue},a.testDetailsBuildJs=function(e){var n=angular.copy(a.configuration.alarmDetailsBuildJs);i.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(9),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(10),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(11),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,i){var a=function(a,r,l,s){var d=o.default;r.html(d),a.types=n,a.originator=null,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue,a.configuration.originatorId&&a.configuration.originatorType?a.originator={id:a.configuration.originatorId,entityType:a.configuration.originatorType}:a.originator=null,a.$watch("originator",function(e,t){angular.equals(e,t)||(a.originator?(s.$viewValue.originatorId=a.originator.id,s.$viewValue.originatorType=a.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},a.testScript=function(e){var n=angular.copy(a.configuration.jsScript);i.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,s.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(1);var r=n(12),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(13),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(74),r=i(a),o=n(52),l=i(o),s=n(57),d=i(s),u=n(54),c=i(u),m=n(53),g=i(m),p=n(61),f=i(p),b=n(68),v=i(b),y=n(69),h=i(y),q=n(67),$=i(q),x=n(60),k=i(x),T=n(72),C=i(T),w=n(73),M=i(w),N=n(66),_=i(N),S=n(62),E=i(S),F=n(71),P=i(F),A=n(64),V=i(A),I=n(63),j=i(I),O=n(51),D=i(O),R=n(75),K=i(R),L=n(56),U=i(L),z=n(55),B=i(z),H=n(70),G=i(H),Y=n(58),Q=i(Y),J=n(65),W=i(J);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",r.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",h.default).directive("tbActionNodeRestApiCallConfig",$.default).directive("tbActionNodeKafkaConfig",k.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",_.default).directive("tbActionNodeMqttConfig",E.default).directive("tbActionNodeSendEmailConfig",P.default).directive("tbActionNodeMsgDelayConfig",V.default).directive("tbActionNodeMsgCountConfig",j.default).directive("tbActionNodeAssignToCustomerConfig",D.default).directive("tbActionNodeUnAssignToCustomerConfig",K.default).directive("tbActionNodeDeleteRelationConfig",U.default).directive("tbActionNodeCreateRelationConfig",B.default).directive("tbActionNodeCustomTableConfig",G.default).directive("tbActionNodeGpsGeofencingConfig",Q.default).directive("tbActionNodePubSubConfig",W.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(14),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},i.testScript=function(e){var a=angular.copy(i.configuration.jsScript);n.testNodeScript(e,a,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(15),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$mdExpansionPanel=t,i.ruleNodeTypes=n,i.credentialsTypeChanged=function(){var e=i.configuration.credentials.type;i.configuration.credentials={},i.configuration.credentials.type=e,i.updateValidity()},i.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){i.$apply(function(){if(n.target.result){l.$setDirty();var a=n.target.result;a&&a.length>0&&("caCert"==t&&(i.configuration.credentials.caCertFileName=e.name,i.configuration.credentials.caCert=a),"privateKey"==t&&(i.configuration.credentials.privateKeyFileName=e.name,i.configuration.credentials.privateKey=a),"Cert"==t&&(i.configuration.credentials.certFileName=e.name,i.configuration.credentials.cert=a)),i.updateValidity()}})},n.readAsText(e.file)},i.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(i.configuration.credentials.caCertFileName=null,i.configuration.credentials.caCert=null),"privateKey"==e&&(i.configuration.credentials.privateKeyFileName=null,i.configuration.credentials.privateKey=null),"Cert"==e&&(i.configuration.credentials.certFileName=null,i.configuration.credentials.cert=null),i.updateValidity()},i.updateValidity=function(){var e=!0,t=i.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:i}}a.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(2);var r=n(16),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(17),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(18),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.serviceAccountFileAdded=function(e){var t=new FileReader;t.onload=function(t){n.$apply(function(){if(t.target.result){r.$setDirty();var i=t.target.result;i&&i.length>0&&(n.configuration.serviceAccountKeyFileName=e.name,n.configuration.serviceAccountKey=i),n.updateValidity()}})},t.readAsText(e.file)},n.clearServiceAccountFile=function(){r.$setDirty(),n.configuration.serviceAccountKeyFileName=null,n.configuration.serviceAccountKey=null,n.updateValidity()},n.updateValidity=function(){var e=!0,t=n.configuration;t.serviceAccountKeyFileName&&t.serviceAccountKey||(e=!1),r.$setValidity("SAKey",e)},n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(19),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(20),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(21),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(22),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(23),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(24),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"
4   -},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(25),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(26),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(27),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(28),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(29),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(30),o=i(r)},function(e,t){"use strict";function n(e){var t=function(t,n,i,a){n.html("<div></div>"),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(31),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(32),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),n.entityDetailsList=[];for(var s in t.entityDetails){var d=s;n.entityDetailsList.push(d)}r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(33),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s);var d=186;i.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],i.ruleNodeTypes=n,i.aggPeriodTimeUnits={},i.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,i.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,i.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,i.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,i.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{},link:i}}a.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(34),o=i(r);n(3)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(83),r=i(a),o=n(84),l=i(o),s=n(79),d=i(s),u=n(85),c=i(u),m=n(78),g=i(m),p=n(86),f=i(p),b=n(81),v=i(b),y=n(80),h=i(y);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",r.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).directive("tbEnrichmentNodeEntityDetailsConfig",h.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(35),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(36),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(37),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(38),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(39),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(40),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(41),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(93),r=i(a),o=n(91),l=i(o),s=n(94),d=i(s),u=n(88),c=i(u),m=n(92),g=i(m),p=n(87),f=i(p),b=n(89),v=i(b);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",r.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).directive("tbFilterNodeGpsGeofencingConfig",v.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){function s(){if(l.$viewValue){for(var e=[],t=0;t<i.messageTypes.length;t++)e.push(i.messageTypes[t].value);l.$viewValue.messageTypes=e,d()}}function d(){if(i.required){var e=!(!l.$viewValue.messageTypes||!l.$viewValue.messageTypes.length);l.$setValidity("messageTypes",e)}else l.$setValidity("messageTypes",!0)}var u=o.default;a.html(u),i.selectedMessageType=null,i.messageTypeSearchText=null,i.ngModelCtrl=l;var c=[];for(var m in n.messageType){var g={name:n.messageType[m].name,value:n.messageType[m].value};c.push(g)}i.transformMessageTypeChip=function(e){var n,i=t("filter")(c,{name:e},!0);return n=i&&i.length?angular.copy(i[0]):{name:e,value:e}},i.messageTypesSearch=function(e){var n=e?t("filter")(c,{name:e}):c;return n.map(function(e){return e.name})},i.createMessageType=function(e,t){var n=angular.element(t,a)[0].firstElementChild,i=angular.element(n),r=i.scope().$mdChipsCtrl.getChipBuffer();e.preventDefault(),e.stopPropagation(),i.scope().$mdChipsCtrl.appendChip(r.trim()),i.scope().$mdChipsCtrl.resetChipBuffer()},l.$render=function(){i.messageTypesWatch&&(i.messageTypesWatch(),i.messageTypesWatch=null);var e=l.$viewValue,t=[];if(e&&e.messageTypes)for(var a=0;a<e.messageTypes.length;a++){var r=e.messageTypes[a];n.messageType[r]?t.push(angular.copy(n.messageType[r])):t.push({name:r,value:r})}i.messageTypes=t,i.messageTypesWatch=i.$watch("messageTypes",function(e,t){angular.equals(e,t)||s()},!0)},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:i}}a.$inject=["$compile","$filter","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(4);var r=n(42),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.allowedEntityTypes=[t.entityType.device,t.entityType.asset,t.entityType.entityView,t.entityType.tenant,t.entityType.customer,t.entityType.user,t.entityType.dashboard,t.entityType.rulechain,t.entityType.rulenode],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(43),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},i.testScript=function(e){var a=angular.copy(i.configuration.jsScript);n.testNodeScript(e,a,"filter",t.instant("tb.rulenode.filter")+"","Filter",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(44),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},i.testScript=function(e){var a=angular.copy(i.configuration.jsScript);n.testNodeScript(e,a,"switch",t.instant("tb.rulenode.switch")+"","Switch",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(45),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){function r(e){e>-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),a.$setViewValue(e),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),a.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=a,t.removeKeyVal=r,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||a.$setViewValue(t.query)}),a.$render=function(){if(a.$viewValue){var e=a.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),d()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(46),o=i(r);n(5)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(47),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(48),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(97),r=i(a),o=n(99),l=i(o),s=n(100),d=i(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",r.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},i.testScript=function(e){var a=angular.copy(i.configuration.jsScript);n.testNodeScript(e,a,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(49),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(50),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(104),r=i(a),o=n(90),l=i(o),s=n(82),d=i(s),u=n(98),c=i(u),m=n(59),g=i(m),p=n(77),f=i(p),b=n(96),v=i(b),y=n(76),h=i(y),q=n(95),$=i(q),x=n(103),k=i(x);t.default=angular.module("thingsboard.ruleChain.config",[r.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",h.default).directive("tbKvMapConfig",$.default).config(k.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","originator-alarm-originator":"Alarm Originator","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","use-metadata-period-in-seconds-patterns":"Use metadata period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds metadata pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","period-in-seconds-pattern-hint":"Period in seconds pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use <code>${metaKeyName}</code> to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use <code>${metaKeyName}</code> to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use <code>${metaKeyName}</code> to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use <code>${metaKeyName}</code> to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory",headers:"Headers","headers-hint":"Use <code>${metaKeyName}</code> in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","message-attributes":"Message attributes","message-attributes-hint":"Use <code>${metaKeyName}</code> in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.",
5   -"start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.',"entity-details":"Select entity details:","entity-details-country":"Country","entity-details-state":"State","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","add-to-metadata":"Add selected details to message metadata","add-to-metadata-hint":"If selected, adds the selected details keys to the message metadata instead of message data.","entity-details-list-empty":"No entity details selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"You should enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-key-name":"Latitude key name","longitude-key-name":"Longitude key name","latitude-key-name-required":"Latitude key name is required.","longitude-key-name-required":"Longitude key name is required.","fetch-perimeter-info-from-message-metadata":"Fetch perimeter information from message metadata","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Please, use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit","tell-failure-if-absent":"Tell Failure","tell-failure-if-absent-hint":'If at least one selected key doesn\'t exist the outbound message will report "Failure".'},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){(0,o.default)(e)}a.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(102),o=i(r)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"},ALARM_ORIGINATOR:{name:"tb.rulenode.originator-alarm-originator",value:"ALARM_ORIGINATOR"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],entityDetails:{COUNTRY:{name:"tb.rulenode.entity-details-country",value:"COUNTRY"},STATE:{name:"tb.rulenode.entity-details-state",value:"STATE"},ZIP:{name:"tb.rulenode.entity-details-zip",value:"ZIP"},ADDRESS:{name:"tb.rulenode.entity-details-address",value:"ADDRESS"},ADDRESS2:{name:"tb.rulenode.entity-details-address2",value:"ADDRESS2"},PHONE:{name:"tb.rulenode.entity-details-phone",value:"PHONE"},EMAIL:{name:"tb.rulenode.entity-details-email",value:"EMAIL"},ADDITIONAL_INFO:{name:"tb.rulenode.entity-details-additional_info",value:"ADDITIONAL_INFO"}},sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},perimeterType:{CIRCLE:{name:"tb.rulenode.perimeter-circle",value:"CIRCLE"},POLYGON:{name:"tb.rulenode.perimeter-polygon",value:"POLYGON"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},rangeUnit:{METER:{value:"METER",name:"tb.rulenode.range-unit-meter"},KILOMETER:{value:"KILOMETER",name:"tb.rulenode.range-unit-kilometer"},FOOT:{value:"FOOT",name:"tb.rulenode.range-unit-foot"},MILE:{value:"MILE",name:"tb.rulenode.range-unit-mile"},NAUTICAL_MILE:{value:"NAUTICAL_MILE",name:"tb.rulenode.range-unit-nautical-mile"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}]));
6   -//# sourceMappingURL=rulenode-core-config.js.map
\ No newline at end of file
  1 +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@angular/core"),require("@angular/common"),require("@ngx-translate/core"),require("@shared/public-api"),require("@ngrx/store"),require("@angular/forms")):"function"==typeof define&&define.amd?define("rulenode-core-config",["exports","@angular/core","@angular/common","@ngx-translate/core","@shared/public-api","@ngrx/store","@angular/forms"],t):t((e=e||self)["rulenode-core-config"]={},e.ng.core,e.ng.common,e["ngx-translate"],e.shared,e["ngrx-store"],e.ng.forms)}(this,(function(e,t,r,i,a,n,s){"use strict";
  2 +/*! *****************************************************************************
  3 + Copyright (c) Microsoft Corporation. All rights reserved.
  4 + Licensed under the Apache License, Version 2.0 (the "License"); you may not use
  5 + this file except in compliance with the License. You may obtain a copy of the
  6 + License at http://www.apache.org/licenses/LICENSE-2.0
  7 +
  8 + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  9 + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
  10 + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  11 + MERCHANTABLITY OR NON-INFRINGEMENT.
  12 +
  13 + See the Apache Version 2.0 License for specific language governing permissions
  14 + and limitations under the License.
  15 + ***************************************************************************** */var o=function(e,t){return(o=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)};function u(e,t){function r(){this.constructor=e}o(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}var d=function(e){function r(t){var r=e.call(this,t)||this;return r.store=t,r}return u(r,e),r.prototype.ngOnInit=function(){e.prototype.ngOnInit.call(this)},r.prototype.onConfigurationSet=function(e){},r.decorators=[{type:t.Component,args:[{selector:"tb-node-empty-config",template:"<div></div>"}]}],r.ctorParameters=function(){return[{type:n.Store}]},r}(a.RuleNodeConfigurationComponent);var l=function(e){function r(t,r){var i=e.call(this,t)||this;return i.store=t,i.fb=r,i.attributeScopes=Object.keys(a.AttributeScope),i.telemetryTypeTranslationsMap=a.telemetryTypeTranslations,i}return u(r,e),r.prototype.ngOnInit=function(){e.prototype.ngOnInit.call(this)},r.prototype.onConfigurationSet=function(e){var t=this;this.attributesConfigForm=this.fb.group({scope:[e?e.scope:null,[s.Validators.required]]}),this.attributesConfigForm.valueChanges.subscribe((function(e){t.attributesConfigForm.valid?t.notifyConfigurationUpdated(e):t.notifyConfigurationUpdated(null)}))},r.decorators=[{type:t.Component,args:[{selector:"tb-action-node-attributes-config",template:'<section [formGroup]="attributesConfigForm" fxLayout="column">\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>attribute.attributes-scope</mat-label>\n <mat-select formControlName="scope" required>\n <mat-option *ngFor="let scope of attributeScopes" [value]="scope">\n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n</section>\n'}]}],r.ctorParameters=function(){return[{type:n.Store},{type:s.FormBuilder}]},r}(a.RuleNodeConfigurationComponent);var m=function(e){function r(t,r){var i=e.call(this,t)||this;return i.store=t,i.fb=r,i}return u(r,e),r.prototype.ngOnInit=function(){e.prototype.ngOnInit.call(this)},r.prototype.onConfigurationSet=function(e){var t=this;this.timeseriesConfigForm=this.fb.group({defaultTTL:[e?e.defaultTTL:null,[s.Validators.required,s.Validators.min(0)]]}),this.timeseriesConfigForm.valueChanges.subscribe((function(e){t.timeseriesConfigForm.valid?t.notifyConfigurationUpdated(e):t.notifyConfigurationUpdated(null)}))},r.decorators=[{type:t.Component,args:[{selector:"tb-action-node-timeseries-config",template:'<section [formGroup]="timeseriesConfigForm" fxLayout="column">\n <mat-form-field fxFlex class="mat-block">\n <mat-label translate>tb.rulenode.default-ttl</mat-label>\n <input type="number" min="0" step="1" matInput formControlName="defaultTTL" required>\n <mat-error *ngIf="timeseriesConfigForm.get(\'defaultTTL\').hasError(\'required\')">\n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n </mat-error>\n <mat-error *ngIf="timeseriesConfigForm.get(\'defaultTTL\').hasError(\'min\')">\n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n </mat-error>\n </mat-form-field>\n</section>\n'}]}],r.ctorParameters=function(){return[{type:n.Store},{type:s.FormBuilder}]},r}(a.RuleNodeConfigurationComponent);var c=function(){function e(){}return e.decorators=[{type:t.NgModule,args:[{declarations:[l,m],imports:[r.CommonModule,a.SharedModule],exports:[l,m]}]}],e}(),p=function(){function e(e){!function(e){e.setTranslation("en_US",{tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","originator-alarm-originator":"Alarm Originator","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","use-metadata-period-in-seconds-patterns":"Use metadata period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds metadata pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","period-in-seconds-pattern-hint":"Period in seconds pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use <code>${metaKeyName}</code> to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use <code>${metaKeyName}</code> to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use <code>${metaKeyName}</code> to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use <code>${metaKeyName}</code> to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory",headers:"Headers","headers-hint":"Use <code>${metaKeyName}</code> in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","message-attributes":"Message attributes","message-attributes-hint":"Use <code>${metaKeyName}</code> in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.',"entity-details":"Select entity details:","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","add-to-metadata":"Add selected details to message metadata","add-to-metadata-hint":"If selected, adds the selected details keys to the message metadata instead of message data.","entity-details-list-empty":"No entity details selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"You should enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-key-name":"Latitude key name","longitude-key-name":"Longitude key name","latitude-key-name-required":"Latitude key name is required.","longitude-key-name-required":"Longitude key name is required.","fetch-perimeter-info-from-message-metadata":"Fetch perimeter information from message metadata","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Please, use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit","tell-failure-if-absent":"Tell Failure","tell-failure-if-absent-hint":'If at least one selected key doesn\'t exist the outbound message will report "Failure".'},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}},!0)}(e)}return e.decorators=[{type:t.NgModule,args:[{declarations:[d],imports:[r.CommonModule,a.SharedModule],exports:[c,d]}]}],e.ctorParameters=function(){return[{type:i.TranslateService}]},e}();e.RuleNodeCoreConfigModule=p,e.default=p,e.ɵa=d,e.ɵb=c,e.ɵc=l,e.ɵd=m,Object.defineProperty(e,"__esModule",{value:!0})}));
  16 +//# sourceMappingURL=rulenode-core-config.umd.min.js.map
\ No newline at end of file
... ...
... ... @@ -78,7 +78,8 @@
78 78 "node_modules/ace-builds/src-min/snippets/css.js",
79 79 "node_modules/ace-builds/src-min/snippets/json.js",
80 80 "node_modules/ace-builds/src-min/snippets/java.js",
81   - "node_modules/ace-builds/src-min/snippets/javascript.js"
  81 + "node_modules/ace-builds/src-min/snippets/javascript.js",
  82 + "node_modules/systemjs/dist/system.js"
82 83 ],
83 84 "es5BrowserSupport": true,
84 85 "customWebpackConfig": {
... ... @@ -116,7 +117,7 @@
116 117 "builder": "@angular-builders/custom-webpack:dev-server",
117 118 "options": {
118 119 "browserTarget": "thingsboard:build",
119   - "proxyConfig": "proxy.conf.json"
  120 + "proxyConfig": "proxy.conf.js"
120 121 },
121 122 "configurations": {
122 123 "production": {
... ...
... ... @@ -12624,6 +12624,11 @@
12624 12624 "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
12625 12625 "dev": true
12626 12626 },
  12627 + "systemjs": {
  12628 + "version": "0.21.5",
  12629 + "resolved": "https://registry.npmjs.org/systemjs/-/systemjs-0.21.5.tgz",
  12630 + "integrity": "sha512-GWzZhN/x7Fsae2CYkz2GF7OgOS+YDgKulcgd5L1kTogZHMKDrPx5T8zI8I0y5RoU9Dx78Z7j1XMfuFa1thD84A=="
  12631 + },
12627 12632 "tapable": {
12628 12633 "version": "1.1.3",
12629 12634 "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
... ...
... ... @@ -74,6 +74,7 @@
74 74 "schema-inspector": "^1.6.8",
75 75 "screenfull": "^5.0.0",
76 76 "split.js": "^1.5.11",
  77 + "systemjs": "0.21.5",
77 78 "tinycolor2": "^1.4.1",
78 79 "tooltipster": "^4.2.7",
79 80 "tslib": "^1.10.0",
... ...
  1 +/*
  2 + * Copyright © 2016-2019 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 +const ruleNodeUiforwardHost = 'localhost';
  18 +const ruleNodeUiforwardPort = 8080;
  19 +
  20 +const PROXY_CONFIG = {
  21 + '/api': {
  22 + 'target': 'http://localhost:8080',
  23 + 'secure': false
  24 + },
  25 + '/static/rulenode': {
  26 + 'target': `http://${ruleNodeUiforwardHost}:${ruleNodeUiforwardPort}`,
  27 + 'secure': false
  28 + },
  29 + '/api/ws': {
  30 + 'target': 'ws://localhost:8080',
  31 + 'ws': true
  32 + }
  33 +}
  34 +
  35 +module.exports = PROXY_CONFIG;
... ...
  1 +///
  2 +/// Copyright © 2016-2019 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 +export * from './alias-controller';
  18 +export * from './data-aggregator';
  19 +export * from './datasource.service';
  20 +export * from './datasource-subcription';
  21 +export * from './widget-api.models';
  22 +export * from './widget-subscription';
... ...
... ... @@ -25,6 +25,7 @@ import {WINDOW} from '@core/services/window.service';
25 25 import { ActivationEnd, NavigationEnd, Router } from '@angular/router';
26 26 import { filter, map, publishReplay, refCount } from 'rxjs/operators';
27 27
  28 +// @dynamic
28 29 @Injectable({
29 30 providedIn: 'root'
30 31 })
... ...
  1 +///
  2 +/// Copyright © 2016-2019 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 +export * from './admin.service';
  18 +export * from './alarm.service';
  19 +export * from './asset.service';
  20 +export * from './attribute.service';
  21 +export * from './audit-log.service';
  22 +export * from './component-descriptor.service';
  23 +export * from './customer.service';
  24 +export * from './dashboard.service';
  25 +export * from './device.service';
  26 +export * from './entity.service';
  27 +export * from './entity-relation.service';
  28 +export * from './entity-view.service';
  29 +export * from './event.service';
  30 +export * from './http-utils';
  31 +export * from './rule-chain.service';
  32 +export * from './tenant.service';
  33 +export * from './user.service';
  34 +export * from './widget.service';
... ...
... ... @@ -14,7 +14,7 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import { Injectable } from '@angular/core';
  17 +import { ComponentFactory, Injectable } from '@angular/core';
18 18 import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
19 19 import { forkJoin, Observable, of } from 'rxjs/index';
20 20 import { HttpClient } from '@angular/common/http';
... ... @@ -28,12 +28,16 @@ import {
28 28 ruleNodeTypeComponentTypes, unknownNodeComponent
29 29 } from '@shared/models/rule-chain.models';
30 30 import { ComponentDescriptorService } from './component-descriptor.service';
31   -import { LinkLabel, RuleNodeComponentDescriptor } from '@app/shared/models/rule-node.models';
  31 +import {
  32 + IRuleNodeConfigurationComponent,
  33 + LinkLabel,
  34 + RuleNodeComponentDescriptor
  35 +} from '@app/shared/models/rule-node.models';
32 36 import { ResourcesService } from '../services/resources.service';
33 37 import { catchError, map, mergeMap } from 'rxjs/operators';
34 38 import { TranslateService } from '@ngx-translate/core';
35 39 import { EntityType } from '@shared/models/entity-type.models';
36   -import { deepClone } from '@core/utils';
  40 +import { deepClone, snakeCase } from '@core/utils';
37 41
38 42 @Injectable({
39 43 providedIn: 'root'
... ... @@ -41,6 +45,7 @@ import { deepClone } from '@core/utils';
41 45 export class RuleChainService {
42 46
43 47 private ruleNodeComponents: Array<RuleNodeComponentDescriptor>;
  48 + private ruleNodeConfigFactories: {[directive: string]: ComponentFactory<IRuleNodeConfigurationComponent>} = {};
44 49
45 50 constructor(
46 51 private http: HttpClient,
... ... @@ -105,13 +110,14 @@ export class RuleChainService {
105 110 );
106 111 }
107 112
108   - public getRuleNodeComponents(config?: RequestConfig): Observable<Array<RuleNodeComponentDescriptor>> {
  113 + public getRuleNodeComponents(ruleNodeConfigResourcesModulesMap: {[key: string]: any}, config?: RequestConfig):
  114 + Observable<Array<RuleNodeComponentDescriptor>> {
109 115 if (this.ruleNodeComponents) {
110 116 return of(this.ruleNodeComponents);
111 117 } else {
112 118 return this.loadRuleNodeComponents(config).pipe(
113 119 mergeMap((components) => {
114   - return this.resolveRuleNodeComponentsUiResources(components).pipe(
  120 + return this.resolveRuleNodeComponentsUiResources(components, ruleNodeConfigResourcesModulesMap).pipe(
115 121 map((ruleNodeComponents) => {
116 122 this.ruleNodeComponents = ruleNodeComponents;
117 123 this.ruleNodeComponents.push(ruleChainNodeComponent);
... ... @@ -132,6 +138,10 @@ export class RuleChainService {
132 138 }
133 139 }
134 140
  141 + public getRuleNodeConfigFactory(directive: string): ComponentFactory<IRuleNodeConfigurationComponent> {
  142 + return this.ruleNodeConfigFactories[directive];
  143 + }
  144 +
135 145 public getRuleNodeComponentByClazz(clazz: string): RuleNodeComponentDescriptor {
136 146 const found = this.ruleNodeComponents.filter((component) => component.clazz === clazz);
137 147 if (found && found.length) {
... ... @@ -192,11 +202,12 @@ export class RuleChainService {
192 202 );
193 203 }
194 204
195   - private resolveRuleNodeComponentsUiResources(components: Array<RuleNodeComponentDescriptor>):
  205 + private resolveRuleNodeComponentsUiResources(components: Array<RuleNodeComponentDescriptor>,
  206 + ruleNodeConfigResourcesModulesMap: {[key: string]: any}):
196 207 Observable<Array<RuleNodeComponentDescriptor>> {
197 208 const tasks: Observable<RuleNodeComponentDescriptor>[] = [];
198 209 components.forEach((component) => {
199   - tasks.push(this.resolveRuleNodeComponentUiResources(component));
  210 + tasks.push(this.resolveRuleNodeComponentUiResources(component, ruleNodeConfigResourcesModulesMap));
200 211 });
201 212 return forkJoin(tasks).pipe(
202 213 catchError((err) => {
... ... @@ -205,13 +216,39 @@ export class RuleChainService {
205 216 );
206 217 }
207 218
208   - private resolveRuleNodeComponentUiResources(component: RuleNodeComponentDescriptor): Observable<RuleNodeComponentDescriptor> {
209   - const uiResources = component.configurationDescriptor.nodeDefinition.uiResources;
  219 + private resolveRuleNodeComponentUiResources(component: RuleNodeComponentDescriptor,
  220 + ruleNodeConfigResourcesModulesMap: {[key: string]: any}):
  221 + Observable<RuleNodeComponentDescriptor> {
  222 + const nodeDefinition = component.configurationDescriptor.nodeDefinition;
  223 + const uiResources = nodeDefinition.uiResources;
210 224 if (uiResources && uiResources.length) {
  225 + const commonResources = uiResources.filter((resource) => !resource.endsWith('.js'));
  226 + const moduleResource = uiResources.find((resource) => resource.endsWith('.js'));
211 227 const tasks: Observable<any>[] = [];
212   - uiResources.forEach((uiResource) => {
213   - tasks.push(this.resourcesService.loadResource(uiResource));
214   - });
  228 + if (commonResources && commonResources.length) {
  229 + commonResources.forEach((resource) => {
  230 + tasks.push(this.resourcesService.loadResource(resource));
  231 + });
  232 + }
  233 + if (moduleResource) {
  234 + tasks.push(this.resourcesService.loadModule(moduleResource, ruleNodeConfigResourcesModulesMap).pipe(
  235 + map((res) => {
  236 + if (nodeDefinition.configDirective && nodeDefinition.configDirective.length) {
  237 + const selector = snakeCase(nodeDefinition.configDirective, '-');
  238 + const componentFactory = res.componentFactories.find((factory) =>
  239 + factory.selector === selector);
  240 + if (componentFactory) {
  241 + this.ruleNodeConfigFactories[nodeDefinition.configDirective] = componentFactory;
  242 + } else {
  243 + component.configurationDescriptor.nodeDefinition.uiResourceLoadError =
  244 + this.translate.instant('rulenode.directive-is-not-loaded',
  245 + {directiveName: nodeDefinition.configDirective});
  246 + }
  247 + }
  248 + return of(component);
  249 + })
  250 + ));
  251 + }
215 252 return forkJoin(tasks).pipe(
216 253 map((res) => {
217 254 return component;
... ... @@ -231,7 +268,7 @@ export class RuleChainService {
231 268 map(ruleChain => ruleChain),
232 269 catchError((err) => {
233 270 const ruleChain = {
234   - id: {
  271 + id: {
235 272 entityType: EntityType.RULE_CHAIN,
236 273 id: ruleChainId
237 274 }
... ...
... ... @@ -18,6 +18,7 @@ import { Injectable } from '@angular/core';
18 18
19 19 const APP_PREFIX = 'TB-';
20 20
  21 +// @dynamic
21 22 @Injectable(
22 23 {
23 24 providedIn: 'root'
... ...
  1 +///
  2 +/// Copyright © 2016-2019 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 +export * from './api/public-api';
  18 +export * from './http/public-api';
  19 +export * from './local-storage/local-storage.service';
  20 +export * from './services/public-api';
  21 +export * from './ws/telemetry-websocket.service';
  22 +export * from './core.state';
  23 +export * from './core.module';
... ...
... ... @@ -24,6 +24,7 @@ export interface ConfirmDialogData {
24 24 ok: string;
25 25 }
26 26
  27 +// @dynamic
27 28 @Component({
28 29 selector: 'tb-confirm-dialog',
29 30 templateUrl: './confirm-dialog.component.html',
... ...
  1 +///
  2 +/// Copyright © 2016-2019 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 +export * from './script/node-script-test.service';
  18 +export * from './broadcast.models';
  19 +export * from './broadcast.service';
  20 +export * from './dashboard-utils.service';
  21 +export * from './dialog.service';
  22 +export * from './dynamic-component-factory.service';
  23 +export * from './item-buffer.service';
  24 +export * from './menu.models';
  25 +export * from './menu.service';
  26 +export * from './notification.service';
  27 +export * from './raf.service';
  28 +export * from './resources.service';
  29 +export * from './time.service';
  30 +export * from './title.service';
  31 +export * from './utils.service';
  32 +export * from './window.service';
... ...
... ... @@ -20,6 +20,7 @@ import { WINDOW } from '@core/services/window.service';
20 20
21 21 export type CancelAnimationFrame = () => void;
22 22
  23 +// @dynamic
23 24 @Injectable({
24 25 providedIn: 'root'
25 26 })
... ...
... ... @@ -14,20 +14,25 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import { Injectable, Inject } from '@angular/core';
  17 +import { Injectable, Inject, ModuleWithComponentFactories, Compiler, Injector } from '@angular/core';
18 18 import { DOCUMENT } from '@angular/common';
19 19 import { ReplaySubject, Observable, throwError } from 'rxjs';
20 20
  21 +declare const SystemJS;
  22 +
21 23 @Injectable({
22 24 providedIn: 'root'
23 25 })
24 26 export class ResourcesService {
25 27
26 28 private loadedResources: { [url: string]: ReplaySubject<any> } = {};
  29 + private loadedModules: { [url: string]: ReplaySubject<ModuleWithComponentFactories<any>> } = {};
27 30
28 31 private anchor = this.document.getElementsByTagName('head')[0] || this.document.getElementsByTagName('body')[0];
29 32
30   - constructor(@Inject(DOCUMENT) private readonly document: any) {}
  33 + constructor(@Inject(DOCUMENT) private readonly document: any,
  34 + private compiler: Compiler,
  35 + private injector: Injector) {}
31 36
32 37 public loadResource(url: string): Observable<any> {
33 38 if (this.loadedResources[url]) {
... ... @@ -47,6 +52,49 @@ export class ResourcesService {
47 52 return this.loadResourceByType(fileType, url);
48 53 }
49 54
  55 + public loadModule(url: string, modulesMap: {[key: string]: any}): Observable<ModuleWithComponentFactories<any>> {
  56 + if (this.loadedModules[url]) {
  57 + return this.loadedModules[url].asObservable();
  58 + }
  59 + const subject = new ReplaySubject<ModuleWithComponentFactories<any>>();
  60 + this.loadedModules[url] = subject;
  61 + if (modulesMap) {
  62 + for (const moduleId of Object.keys(modulesMap)) {
  63 + SystemJS.set(moduleId, modulesMap[moduleId]);
  64 + }
  65 + }
  66 + SystemJS.import(url).then(
  67 + (module) => {
  68 + if (module.default) {
  69 + this.compiler.compileModuleAndAllComponentsAsync(module.default).then(
  70 + (compiled) => {
  71 + try {
  72 + compiled.ngModuleFactory.create(this.injector);
  73 + this.loadedModules[url].next(compiled);
  74 + this.loadedModules[url].complete();
  75 + } catch (e) {
  76 + this.loadedModules[url].error(new Error(`Unable to init module from url: ${url}`));
  77 + delete this.loadedModules[url];
  78 + }
  79 + },
  80 + (e) => {
  81 + this.loadedModules[url].error(new Error(`Unable to compile module from url: ${url}`));
  82 + delete this.loadedModules[url];
  83 + }
  84 + );
  85 + } else {
  86 + this.loadedModules[url].error(new Error(`Module '${url}' doesn't have default export!`));
  87 + delete this.loadedModules[url];
  88 + }
  89 + },
  90 + (e) => {
  91 + this.loadedModules[url].error(new Error(`Unable to load module from url: ${url}`));
  92 + delete this.loadedModules[url];
  93 + }
  94 + );
  95 + return subject.asObservable();
  96 + }
  97 +
50 98 private loadResourceByType(type: 'css' | 'js', url: string): Observable<any> {
51 99 const subject = new ReplaySubject();
52 100 this.loadedResources[url] = subject;
... ...
  1 +///
  2 +/// Copyright © 2016-2019 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 +import { Injectable } from '@angular/core';
  18 +import { Observable, of } from 'rxjs';
  19 +
  20 +@Injectable({
  21 + providedIn: 'root'
  22 +})
  23 +export class NodeScriptTestService {
  24 +
  25 + testNodeScript(script: string, scriptType: any, functionTitle: string,
  26 + functionName: string, argNames: string[], ruleNodeId: string): Observable<string> {
  27 + console.log(`testNodeScript TODO: ${script}`);
  28 + return of(script);
  29 + }
  30 +
  31 +}
... ...
... ... @@ -14,6 +14,9 @@
14 14 /// limitations under the License.
15 15 ///
16 16
  17 +// tslint:disable-next-line:no-reference
  18 +/// <reference path="../../../../src/typings/rawloader.typings.d.ts" />
  19 +
17 20 import { Inject, Injectable, NgZone } from '@angular/core';
18 21 import { WINDOW } from '@core/services/window.service';
19 22 import { ExceptionData } from '@app/shared/models/error.models';
... ... @@ -67,6 +70,7 @@ const commonMaterialIcons: Array<string> = [ 'more_horiz', 'more_vert', 'open_in
67 70 'settings', 'notifications', 'notifications_active', 'info', 'info_outline', 'warning', 'list', 'file_download', 'import_export',
68 71 'share', 'add', 'edit', 'done' ];
69 72
  73 +// @dynamic
70 74 @Injectable({
71 75 providedIn: 'root'
72 76 })
... ...
... ... @@ -387,3 +387,12 @@ export function guid(): string {
387 387 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
388 388 s4() + '-' + s4() + s4() + s4();
389 389 }
  390 +
  391 +const SNAKE_CASE_REGEXP = /[A-Z]/g;
  392 +
  393 +export function snakeCase(name: string, separator: string): string {
  394 + separator = separator || '_';
  395 + return name.replace(SNAKE_CASE_REGEXP, (letter, pos) => {
  396 + return (pos ? separator : '') + letter.toLowerCase();
  397 + });
  398 +}
... ...
... ... @@ -40,6 +40,7 @@ const RECONNECT_INTERVAL = 2000;
40 40 const WS_IDLE_TIMEOUT = 90000;
41 41 const MAX_PUBLISH_COMMANDS = 10;
42 42
  43 +// @dynamic
43 44 @Injectable({
44 45 providedIn: 'root'
45 46 })
... ...
... ... @@ -30,6 +30,7 @@ import {
30 30 AliasesEntitySelectPanelData
31 31 } from './aliases-entity-select-panel.component';
32 32
  33 +// @dynamic
33 34 @Component({
34 35 selector: 'tb-aliases-entity-select',
35 36 templateUrl: './aliases-entity-select.component.html',
... ...
... ... @@ -73,7 +73,7 @@ export class EntityAliasDialogComponent extends DialogComponent<EntityAliasDialo
73 73 public dialogRef: MatDialogRef<EntityAliasDialogComponent, EntityAlias>,
74 74 private fb: FormBuilder,
75 75 private utils: UtilsService,
76   - private translate: TranslateService,
  76 + public translate: TranslateService,
77 77 private entityService: EntityService) {
78 78 super(store, router, dialogRef);
79 79 this.isAdd = data.isAdd;
... ...
... ... @@ -92,7 +92,7 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit,
92 92
93 93 filteredEntityAliases: Observable<Array<EntityAlias>>;
94 94
95   - private searchText = '';
  95 + searchText = '';
96 96
97 97 private dirty = false;
98 98
... ...
... ... @@ -42,7 +42,7 @@
42 42 <fieldset [disabled]="isLoading$ | async">
43 43 <div fxFlex fxLayout="row" fxLayoutAlign="start center"
44 44 formArrayName="entityAliases"
45   - *ngFor="let entityAliasControl of entityAliasesFormGroup.get('entityAliases').controls; let $index = index">
  45 + *ngFor="let entityAliasControl of entityAliasesFormArray().controls; let $index = index">
46 46 <span fxFlex="5">{{$index + 1}}.</span>
47 47 <div class="mat-elevation-z4 tb-alias" fxFlex="95" fxLayout="row" fxLayoutAlign="start center">
48 48 <mat-form-field floatLabel="always" hideRequiredMarker class="mat-block" fxFlex="150px">
... ...
... ... @@ -154,6 +154,11 @@ export class EntityAliasesDialogComponent extends DialogComponent<EntityAliasesD
154 154 return aliasFormControl;
155 155 }
156 156
  157 +
  158 + entityAliasesFormArray(): FormArray {
  159 + return this.entityAliasesFormGroup.get('entityAliases') as FormArray;
  160 + }
  161 +
157 162 ngOnInit(): void {
158 163 }
159 164
... ...
... ... @@ -112,7 +112,7 @@
112 112 [selectFirstBundle]="false"
113 113 [selectBundleAlias]="selectedWidgetsBundleAlias"
114 114 [(ngModel)]="widgetsBundle"
115   - (ngModelChange)="onWidgetsBundleChanged($event)">
  115 + (ngModelChange)="onWidgetsBundleChanged()">
116 116 </tb-widgets-bundle-select>
117 117 </div>
118 118 <button mat-button mat-raised-button [fxShow]="widgetsList.length > 0"
... ... @@ -195,7 +195,7 @@
195 195 <ngx-hm-carousel fxFlex *ngIf="mode === 'widget' && widgetsList.length > 0"
196 196 #carousel
197 197 [(ngModel)]="widgetsCarouselIndex"
198   - (ngModelChange)="onWidgetsCarouselIndexChanged($event)"
  198 + (ngModelChange)="onWidgetsCarouselIndexChanged()"
199 199 [data]="widgetsList"
200 200 [infinite]="false"
201 201 class="carousel c-accent">
... ...
... ... @@ -54,6 +54,7 @@ import { ImportDialogCsvComponent, ImportDialogCsvData } from './import-dialog-c
54 54 import { ImportEntityData, ImportEntitiesResultInfo } from '@shared/models/entity.models';
55 55 import { RequestConfig } from '@core/http/http-utils';
56 56
  57 +// @dynamic
57 58 @Injectable()
58 59 export class ImportExportService {
59 60
... ...
  1 +///
  2 +/// Copyright © 2016-2019 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 +export * from './home-components.module';
... ...
... ... @@ -16,17 +16,17 @@
16 16
17 17 -->
18 18 <div class="tb-relation-filters" [formGroup]="relationFiltersFormGroup">
19   - <div class="header" [fxShow]="relationFiltersFormGroup.get('relationFilters').length">
  19 + <div class="header" [fxShow]="relationFiltersFormArray().length">
20 20 <div fxLayout="row" fxLayoutAlign="start center">
21 21 <span class="cell" style="width: 200px; min-width: 200px;" translate>relation.type</span>
22 22 <span class="cell" fxFlex translate>entity.entity-types</span>
23 23 <span class="cell" style="width: 40px; min-width: 40px;">&nbsp;</span>
24 24 </div>
25 25 </div>
26   - <div class="body" [fxShow]="relationFiltersFormGroup.get('relationFilters').length">
  26 + <div class="body" [fxShow]="relationFiltersFormArray().length">
27 27 <div class="row" fxFlex fxLayout="row"
28 28 fxLayoutAlign="start center" formArrayName="relationFilters"
29   - *ngFor="let relationFilterControl of relationFiltersFormGroup.get('relationFilters').controls; let $index = index">
  29 + *ngFor="let relationFilterControl of relationFiltersFormArray().controls; let $index = index">
30 30 <div class="mat-elevation-z1" fxFlex fxLayout="row" fxLayoutAlign="start center">
31 31 <tb-relation-type-autocomplete
32 32 class="cell" style="width: 200px; min-width: 200px;"
... ... @@ -48,7 +48,7 @@
48 48 </div>
49 49 </div>
50 50 </div>
51   - <div class="any-filter" [fxShow]="!relationFiltersFormGroup.get('relationFilters').length">
  51 + <div class="any-filter" [fxShow]="!relationFiltersFormArray().length">
52 52 <span fxLayoutAlign="center center"
53 53 class="tb-prompt" translate>relation.any-relation</span>
54 54 </div>
... ...
... ... @@ -69,6 +69,10 @@ export class RelationFiltersComponent extends PageComponent implements ControlVa
69 69 this.fb.array([]));
70 70 }
71 71
  72 + relationFiltersFormArray(): FormArray {
  73 + return this.relationFiltersFormGroup.get('relationFilters') as FormArray;
  74 + }
  75 +
72 76 registerOnChange(fn: any): void {
73 77 this.propagateChange = fn;
74 78 }
... ...
... ... @@ -14,6 +14,9 @@
14 14 /// limitations under the License.
15 15 ///
16 16
  17 +// tslint:disable-next-line:no-reference
  18 +/// <reference path="../../../../../../../src/typings/split.js.typings.d.ts" />
  19 +
17 20 import {
18 21 AfterViewInit,
19 22 ChangeDetectionStrategy,
... ...
... ... @@ -78,7 +78,7 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
78 78
79 79 displayAdvanced = false;
80 80
81   - private modelValue: DataKey;
  81 + modelValue: DataKey;
82 82
83 83 private propagateChange = null;
84 84
... ...
... ... @@ -140,7 +140,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie
140 140 placeholder: string;
141 141 requiredText: string;
142 142
143   - private searchText = '';
  143 + searchText = '';
144 144 private latestSearchTextResult: Array<DataKey> = null;
145 145
146 146 private dirty = false;
... ...
... ... @@ -17,7 +17,7 @@
17 17 -->
18 18 <button cdkOverlayOrigin #legendConfigPanelOrigin="cdkOverlayOrigin" [disabled]="disabled"
19 19 type="button"
20   - mat-button mat-raised-button color="primary" (click)="openEditMode($event)">
  20 + mat-button mat-raised-button color="primary" (click)="openEditMode()">
21 21 <mat-icon class="material-icons">toc</mat-icon>
22 22 <span translate>legend.settings</span>
23 23 </button>
... ...
... ... @@ -59,6 +59,7 @@ import {
59 59 LegendConfigPanelData
60 60 } from '@home/components/widget/legend-config-panel.component';
61 61
  62 +// @dynamic
62 63 @Component({
63 64 selector: 'tb-legend-config',
64 65 templateUrl: './legend-config.component.html',
... ...
... ... @@ -14,6 +14,9 @@
14 14 /// limitations under the License.
15 15 ///
16 16
  17 +// tslint:disable-next-line:no-reference
  18 +/// <reference path="../../../../../../../src/typings/jquery.flot.typings.d.ts" />
  19 +
17 20 import { JsonSettingsSchema, DataKey, DatasourceData } from '@shared/models/widget.models';
18 21
19 22 export declare type ChartType = 'line' | 'pie' | 'bar' | 'state' | 'graph';
... ... @@ -395,8 +398,8 @@ export function flotSettingsSchema(chartType: ChartType): JsonSettingsSchema {
395 398 return schema;
396 399 }
397 400
398   -export function flotPieSettingsSchema(): JsonSettingsSchema {
399   - return {
  401 +export const flotPieSettingsSchema: JsonSettingsSchema =
  402 + {
400 403 schema: {
401 404 type: 'object',
402 405 title: 'Settings',
... ... @@ -477,8 +480,7 @@ export function flotPieSettingsSchema(): JsonSettingsSchema {
477 480 },
478 481 'fontSize'
479 482 ]
480   - };
481   -}
  483 +};
482 484
483 485 export function flotDatakeySettingsSchema(defaultShowLines: boolean): JsonSettingsSchema {
484 486 return {
... ...
... ... @@ -36,12 +36,17 @@ import {
36 36 TbFlotTicksFormatterFunction,
37 37 TooltipValueFormatFunction
38 38 } from './flot-widget.models';
39   -import * as moment from 'moment';
40   -import * as tinycolor from 'tinycolor2';
  39 +import * as moment_ from 'moment';
  40 +import * as tinycolor_ from 'tinycolor2';
41 41 import { AggregationType } from '@shared/models/time/time.models';
42 42 import { CancelAnimationFrame } from '@core/services/raf.service';
43 43 import Timeout = NodeJS.Timeout;
44 44
  45 +const tinycolor = tinycolor_;
  46 +const moment = moment_;
  47 +
  48 +const flotPieSettingsSchemaValue = flotPieSettingsSchema;
  49 +
45 50 export class TbFlot {
46 51
47 52 private settings: TbFlotSettings;
... ... @@ -89,11 +94,11 @@ export class TbFlot {
89 94 private pieAnimationLastTime: number;
90 95 private pieAnimationCaf: CancelAnimationFrame;
91 96
92   - static get pieSettingsSchema(): JsonSettingsSchema {
93   - return flotPieSettingsSchema();
  97 + static pieSettingsSchema(): JsonSettingsSchema {
  98 + return flotPieSettingsSchemaValue;
94 99 }
95 100
96   - static get pieDatakeySettingsSchema(): JsonSettingsSchema {
  101 + static pieDatakeySettingsSchema(): JsonSettingsSchema {
97 102 return {};
98 103 }
99 104
... ...
... ... @@ -38,14 +38,17 @@ import { SharedModule } from '@shared/shared.module';
38 38 import { WidgetComponentsModule } from '@home/components/widget/widget-components.module';
39 39 import { WINDOW } from '@core/services/window.service';
40 40
41   -import * as tinycolor from 'tinycolor2';
  41 +import * as tinycolor_ from 'tinycolor2';
42 42 import { TbFlot } from './lib/flot-widget';
43 43 import { NULL_UUID } from '@shared/models/id/has-uuid';
44 44 import { WidgetTypeId } from '@app/shared/models/id/widget-type-id';
45 45 import { TenantId } from '@app/shared/models/id/tenant-id';
46 46
  47 +const tinycolor = tinycolor_;
  48 +
47 49 // declare var jQuery: any;
48 50
  51 +// @dynamic
49 52 @Injectable()
50 53 export class WidgetComponentService {
51 54
... ...
... ... @@ -71,7 +71,7 @@
71 71 class="tb-hint">{{ 'widget-config.maximum-datasources' | translate:{count: modelValue?.typeParameters.maxDatasources} }}</div>
72 72 </mat-panel-title>
73 73 </mat-expansion-panel-header>
74   - <div *ngIf="dataSettings.get('datasources').length === 0; else datasourcesTemplate">
  74 + <div *ngIf="datasourcesFormArray().length === 0; else datasourcesTemplate">
75 75 <span translate fxLayoutAlign="center center"
76 76 class="tb-prompt">datasource.add-datasource-prompt</span>
77 77 </div>
... ... @@ -89,7 +89,7 @@
89 89 <div style="overflow: auto; padding-bottom: 15px;">
90 90 <div fxFlex fxLayout="row" fxLayoutAlign="start center"
91 91 formArrayName="datasources"
92   - *ngFor="let datasourceControl of dataSettings.get('datasources').controls; let $index = index;">
  92 + *ngFor="let datasourceControl of datasourcesFormArray().controls; let $index = index;">
93 93 <span fxFlex="5">{{$index + 1}}.</span>
94 94 <div [formGroupName]="$index" class="mat-elevation-z4" fxFlex
95 95 fxLayout="row"
... ... @@ -109,7 +109,7 @@
109 109 </mat-form-field>
110 110 <section class="tb-datasource" [ngSwitch]="datasourceControl.get('type').value">
111 111 <ng-template [ngSwitchCase]="datasourceType.function">
112   - <mat-form-field floatLabel="always" [fxShow]="widgetType !== widgetTypes.alarm"
  112 + <mat-form-field floatLabel="always"
113 113 class="tb-datasource-name" style="min-width: 200px;">
114 114 <mat-label></mat-label>
115 115 <input matInput
... ... @@ -156,7 +156,7 @@
156 156 type="button"
157 157 mat-button mat-raised-button color="primary"
158 158 [fxShow]="modelValue?.typeParameters &&
159   - (modelValue?.typeParameters.maxDatasources == -1 || dataSettings.get('datasources').controls.length < modelValue?.typeParameters.maxDatasources)"
  159 + (modelValue?.typeParameters.maxDatasources == -1 || datasourcesFormArray().controls.length < modelValue?.typeParameters.maxDatasources)"
160 160 (click)="addDatasource()"
161 161 matTooltip="{{ 'widget-config.add-datasource' | translate }}"
162 162 matTooltipPosition="above">
... ...
... ... @@ -128,7 +128,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont
128 128
129 129 selectedTab: number;
130 130
131   - private modelValue: WidgetConfigComponentData;
  131 + modelValue: WidgetConfigComponentData;
132 132
133 133 private propagateChange = null;
134 134
... ... @@ -307,6 +307,10 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont
307 307 this.fb.control(null, []));
308 308 }
309 309
  310 + datasourcesFormArray(): FormArray {
  311 + return this.dataSettings.get('datasources') as FormArray;
  312 + }
  313 +
310 314 registerOnChange(fn: any): void {
311 315 this.propagateChange = fn;
312 316 }
... ...
... ... @@ -86,6 +86,7 @@ import {
86 86 import { ImportExportService } from '@home/components/import-export/import-export.service';
87 87 import { AuthState } from '@app/core/auth/auth.models';
88 88
  89 +// @dynamic
89 90 @Component({
90 91 selector: 'tb-dashboard-page',
91 92 templateUrl: './dashboard-page.component.html',
... ...
... ... @@ -90,6 +90,7 @@ const routes: Routes = [
90 90 }
91 91 ];
92 92
  93 +// @dynamic
93 94 @NgModule({
94 95 imports: [RouterModule.forChild(routes)],
95 96 exports: [RouterModule],
... ...
... ... @@ -47,7 +47,7 @@ import { map } from 'rxjs/operators';
47 47 })
48 48 export class EntityStateControllerComponent extends StateControllerComponent implements OnInit, OnDestroy {
49 49
50   - private selectedStateIndex = -1;
  50 + selectedStateIndex = -1;
51 51
52 52 constructor(protected router: Router,
53 53 protected route: ActivatedRoute,
... ...
  1 +///
  2 +/// Copyright © 2016-2019 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 +export * from './home-pages.module';
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2019 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 +<form #ruleNodeForm="ngForm" (ngSubmit)="add()" style="min-width: 650px;">
  19 + <mat-toolbar fxLayout="row" color="primary">
  20 + <h2 translate>rulenode.add</h2>
  21 + <span fxFlex></span>
  22 + <div [tb-help]="helpLinkIdForRuleNodeType()"></div>
  23 + <button mat-button mat-icon-button
  24 + (click)="cancel()"
  25 + type="button">
  26 + <mat-icon class="material-icons">close</mat-icon>
  27 + </button>
  28 + </mat-toolbar>
  29 + <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
  30 + </mat-progress-bar>
  31 + <div mat-dialog-content>
  32 + <fieldset [disabled]="isLoading$ | async">
  33 + <tb-rule-node #tbRuleNode
  34 + [ruleNode]="ruleNode"
  35 + [ruleChainId]="ruleChainId"
  36 + [isEdit]="true"
  37 + [isReadOnly]="false">
  38 + </tb-rule-node>
  39 + </fieldset>
  40 + </div>
  41 + <div mat-dialog-actions fxLayout="row">
  42 + <span fxFlex></span>
  43 + <button mat-button mat-raised-button color="primary"
  44 + type="submit"
  45 + [disabled]="(isLoading$ | async) || tbRuleNode.ruleNodeForm.invalid || !tbRuleNode.ruleNodeForm.dirty">
  46 + {{ 'action.add' | translate }}
  47 + </button>
  48 + <button mat-button color="primary"
  49 + style="margin-right: 20px;"
  50 + type="button"
  51 + [disabled]="(isLoading$ | async)"
  52 + (click)="cancel()" cdkFocusInitial>
  53 + {{ 'action.cancel' | translate }}
  54 + </button>
  55 + </div>
  56 +</form>
... ...
... ... @@ -45,7 +45,7 @@
45 45 <mat-option *ngFor="let label of filteredLabels | async" [value]="label">
46 46 <span [innerHTML]="label.name | highlight:searchText"></span>
47 47 </mat-option>
48   - <mat-option *ngIf="!(filteredLabels | async)?.length" [value]="null" class="tb-not-found">
  48 + <mat-option *ngIf="(filteredLabels | async)?.length === 0" [value]="null" class="tb-not-found">
49 49 <div class="tb-not-found-content" (click)="$event.stopPropagation()">
50 50 <div *ngIf="!textIsNotEmpty(searchText); else searchNotEmpty">
51 51 <span translate>rulenode.no-link-labels-found</span>
... ...
... ... @@ -94,9 +94,9 @@ export class LinkLabelsComponent implements ControlValueAccessor, OnInit, OnChan
94 94
95 95 filteredLabels: Observable<Array<LinkLabel>>;
96 96
97   - private labels: Array<LinkLabel> = [];
  97 + labels: Array<LinkLabel> = [];
98 98
99   - private searchText = '';
  99 + searchText = '';
100 100
101 101 private propagateChange = (v: any) => { };
102 102
... ... @@ -190,7 +190,7 @@ export class LinkLabelsComponent implements ControlValueAccessor, OnInit, OnChan
190 190 }
191 191
192 192 add(event: MatChipInputEvent): void {
193   - if (!this.matAutocomplete.isOpen) {
  193 + if (!this.matAutocomplete.isOpen || this.allowCustom) {
194 194 this.transformLinkLabel(event.value);
195 195 }
196 196 }
... ... @@ -250,8 +250,8 @@ export class LinkLabelsComponent implements ControlValueAccessor, OnInit, OnChan
250 250 if (this.required) {
251 251 this.chipList.errorState = false;
252 252 }
  253 + this.updateModel();
253 254 }
254   - this.updateModel();
255 255 }
256 256
257 257 clear(value: string = '') {
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2019 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 [formGroup]="ruleNodeConfigFormGroup">
  19 + <ng-container #definedConfigContent></ng-container>
  20 + <div class="tb-rulenode-directive-error" *ngIf="definedDirectiveError">{{definedDirectiveError}}</div>
  21 + <tb-json-object-edit *ngIf="!useDefinedDirective()" #jsonObjectEditComponent
  22 + class="tb-rule-node-configuration-json"
  23 + formControlName="configuration"
  24 + [label]="'rulenode.configuration' | translate"
  25 + [required]="required"
  26 + [fillHeight]="true">
  27 + </tb-json-object-edit>
  28 +</div>
... ...
  1 +/**
  2 + * Copyright © 2016-2019 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 +:host {
  17 + tb-json-object-edit.tb-rule-node-configuration-json {
  18 + display: block;
  19 + height: 300px;
  20 + }
  21 +
  22 + .tb-rulenode-directive-error {
  23 + font-size: 13px;
  24 + font-weight: 400;
  25 + color: rgb(221, 44, 0);
  26 + }
  27 +}
... ...
  1 +///
  2 +/// Copyright © 2016-2019 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 +import {
  18 + AfterViewInit,
  19 + Component, ElementRef,
  20 + EventEmitter, forwardRef,
  21 + Input,
  22 + OnChanges,
  23 + OnInit,
  24 + Output,
  25 + SimpleChanges,
  26 + ViewChild,
  27 + Compiler,
  28 + Injector, ComponentRef, OnDestroy
  29 +} from '@angular/core';
  30 +import { PageComponent } from '@shared/components/page.component';
  31 +import { Store } from '@ngrx/store';
  32 +import { AppState } from '@core/core.state';
  33 +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, NgForm, Validators } from '@angular/forms';
  34 +import { FcRuleNode, FcRuleEdge } from './rulechain-page.models';
  35 +import { RuleNodeType, LinkLabel, RuleNodeDefinition, RuleNodeConfiguration, IRuleNodeConfigurationComponent } from '@shared/models/rule-node.models';
  36 +import { EntityType } from '@shared/models/entity-type.models';
  37 +import { Observable, of, Subscription } from 'rxjs';
  38 +import { RuleChainService } from '@core/http/rule-chain.service';
  39 +import { coerceBooleanProperty } from '@angular/cdk/coercion';
  40 +import { deepClone } from '@core/utils';
  41 +import { EntityAlias } from '@shared/models/alias.models';
  42 +import { TruncatePipe } from '@shared/pipe/truncate.pipe';
  43 +import { MatChipList, MatAutocomplete, MatChipInputEvent, MatAutocompleteSelectedEvent } from '@angular/material';
  44 +import { TranslateService } from '@ngx-translate/core';
  45 +import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes';
  46 +import { catchError, map, mergeMap, share } from 'rxjs/operators';
  47 +import { DynamicWidgetComponent } from '@home/components/widget/dynamic-widget.component';
  48 +import { SharedModule } from '@shared/shared.module';
  49 +import { WidgetComponentsModule } from '@home/components/widget/widget-components.module';
  50 +import { DynamicComponentFactoryService } from '@core/services/dynamic-component-factory.service';
  51 +import { ViewContainerRef } from '@angular/core';
  52 +import { JsonObjectEditComponent } from '@shared/components/json-object-edit.component';
  53 +
  54 +@Component({
  55 + selector: 'tb-rule-node-config',
  56 + templateUrl: './rule-node-config.component.html',
  57 + styleUrls: ['./rule-node-config.component.scss'],
  58 + providers: [{
  59 + provide: NG_VALUE_ACCESSOR,
  60 + useExisting: forwardRef(() => RuleNodeConfigComponent),
  61 + multi: true
  62 + }]
  63 +})
  64 +export class RuleNodeConfigComponent implements ControlValueAccessor, OnInit, OnDestroy, AfterViewInit {
  65 +
  66 + @ViewChild('definedConfigContent', {read: ViewContainerRef, static: true}) definedConfigContainer: ViewContainerRef;
  67 +
  68 + @ViewChild('jsonObjectEditComponent', {static: false}) jsonObjectEditComponent: JsonObjectEditComponent;
  69 +
  70 + private requiredValue: boolean;
  71 + get required(): boolean {
  72 + return this.requiredValue;
  73 + }
  74 + @Input()
  75 + set required(value: boolean) {
  76 + this.requiredValue = coerceBooleanProperty(value);
  77 + }
  78 +
  79 + @Input()
  80 + disabled: boolean;
  81 +
  82 + @Input()
  83 + ruleNodeId: string;
  84 +
  85 + nodeDefinitionValue: RuleNodeDefinition;
  86 +
  87 + @Input()
  88 + set nodeDefinition(nodeDefinition: RuleNodeDefinition) {
  89 + if (this.nodeDefinitionValue !== nodeDefinition) {
  90 + this.nodeDefinitionValue = nodeDefinition;
  91 + if (this.nodeDefinitionValue) {
  92 + this.validateDefinedDirective();
  93 + }
  94 + }
  95 + }
  96 +
  97 + get nodeDefinition(): RuleNodeDefinition {
  98 + return this.nodeDefinitionValue;
  99 + }
  100 +
  101 + definedDirectiveError: string;
  102 +
  103 + ruleNodeConfigFormGroup: FormGroup;
  104 +
  105 + changeSubscription: Subscription;
  106 +
  107 + private definedConfigComponentRef: ComponentRef<IRuleNodeConfigurationComponent>;
  108 + private definedConfigComponent: IRuleNodeConfigurationComponent;
  109 +
  110 + private configuration: RuleNodeConfiguration;
  111 +
  112 + private propagateChange = (v: any) => { };
  113 +
  114 + constructor(private translate: TranslateService,
  115 + private ruleChainService: RuleChainService,
  116 + private fb: FormBuilder) {
  117 + this.ruleNodeConfigFormGroup = this.fb.group({
  118 + configuration: [null, Validators.required]
  119 + });
  120 + }
  121 +
  122 + registerOnChange(fn: any): void {
  123 + this.propagateChange = fn;
  124 + }
  125 +
  126 + registerOnTouched(fn: any): void {
  127 + }
  128 +
  129 + ngOnInit(): void {
  130 + }
  131 +
  132 + ngOnDestroy(): void {
  133 + if (this.definedConfigComponentRef) {
  134 + this.definedConfigComponentRef.destroy();
  135 + }
  136 + }
  137 +
  138 + ngAfterViewInit(): void {
  139 + }
  140 +
  141 + setDisabledState(isDisabled: boolean): void {
  142 + this.disabled = isDisabled;
  143 + if (this.disabled) {
  144 + this.ruleNodeConfigFormGroup.disable({emitEvent: false});
  145 + } else {
  146 + this.ruleNodeConfigFormGroup.enable({emitEvent: false});
  147 + }
  148 + }
  149 +
  150 + writeValue(value: RuleNodeConfiguration): void {
  151 + this.configuration = value;
  152 + if (this.changeSubscription) {
  153 + this.changeSubscription.unsubscribe();
  154 + this.changeSubscription = null;
  155 + }
  156 + if (this.definedConfigComponent) {
  157 + this.definedConfigComponent.configuration = this.configuration;
  158 + this.changeSubscription = this.definedConfigComponent.configurationChanged.subscribe((configuration) => {
  159 + this.updateModel(configuration);
  160 + });
  161 + } else {
  162 + this.ruleNodeConfigFormGroup.get('configuration').patchValue(value, {emitEvent: false});
  163 + this.changeSubscription = this.ruleNodeConfigFormGroup.get('configuration').valueChanges.subscribe(
  164 + (configuration: RuleNodeConfiguration) => {
  165 + this.updateModel(configuration);
  166 + }
  167 + );
  168 + }
  169 + }
  170 +
  171 + useDefinedDirective(): boolean {
  172 + return this.nodeDefinition &&
  173 + (this.nodeDefinition.configDirective &&
  174 + this.nodeDefinition.configDirective.length) && !this.definedDirectiveError;
  175 + }
  176 +
  177 + private updateModel(configuration: RuleNodeConfiguration) {
  178 + if (this.definedConfigComponent || this.ruleNodeConfigFormGroup.valid) {
  179 + this.propagateChange(configuration);
  180 + } else {
  181 + this.propagateChange(this.required ? null : configuration);
  182 + }
  183 + }
  184 +
  185 + private validateDefinedDirective() {
  186 + if (this.definedConfigComponentRef) {
  187 + this.definedConfigComponentRef.destroy();
  188 + this.definedConfigComponentRef = null;
  189 + }
  190 + if (this.nodeDefinition.uiResourceLoadError && this.nodeDefinition.uiResourceLoadError.length) {
  191 + this.definedDirectiveError = this.nodeDefinition.uiResourceLoadError;
  192 + } else if (this.nodeDefinition.configDirective && this.nodeDefinition.configDirective.length) {
  193 + if (this.changeSubscription) {
  194 + this.changeSubscription.unsubscribe();
  195 + this.changeSubscription = null;
  196 + }
  197 + this.definedConfigContainer.clear();
  198 + const factory = this.ruleChainService.getRuleNodeConfigFactory(this.nodeDefinition.configDirective);
  199 + this.definedConfigComponentRef = this.definedConfigContainer.createComponent(factory);
  200 + this.definedConfigComponent = this.definedConfigComponentRef.instance;
  201 + this.definedConfigComponent.ruleNodeId = this.ruleNodeId;
  202 + this.definedConfigComponent.configuration = this.configuration;
  203 + this.changeSubscription = this.definedConfigComponent.configurationChanged.subscribe((configuration) => {
  204 + this.updateModel(configuration);
  205 + });
  206 + }
  207 + }
  208 +}
... ...
... ... @@ -31,7 +31,11 @@
31 31 {{ 'rulenode.debug-mode' | translate }}
32 32 </mat-checkbox>
33 33 </section>
34   - TODO: tb-rule-node-config
  34 + <tb-rule-node-config
  35 + formControlName="configuration"
  36 + [ruleNodeId]="ruleNode.ruleNodeId?.id"
  37 + [nodeDefinition]="ruleNode.component.configurationDescriptor.nodeDefinition">
  38 + </tb-rule-node-config>
35 39 <div formGroupName="additionalInfo" fxLayout="column">
36 40 <mat-form-field class="mat-block">
37 41 <mat-label translate>rulenode.description</mat-label>
... ...
... ... @@ -70,6 +70,7 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O
70 70 this.ruleNodeFormGroup = this.fb.group({
71 71 name: [this.ruleNode.name, [Validators.required]],
72 72 debugMode: [this.ruleNode.debugMode, []],
  73 + configuration: [this.ruleNode.configuration, [Validators.required]],
73 74 additionalInfo: this.fb.group(
74 75 {
75 76 description: [this.ruleNode.additionalInfo ? this.ruleNode.additionalInfo.description : ''],
... ...
... ... @@ -151,7 +151,6 @@ export class RuleChainPageComponent extends PageComponent
151 151 return source.type === FlowchartConstants.rightConnectorType && destination.type === FlowchartConstants.leftConnectorType;
152 152 },
153 153 createEdge: (event, edge: FcRuleEdge) => {
154   - console.log('TODO');
155 154 const sourceNode = this.ruleChainCanvas.modelService.nodes.getNodeByConnectorId(edge.source) as FcRuleNode;
156 155 if (sourceNode.component.type === RuleNodeType.INPUT) {
157 156 const destNode = this.ruleChainCanvas.modelService.nodes.getNodeByConnectorId(edge.destination) as FcRuleNode;
... ... @@ -185,9 +184,8 @@ export class RuleChainPageComponent extends PageComponent
185 184 }
186 185 }
187 186 },
188   - dropNode: (event, node) => {
189   - console.log('TODO dropNode');
190   - console.log(node);
  187 + dropNode: (event, node: FcRuleNode) => {
  188 + this.addRuleNode(node);
191 189 }
192 190 };
193 191
... ... @@ -269,7 +267,7 @@ export class RuleChainPageComponent extends PageComponent
269 267 this.createRuleChainModel();
270 268 }
271 269
272   - private updateRuleChainLibrary() {
  270 + updateRuleChainLibrary() {
273 271 const search = this.ruleNodeTypeSearch.toUpperCase();
274 272 const res = this.ruleNodeComponents.filter(
275 273 (ruleNodeComponent) => ruleNodeComponent.name.toUpperCase().includes(search));
... ... @@ -734,6 +732,46 @@ export class RuleChainPageComponent extends PageComponent
734 732 this.createRuleChainModel();
735 733 }
736 734
  735 + addRuleNode(ruleNode: FcRuleNode) {
  736 + ruleNode.configuration = deepClone(ruleNode.component.configurationDescriptor.nodeDefinition.defaultConfiguration);
  737 + const ruleChainId = this.ruleChain.id ? this.ruleChain.id.id : null;
  738 + this.dialog.open<AddRuleNodeDialogComponent, AddRuleNodeDialogData,
  739 + FcRuleNode>(AddRuleNodeDialogComponent, {
  740 + disableClose: true,
  741 + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
  742 + data: {
  743 + ruleNode,
  744 + ruleChainId
  745 + }
  746 + }).afterClosed().subscribe(
  747 + (addedRuleNode) => {
  748 + if (addedRuleNode) {
  749 + addedRuleNode.id = 'rule-chain-node-' + this.nextNodeID++;
  750 + addedRuleNode.connectors = [];
  751 + if (addedRuleNode.component.configurationDescriptor.nodeDefinition.inEnabled) {
  752 + addedRuleNode.connectors.push(
  753 + {
  754 + id: (this.nextConnectorID++) + '',
  755 + type: FlowchartConstants.leftConnectorType
  756 + }
  757 + );
  758 + }
  759 + if (addedRuleNode.component.configurationDescriptor.nodeDefinition.outEnabled) {
  760 + addedRuleNode.connectors.push(
  761 + {
  762 + id: (this.nextConnectorID++) + '',
  763 + type: FlowchartConstants.rightConnectorType
  764 + }
  765 + );
  766 + }
  767 + this.ruleChainModel.nodes.push(addedRuleNode);
  768 + this.onModelChanged();
  769 + this.updateRuleNodesHighlight();
  770 + }
  771 + }
  772 + );
  773 + }
  774 +
737 775 addRuleNodeLink(link: FcRuleEdge, labels: {[label: string]: LinkLabel}, allowCustomLabels: boolean): Observable<FcRuleEdge> {
738 776 return this.dialog.open<AddRuleNodeLinkDialogComponent, AddRuleNodeLinkDialogData,
739 777 FcRuleEdge>(AddRuleNodeLinkDialogComponent, {
... ... @@ -885,3 +923,56 @@ export class AddRuleNodeLinkDialogComponent extends DialogComponent<AddRuleNodeL
885 923 this.dialogRef.close(this.link);
886 924 }
887 925 }
  926 +
  927 +export interface AddRuleNodeDialogData {
  928 + ruleNode: FcRuleNode;
  929 + ruleChainId: string;
  930 +}
  931 +
  932 +@Component({
  933 + selector: 'tb-add-rule-node-dialog',
  934 + templateUrl: './add-rule-node-dialog.component.html',
  935 + providers: [{provide: ErrorStateMatcher, useExisting: AddRuleNodeDialogComponent}],
  936 + styleUrls: []
  937 +})
  938 +export class AddRuleNodeDialogComponent extends DialogComponent<AddRuleNodeDialogComponent, FcRuleNode>
  939 + implements OnInit, ErrorStateMatcher {
  940 +
  941 + ruleNode: FcRuleNode;
  942 + ruleChainId: string;
  943 +
  944 + submitted = false;
  945 +
  946 + constructor(protected store: Store<AppState>,
  947 + protected router: Router,
  948 + @Inject(MAT_DIALOG_DATA) public data: AddRuleNodeDialogData,
  949 + @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
  950 + public dialogRef: MatDialogRef<AddRuleNodeDialogComponent, FcRuleNode>) {
  951 + super(store, router, dialogRef);
  952 +
  953 + this.ruleNode = this.data.ruleNode;
  954 + this.ruleChainId = this.data.ruleChainId;
  955 + }
  956 +
  957 + ngOnInit(): void {
  958 + }
  959 +
  960 + isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
  961 + const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
  962 + const customErrorState = !!(control && control.invalid && this.submitted);
  963 + return originalErrorState || customErrorState;
  964 + }
  965 +
  966 + helpLinkIdForRuleNodeType(): string {
  967 + return getRuleNodeHelpLink(this.ruleNode.component);
  968 + }
  969 +
  970 + cancel(): void {
  971 + this.dialogRef.close(null);
  972 + }
  973 +
  974 + add(): void {
  975 + this.submitted = true;
  976 + this.dialogRef.close(this.ruleNode);
  977 + }
  978 +}
... ...
... ... @@ -39,6 +39,29 @@ import { RuleChainPageComponent } from '@home/pages/rulechain/rulechain-page.com
39 39 import { RuleNodeComponentDescriptor } from '@shared/models/rule-node.models';
40 40 import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard';
41 41
  42 +import * as AngularCommon from '@angular/common';
  43 +import * as AngularCore from '@angular/core';
  44 +import * as AngularForms from '@angular/forms';
  45 +import * as AngularCdkCoercion from '@angular/cdk/coercion';
  46 +import * as NgrxStore from '@ngrx/store';
  47 +import * as TranslateCore from '@ngx-translate/core';
  48 +import * as TbCore from '@core/public-api';
  49 +import * as TbShared from '@shared/public-api';
  50 +
  51 +declare const SystemJS;
  52 +
  53 +const ruleNodeConfigResourcesModulesMap = {
  54 + '@angular/core': SystemJS.newModule(AngularCore),
  55 + '@angular/common': SystemJS.newModule(AngularCommon),
  56 + '@angular/forms': SystemJS.newModule(AngularForms),
  57 + '@ngrx/store': SystemJS.newModule(NgrxStore),
  58 + '@ngx-translate/core': SystemJS.newModule(TranslateCore),
  59 + '@core/public-api': SystemJS.newModule(TbCore),
  60 + '@shared/public-api': SystemJS.newModule(TbShared)
  61 +};
  62 +
  63 +const t = SystemJS.newModule(AngularCore);
  64 +
42 65
43 66 @Injectable()
44 67 export class RuleChainResolver implements Resolve<RuleChain> {
... ... @@ -71,7 +94,7 @@ export class RuleNodeComponentsResolver implements Resolve<Array<RuleNodeCompone
71 94 }
72 95
73 96 resolve(route: ActivatedRouteSnapshot): Observable<Array<RuleNodeComponentDescriptor>> {
74   - return this.ruleChainService.getRuleNodeComponents();
  97 + return this.ruleChainService.getRuleNodeComponents(ruleNodeConfigResourcesModulesMap);
75 98 }
76 99 }
77 100
... ... @@ -150,6 +173,7 @@ const routes: Routes = [
150 173 }
151 174 ];
152 175
  176 +// @dynamic
153 177 @NgModule({
154 178 imports: [RouterModule.forChild(routes)],
155 179 exports: [RouterModule],
... ...
... ... @@ -21,19 +21,21 @@ import {RuleChainComponent} from '@modules/home/pages/rulechain/rulechain.compon
21 21 import {RuleChainRoutingModule} from '@modules/home/pages/rulechain/rulechain-routing.module';
22 22 import {HomeComponentsModule} from '@modules/home/components/home-components.module';
23 23 import { RuleChainTabsComponent } from '@home/pages/rulechain/rulechain-tabs.component';
24   -import { RuleChainPageComponent, AddRuleNodeLinkDialogComponent } from './rulechain-page.component';
  24 +import { RuleChainPageComponent, AddRuleNodeLinkDialogComponent, AddRuleNodeDialogComponent } from './rulechain-page.component';
25 25 import { RuleNodeComponent } from '@home/pages/rulechain/rulenode.component';
26 26 import { FC_NODE_COMPONENT_CONFIG } from 'ngx-flowchart/dist/ngx-flowchart';
27 27 import { RuleNodeDetailsComponent } from './rule-node-details.component';
28 28 import { RuleNodeLinkComponent } from './rule-node-link.component';
29 29 import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.conponent';
  30 +import { RuleNodeConfigComponent } from './rule-node-config.component';
30 31
31 32 @NgModule({
32 33 entryComponents: [
33 34 RuleChainComponent,
34 35 RuleChainTabsComponent,
35 36 RuleNodeComponent,
36   - AddRuleNodeLinkDialogComponent
  37 + AddRuleNodeLinkDialogComponent,
  38 + AddRuleNodeDialogComponent
37 39 ],
38 40 declarations: [
39 41 RuleChainComponent,
... ... @@ -41,9 +43,11 @@ import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.conponent
41 43 RuleChainPageComponent,
42 44 RuleNodeComponent,
43 45 RuleNodeDetailsComponent,
  46 + RuleNodeConfigComponent,
44 47 LinkLabelsComponent,
45 48 RuleNodeLinkComponent,
46   - AddRuleNodeLinkDialogComponent
  49 + AddRuleNodeLinkDialogComponent,
  50 + AddRuleNodeDialogComponent
47 51 ],
48 52 providers: [
49 53 {
... ...
... ... @@ -34,8 +34,8 @@
34 34 <mat-form-field class="mat-block">
35 35 <mat-label translate>user.activation-method</mat-label>
36 36 <mat-select matInput [ngModelOptions]="{standalone: true}" [(ngModel)]="activationMethod">
37   - <mat-option *ngFor="let activationMethod of (activationMethods | enumToArray)" [value]="activationMethods[activationMethod]">
38   - {{ activationMethodTranslations.get(activationMethods[activationMethod]) | translate }}
  37 + <mat-option *ngFor="let activationMethod of activationMethods" [value]="activationMethod">
  38 + {{ activationMethodTranslations.get(activationMethod) | translate }}
39 39 </mat-option>
40 40 </mat-select>
41 41 </mat-form-field>
... ...
... ... @@ -49,7 +49,7 @@ export class AddUserDialogComponent extends DialogComponent<AddUserDialogCompone
49 49 detailsForm: NgForm;
50 50 user: User;
51 51
52   - activationMethods = ActivationMethod;
  52 + activationMethods = Object.keys(ActivationMethod);
53 53
54 54 activationMethodTranslations = activationMethodTranslations;
55 55
... ...
... ... @@ -48,6 +48,7 @@ import {
48 48 } from '@home/pages/widget/save-widget-type-as-dialog.component';
49 49 import { Subscription } from 'rxjs';
50 50
  51 +// @dynamic
51 52 @Component({
52 53 selector: 'tb-widget-editor',
53 54 templateUrl: './widget-editor.component.html',
... ...
... ... @@ -201,6 +201,7 @@ export const routes: Routes = [
201 201 }
202 202 ];
203 203
  204 +// @dynamic
204 205 @NgModule({
205 206 imports: [RouterModule.forChild(routes)],
206 207 exports: [RouterModule],
... ...
  1 +///
  2 +/// Copyright © 2016-2019 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 +export * from './home.module';
... ...
... ... @@ -81,7 +81,7 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI
81 81
82 82 filteredDashboards: Observable<Array<DashboardInfo>>;
83 83
84   - private searchText = '';
  84 + searchText = '';
85 85
86 86 private propagateChange = (v: any) => { };
87 87
... ...
... ... @@ -40,6 +40,7 @@ import {
40 40 } from './dashboard-select-panel.component';
41 41 import { NULL_UUID } from '@shared/models/id/has-uuid';
42 42
  43 +// @dynamic
43 44 @Component({
44 45 selector: 'tb-dashboard-select',
45 46 templateUrl: './dashboard-select.component.html',
... ...
... ... @@ -94,7 +94,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit
94 94
95 95 filteredEntities: Observable<Array<BaseData<EntityId>>>;
96 96
97   - private searchText = '';
  97 + searchText = '';
98 98
99 99 private dirty = false;
100 100
... ...
... ... @@ -92,7 +92,7 @@ export class EntityKeysListComponent implements ControlValueAccessor, OnInit, Af
92 92
93 93 separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON];
94 94
95   - private searchText = '';
  95 + searchText = '';
96 96
97 97 private dirty = false;
98 98
... ...
... ... @@ -90,7 +90,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV
90 90 entities: Array<BaseData<EntityId>> = [];
91 91 filteredEntities: Observable<Array<BaseData<EntityId>>>;
92 92
93   - private searchText = '';
  93 + searchText = '';
94 94
95 95 private dirty = false;
96 96
... ...
... ... @@ -79,7 +79,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
79 79
80 80 private broadcastSubscription: Subscription;
81 81
82   - private searchText = '';
  82 + searchText = '';
83 83
84 84 private dirty = false;
85 85
... ...
... ... @@ -106,7 +106,7 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit,
106 106
107 107 separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON];
108 108
109   - private searchText = '';
  109 + searchText = '';
110 110
111 111 private dirty = false;
112 112
... ...
... ... @@ -102,7 +102,7 @@ export class EntityTypeListComponent implements ControlValueAccessor, OnInit, Af
102 102 placeholder: string;
103 103 secondaryPlaceholder: string;
104 104
105   - private searchText = '';
  105 + searchText = '';
106 106
107 107 private dirty = false;
108 108
... ...
... ... @@ -63,6 +63,7 @@ export class FabActionsDirective implements OnInit {
63 63
64 64 }
65 65
  66 +// @dynamic
66 67 @Component({
67 68 selector: 'mat-fab-toolbar',
68 69 templateUrl: './fab-toolbar.component.html',
... ...
... ... @@ -35,7 +35,7 @@ import { deepClone, isString } from '@app/core/utils';
35 35 import { TranslateService } from '@ngx-translate/core';
36 36 import { JsonFormProps } from './react/json-form.models';
37 37 import inspector from 'schema-inspector';
38   -import * as tinycolor from 'tinycolor2';
  38 +import * as tinycolor_ from 'tinycolor2';
39 39 import { DialogService } from '@app/core/services/dialog.service';
40 40 import * as React from 'react';
41 41 import * as ReactDOM from 'react-dom';
... ... @@ -43,6 +43,8 @@ import ReactSchemaForm from './react/json-form-react';
43 43 import JsonFormUtils from './react/json-form-utils';
44 44 import { JsonFormComponentData } from './json-form-component.models';
45 45
  46 +const tinycolor = tinycolor_;
  47 +
46 48 @Component({
47 49 selector: 'tb-json-form',
48 50 templateUrl: './json-form.component.html',
... ...
... ... @@ -20,7 +20,6 @@ import reactCSS from 'reactcss';
20 20 import ReactAce from 'react-ace';
21 21 import Button from '@material-ui/core/Button';
22 22 import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models';
23   -import * as tinycolor from 'tinycolor2';
24 23 import { IEditorProps } from 'react-ace/src/types';
25 24
26 25 interface ThingsboardAceEditorProps extends JsonFormFieldProps {
... ...
... ... @@ -17,13 +17,15 @@ import * as React from 'react';
17 17 import * as ReactDOM from 'react-dom';
18 18 import ThingsboardBaseComponent from './json-form-base-component';
19 19 import reactCSS from 'reactcss';
20   -import * as tinycolor from 'tinycolor2';
  20 +import * as tinycolor_ from 'tinycolor2';
21 21 import TextField from '@material-ui/core/TextField';
22 22 import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models';
23 23 import IconButton from '@material-ui/core/IconButton';
24 24 import ClearIcon from '@material-ui/icons/Clear';
25 25 import Tooltip from '@material-ui/core/Tooltip';
26 26
  27 +const tinycolor = tinycolor_;
  28 +
27 29 interface ThingsboardColorState extends JsonFormFieldState {
28 30 color: tinycolor.ColorFormats.RGBA | null;
29 31 focused: boolean;
... ...
... ... @@ -35,7 +35,8 @@ import ThingsboardFieldSet from './json-form-fieldset';
35 35 import { JsonFormProps, GroupInfo, JsonFormData, onChangeFn, OnColorClickFn } from './json-form.models';
36 36
37 37 import _ from 'lodash';
38   -import * as tinycolor from 'tinycolor2';
  38 +import * as tinycolor_ from 'tinycolor2';
  39 +const tinycolor = tinycolor_;
39 40
40 41 class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> {
41 42
... ...
... ... @@ -18,7 +18,9 @@ import { isUndefined, isDefined, isString } from '@app/core/utils';
18 18 import * as equal from 'deep-equal';
19 19 import ObjectPath from 'objectpath';
20 20 import * as React from 'react';
21   -import * as tinycolor from 'tinycolor2';
  21 +import * as tinycolor_ from 'tinycolor2';
  22 +
  23 +const tinycolor = tinycolor_;
22 24
23 25 export interface SchemaValidationResult {
24 26 valid: boolean;
... ...
... ... @@ -49,7 +49,7 @@ export class MatChipDraggableDirective implements AfterViewInit {
49 49 private elementRef: ElementRef<HTMLElement>) {
50 50 }
51 51
52   - @HostListener('document:mouseup', ['$event'])
  52 + @HostListener('document:mouseup')
53 53 onDocumentMouseUp() {
54 54 this.draggableChips.forEach((draggableChip) => {
55 55 draggableChip.preventDrag = false;
... ...
  1 +///
  2 +/// Copyright © 2016-2019 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 +export * from './page.component';
... ...
... ... @@ -69,7 +69,7 @@ export class RelationTypeAutocompleteComponent implements ControlValueAccessor,
69 69
70 70 filteredRelationTypes: Observable<Array<string>>;
71 71
72   - private searchText = '';
  72 + searchText = '';
73 73
74 74 private dirty = false;
75 75
... ...
... ... @@ -17,7 +17,7 @@
17 17 -->
18 18 <button *ngIf="asButton" cdkOverlayOrigin #timewindowPanelOrigin="cdkOverlayOrigin" [disabled]="disabled"
19 19 type="button"
20   - mat-raised-button color="primary" (click)="openEditMode($event)">
  20 + mat-raised-button color="primary" (click)="openEditMode()">
21 21 <mat-icon class="material-icons">query_builder</mat-icon>
22 22 <span>{{innerValue?.displayValue}}</span>
23 23 </button>
... ... @@ -25,20 +25,20 @@
25 25 class="tb-timewindow" fxLayout="row" fxLayoutAlign="start center">
26 26 <button *ngIf="direction === 'left'" [disabled]="disabled" mat-button mat-icon-button class="tb-mat-32"
27 27 type="button"
28   - (click)="openEditMode($event)"
  28 + (click)="openEditMode()"
29 29 matTooltip="{{ 'timewindow.edit' | translate }}"
30 30 [matTooltipPosition]="tooltipPosition">
31 31 <mat-icon class="material-icons">query_builder</mat-icon>
32 32 </button>
33 33 <span [fxHide]="hideLabel()"
34   - (click)="openEditMode($event)"
  34 + (click)="openEditMode()"
35 35 matTooltip="{{ 'timewindow.edit' | translate }}"
36 36 [matTooltipPosition]="tooltipPosition">
37 37 {{innerValue?.displayValue}}
38 38 </span>
39 39 <button *ngIf="direction === 'right'" [disabled]="disabled" mat-button mat-icon-button class="tb-mat-32"
40 40 type="button"
41   - (click)="openEditMode($event)"
  41 + (click)="openEditMode()"
42 42 matTooltip="{{ 'timewindow.edit' | translate }}"
43 43 [matTooltipPosition]="tooltipPosition">
44 44 <mat-icon class="material-icons">query_builder</mat-icon>
... ...
... ... @@ -54,6 +54,7 @@ import { TimeService } from '@core/services/time.service';
54 54 import { TooltipPosition } from '@angular/material/typings/tooltip';
55 55 import { deepClone } from '@core/utils';
56 56
  57 +// @dynamic
57 58 @Component({
58 59 selector: 'tb-timewindow',
59 60 templateUrl: './timewindow.component.html',
... ...
... ... @@ -59,6 +59,7 @@ export const HelpLinks = {
59 59 securitySettings: helpBaseUrl + '/docs/user-guide/ui/security-settings',
60 60 ruleEngine: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/overview/',
61 61 ruleNodeCheckRelation: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#check-relation-filter-node',
  62 + ruleNodeCheckExistenceFields: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#check-existence-fields-node',
62 63 ruleNodeJsFilter: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#script-filter-node',
63 64 ruleNodeJsSwitch: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#switch-node',
64 65 ruleNodeMessageTypeFilter: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#message-type-filter-node',
... ...
  1 +///
  2 +/// Copyright © 2016-2019 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 +export * from './alarm-id';
  18 +export * from './asset-id';
  19 +export * from './audit-log-id';
  20 +export * from './customer-id';
  21 +export * from './dashboard-id';
  22 +export * from './device-credentials-id';
  23 +export * from './device-id';
  24 +export * from './entity-id';
  25 +export * from './entity-view-id';
  26 +export * from './event-id';
  27 +export * from './has-uuid';
  28 +export * from './rule-chain-id';
  29 +export * from './rule-node-id';
  30 +export * from './tenant-id';
  31 +export * from './user-id';
  32 +export * from './widget-type-id';
  33 +export * from './widgets-bundle-id';
... ...
... ... @@ -14,7 +14,8 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import * as tinycolor from 'tinycolor2';
  17 +import * as tinycolor_ from 'tinycolor2';
  18 +const tinycolor = tinycolor_;
18 19
19 20 export interface MaterialColorItem {
20 21 value: string;
... ...
  1 +///
  2 +/// Copyright © 2016-2019 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 +export * from './page-data';
  18 +export * from './page-link';
  19 +export * from './sort-order';
... ...
  1 +///
  2 +/// Copyright © 2016-2019 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 +export * from './id/public-api';
  18 +export * from './page/public-api';
  19 +export * from './telemetry/telemetry.models';
  20 +export * from './time/time.models';
  21 +export * from './alarm.models';
  22 +export * from './alias.models';
  23 +export * from './audit-log.models';
  24 +export * from './authority.enum';
  25 +export * from './base-data';
  26 +export * from './component-descriptor.models';
  27 +export * from './constants';
  28 +export * from './contact-based.model';
  29 +export * from './customer.model';
  30 +export * from './dashboard.models';
  31 +export * from './device.models';
  32 +export * from './entity.models';
  33 +export * from './entity-type.models';
  34 +export * from './entity-view.models';
  35 +export * from './error.models';
  36 +export * from './event.models';
  37 +export * from './login.models';
  38 +export * from './material.models';
  39 +export * from './relation.models';
  40 +export * from './rule-chain.models';
  41 +export * from './rule-node.models';
  42 +export * from './settings.models';
  43 +export * from './tenant.model';
  44 +export * from './user.model';
  45 +export * from './widget.models';
  46 +export * from './widgets-bundle.model';
  47 +export * from './window-message.model';
... ...
... ... @@ -22,6 +22,12 @@ import {RuleChainId} from '@shared/models/id/rule-chain-id';
22 22 import {RuleNodeId} from '@shared/models/id/rule-node-id';
23 23 import { ComponentDescriptor, ComponentType } from '@shared/models/component-descriptor.models';
24 24 import { EntityType, EntityTypeResource } from '@shared/models/entity-type.models';
  25 +import { Observable } from 'rxjs';
  26 +import { PageComponent } from '@shared/components/page.component';
  27 +import { ComponentFactory, EventEmitter, Inject, OnDestroy, OnInit } from '@angular/core';
  28 +import { RafService } from '@core/services/raf.service';
  29 +import { Store } from '@ngrx/store';
  30 +import { AppState } from '@core/core.state';
25 31
26 32 export enum MsgDataType {
27 33 JSON = 'JSON',
... ... @@ -48,23 +54,57 @@ export interface LinkLabel {
48 54 value: string;
49 55 }
50 56
  57 +export interface RuleNodeDefinition {
  58 + description: string;
  59 + details: string;
  60 + inEnabled: boolean;
  61 + outEnabled: boolean;
  62 + relationTypes: string[];
  63 + customRelations: boolean;
  64 + defaultConfiguration: RuleNodeConfiguration;
  65 + icon?: string;
  66 + iconUrl?: string;
  67 + docUrl?: string;
  68 + uiResources?: string[];
  69 + uiResourceLoadError?: string;
  70 + configDirective?: string;
  71 +}
  72 +
51 73 export interface RuleNodeConfigurationDescriptor {
52   - nodeDefinition: {
53   - description: string;
54   - details: string;
55   - inEnabled: boolean;
56   - outEnabled: boolean;
57   - relationTypes: string[];
58   - customRelations: boolean;
59   - defaultConfiguration: any;
60   - icon?: string;
61   - iconUrl?: string;
62   - docUrl?: string;
63   - uiResources?: string[];
64   - uiResourceLoadError?: string;
65   - };
  74 + nodeDefinition: RuleNodeDefinition;
  75 +}
  76 +
  77 +export interface IRuleNodeConfigurationComponent {
  78 + ruleNodeId: string;
  79 + configuration: RuleNodeConfiguration;
  80 + configurationChanged: Observable<RuleNodeConfiguration>;
  81 + [key: string]: any;
66 82 }
67 83
  84 +export abstract class RuleNodeConfigurationComponent extends PageComponent implements
  85 + IRuleNodeConfigurationComponent, OnInit {
  86 +
  87 + ruleNodeId: string;
  88 + configuration: RuleNodeConfiguration;
  89 + configurationChangedEmiter = new EventEmitter<RuleNodeConfiguration>();
  90 + configurationChanged = this.configurationChangedEmiter.asObservable();
  91 +
  92 + protected constructor(@Inject(Store) protected store: Store<AppState>) {
  93 + super(store);
  94 + }
  95 +
  96 + ngOnInit() {
  97 + this.onConfigurationSet(this.configuration);
  98 + }
  99 +
  100 + protected abstract onConfigurationSet(configuration: RuleNodeConfiguration);
  101 +
  102 + protected notifyConfigurationUpdated(configuration: RuleNodeConfiguration) {
  103 + this.configurationChangedEmiter.emit(configuration);
  104 + }
  105 +}
  106 +
  107 +
68 108 export enum RuleNodeType {
69 109 FILTER = 'FILTER',
70 110 ENRICHMENT = 'ENRICHMENT',
... ... @@ -187,6 +227,7 @@ export interface RuleNodeComponentDescriptor extends ComponentDescriptor {
187 227
188 228 const ruleNodeClazzHelpLinkMap = {
189 229 'org.thingsboard.rule.engine.filter.TbCheckRelationNode': 'ruleNodeCheckRelation',
  230 + 'org.thingsboard.rule.engine.filter.TbCheckMessageNode': 'ruleNodeCheckExistenceFields',
190 231 'org.thingsboard.rule.engine.filter.TbJsFilterNode': 'ruleNodeJsFilter',
191 232 'org.thingsboard.rule.engine.filter.TbJsSwitchNode': 'ruleNodeJsSwitch',
192 233 'org.thingsboard.rule.engine.filter.TbMsgTypeFilterNode': 'ruleNodeMessageTypeFilter',
... ...
... ... @@ -31,8 +31,8 @@ export interface User extends BaseData<UserId> {
31 31 }
32 32
33 33 export enum ActivationMethod {
34   - DISPLAY_ACTIVATION_LINK,
35   - SEND_ACTIVATION_MAIL
  34 + DISPLAY_ACTIVATION_LINK = 'DISPLAY_ACTIVATION_LINK',
  35 + SEND_ACTIVATION_MAIL = 'SEND_ACTIVATION_MAIL'
36 36 }
37 37
38 38 export const activationMethodTranslations = new Map<ActivationMethod, string>(
... ...
... ... @@ -20,7 +20,7 @@ import { Pipe, PipeTransform } from '@angular/core';
20 20 name: 'enumToArray'
21 21 })
22 22 export class EnumToArrayPipe implements PipeTransform {
23   - transform(data: object) {
  23 + transform(data: object): string[] {
24 24 const keys = Object.keys(data);
25 25 return keys.slice(keys.length / 2);
26 26 }
... ...
... ... @@ -17,6 +17,7 @@
17 17 import { Inject, Pipe, PipeTransform } from '@angular/core';
18 18 import { WINDOW } from '@core/services/window.service';
19 19
  20 +// @dynamic
20 21 @Pipe({
21 22 name: 'keyboardShortcut'
22 23 })
... ...
  1 +///
  2 +/// Copyright © 2016-2019 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 +export * from './enum-to-array.pipe';
  18 +export * from './highlight.pipe';
  19 +export * from './keyboard-shortcut.pipe';
  20 +export * from './milliseconds-to-time-string.pipe';
  21 +export * from './nospace.pipe';
  22 +export * from './truncate.pipe';
... ...
  1 +///
  2 +/// Copyright © 2016-2019 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 +export * from './components/public-api';
  18 +export * from './models/public-api';
  19 +export * from './pipe/public-api';
  20 +export * from './shared.module';
... ...