From ddf8a3569e7157083c60fafa77f6eb3747cdd238 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 9 Sep 2020 16:08:45 +0300 Subject: [PATCH] Refactoring validation mqtt topic filter; Add help hint --- ...ile-transport-configuration.component.html | 32 ++++++++--- ...ile-transport-configuration.component.scss | 4 ++ ...ofile-transport-configuration.component.ts | 56 ++++++++++++++++--- .../assets/locale/locale.constant-en_US.json | 7 ++- 4 files changed, 79 insertions(+), 20 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html index 80d3e300b0..757756dc7b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html @@ -19,7 +19,6 @@
device-profile.mqtt-device-topic-filters -
@@ -30,8 +29,11 @@ {{ 'device-profile.telemetry-topic-filter-required' | translate}} - - {{ 'device-profile.not-valid-pattern-topic-filter' | translate}} + + {{ 'device-profile.not-valid-single-character' | translate}} + + + {{ 'device-profile.not-valid-multi-character' | translate}} @@ -42,8 +44,11 @@ {{ 'device-profile.rpc-request-topic-filter-required' | translate}} - - {{ 'device-profile.not-valid-pattern-topic-filter' | translate}} + + {{ 'device-profile.not-valid-single-character' | translate}} + + + {{ 'device-profile.not-valid-multi-character' | translate}}
@@ -56,8 +61,11 @@ {{ 'device-profile.attributes-topic-filter-required' | translate}} - - {{ 'device-profile.not-valid-pattern-topic-filter' | translate}} + + {{ 'device-profile.not-valid-single-character' | translate}} + + + {{ 'device-profile.not-valid-multi-character' | translate}} @@ -68,11 +76,17 @@ {{ 'device-profile.rpc-response-topic-filter-required' | translate}} - - {{ 'device-profile.not-valid-pattern-topic-filter' | translate}} + + {{ 'device-profile.not-valid-single-character' | translate}} + + + {{ 'device-profile.not-valid-multi-character' | translate}}
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.scss b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.scss index 4c75cb9472..4e9bfe914b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.scss @@ -23,5 +23,9 @@ legend { color: rgba(0, 0, 0, .7); } + + .tb-hint{ + padding: 0; + } } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts index cd3052c2c9..d5ef8a1a06 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts @@ -15,15 +15,24 @@ /// import { Component, forwardRef, Input, OnInit } from '@angular/core'; -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { + ControlValueAccessor, + FormBuilder, + FormControl, + FormGroup, + NG_VALUE_ACCESSOR, + ValidatorFn, + Validators +} from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DeviceProfileTransportConfiguration, - DeviceTransportType, MqttDeviceProfileTransportConfiguration + DeviceTransportType, + MqttDeviceProfileTransportConfiguration } from '@shared/models/device.models'; -import { isDefinedAndNotNull } from '../../../../../core/utils'; +import { isDefinedAndNotNull } from '@core/utils'; @Component({ selector: 'tb-mqtt-device-profile-transport-configuration', @@ -41,11 +50,10 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control private requiredValue: boolean; - private MQTTTopicPattern = new RegExp('^((?![#+]).)*$|^(.*[^#]\/|^)#$|^(.*\/|^)\+(\/.*|$)$'); - get required(): boolean { return this.requiredValue; } + @Input() set required(value: boolean) { this.requiredValue = coerceBooleanProperty(value); @@ -70,10 +78,10 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control ngOnInit() { this.mqttDeviceProfileTransportConfigurationFormGroup = this.fb.group({ configuration: this.fb.group({ - deviceAttributesTopic: [null, [Validators.required, Validators.pattern(this.MQTTTopicPattern)]], - deviceTelemetryTopic: [null, [Validators.required, Validators.pattern(this.MQTTTopicPattern)]], - deviceRpcRequestTopic: [null, [Validators.required, Validators.pattern(this.MQTTTopicPattern)]], - deviceRpcResponseTopic: [null, [Validators.required, Validators.pattern(this.MQTTTopicPattern)]] + deviceAttributesTopic: [null, [Validators.required, this.validationMQTTTopic()]], + deviceTelemetryTopic: [null, [Validators.required, this.validationMQTTTopic()]], + deviceRpcRequestTopic: [null, [Validators.required, this.validationMQTTTopic()]], + deviceRpcResponseTopic: [null, [Validators.required, this.validationMQTTTopic()]] }) }); this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { @@ -104,4 +112,34 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control } this.propagateChange(configuration); } + + private validationMQTTTopic(): ValidatorFn { + return (c: FormControl) => { + const newTopic = c.value; + const wildcardSymbols = /[#+]/g; + let findSymbol = wildcardSymbols.exec(newTopic); + while (findSymbol) { + const index = findSymbol.index; + const currentSymbol = findSymbol[0]; + const prevSymbol = index > 0 ? newTopic[index - 1] : null; + const nextSymbol = index < (newTopic.length - 1) ? newTopic[index + 1] : null; + if (currentSymbol === '#' && (index !== (newTopic.length - 1) || (prevSymbol !== null && prevSymbol !== '/'))) { + return { + invalidMultiTopicCharacter: { + valid: false + } + }; + } + if (currentSymbol === '+' && ((prevSymbol !== null && prevSymbol !== '/') || (nextSymbol !== null && nextSymbol !== '/'))) { + return { + invalidSingleTopicCharacter: { + valid: false + } + }; + } + findSymbol = wildcardSymbols.exec(newTopic); + } + return null; + }; + } } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 64d697c535..5912cc27d0 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -792,7 +792,7 @@ "no-device-profiles-found": "No device profiles found.", "create-new-device-profile": "Create a new one!", "mqtt-device-topic-filters": "MQTT device topic filters", - "support-level-wildcards": "Support single (+) and multi (#) level wildcards", + "support-level-wildcards": "Single [+] and multi-level [#] wildcards supported.", "telemetry-topic-filter": "Telemetry topic filter", "telemetry-topic-filter-required": "Telemetry topic filter is required.", "rpc-request-topic-filter": "RPC request topic filter", @@ -801,7 +801,10 @@ "attributes-topic-filter-required": "Attributes topic filter is required.", "rpc-response-topic-filter": "RPC response topic filter", "rpc-response-topic-filter-required": "RPC response topic filter is required.", - "not-valid-pattern-topic-filter": "Not valid pattern topic filter" + "not-valid-single-character": "Invalid use of a single-level wildcard character", + "not-valid-multi-character": "Invalid use of a multi-level wildcard character", + "single-level-wildcards-hint": "[+] is suitable for any topic level. Ex.: v1/devices/+/telemetry or +/devices/+/attributes.", + "multi-level-wildcards-hint": "[#] can replace the topic filter itself and must be the last symbol of the topic. Ex.: # or v1/devices/me/#." }, "dialog": { "close": "Close dialog"