Merge branch 'master' of github.com:thingsboard/thingsboard
This commit is contained in:
commit
397a97ce7e
@ -44,7 +44,7 @@ public class DefaultCacheCleanupService implements CacheCleanupService {
|
|||||||
case "3.0.1":
|
case "3.0.1":
|
||||||
log.info("Clear cache to upgrade from version 3.0.1 to 3.1.0 ...");
|
log.info("Clear cache to upgrade from version 3.0.1 to 3.1.0 ...");
|
||||||
clearAllCaches();
|
clearAllCaches();
|
||||||
break;
|
//do not break to show explicit calls for next versions
|
||||||
case "3.1.1":
|
case "3.1.1":
|
||||||
log.info("Clear cache to upgrade from version 3.1.1 to 3.2.0 ...");
|
log.info("Clear cache to upgrade from version 3.1.1 to 3.2.0 ...");
|
||||||
clearCacheByName("devices");
|
clearCacheByName("devices");
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import io.swagger.annotations.ApiModel;
|
|||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||||
|
import org.thingsboard.server.common.data.validation.Length;
|
||||||
import org.thingsboard.server.common.data.validation.NoXss;
|
import org.thingsboard.server.common.data.validation.NoXss;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
@ -32,6 +33,7 @@ public class DeviceProfileAlarm implements Serializable {
|
|||||||
|
|
||||||
@ApiModelProperty(position = 1, value = "String value representing the alarm rule id", example = "highTemperatureAlarmID")
|
@ApiModelProperty(position = 1, value = "String value representing the alarm rule id", example = "highTemperatureAlarmID")
|
||||||
private String id;
|
private String id;
|
||||||
|
@Length(fieldName = "alarm type")
|
||||||
@NoXss
|
@NoXss
|
||||||
@ApiModelProperty(position = 2, value = "String value representing type of the alarm", example = "High Temperature Alarm")
|
@ApiModelProperty(position = 2, value = "String value representing type of the alarm", example = "High Temperature Alarm")
|
||||||
private String alarmType;
|
private String alarmType;
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import io.swagger.annotations.ApiModelProperty;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.thingsboard.server.common.data.id.RuleChainId;
|
import org.thingsboard.server.common.data.id.RuleChainId;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ public class RuleChainMetaData {
|
|||||||
@ApiModelProperty(position = 2, required = true, value = "Index of the first rule node in the 'nodes' list")
|
@ApiModelProperty(position = 2, required = true, value = "Index of the first rule node in the 'nodes' list")
|
||||||
private Integer firstNodeIndex;
|
private Integer firstNodeIndex;
|
||||||
|
|
||||||
|
@Valid
|
||||||
@ApiModelProperty(position = 3, required = true, value = "List of rule node JSON objects")
|
@ApiModelProperty(position = 3, required = true, value = "List of rule node JSON objects")
|
||||||
private List<RuleNode> nodes;
|
private List<RuleNode> nodes;
|
||||||
|
|
||||||
|
|||||||
@ -52,6 +52,7 @@ import org.thingsboard.server.common.data.rule.RuleNode;
|
|||||||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
|
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
|
||||||
import org.thingsboard.server.dao.entity.AbstractEntityService;
|
import org.thingsboard.server.dao.entity.AbstractEntityService;
|
||||||
import org.thingsboard.server.dao.exception.DataValidationException;
|
import org.thingsboard.server.dao.exception.DataValidationException;
|
||||||
|
import org.thingsboard.server.dao.service.ConstraintValidator;
|
||||||
import org.thingsboard.server.dao.service.DataValidator;
|
import org.thingsboard.server.dao.service.DataValidator;
|
||||||
import org.thingsboard.server.dao.service.PaginatedRemover;
|
import org.thingsboard.server.dao.service.PaginatedRemover;
|
||||||
import org.thingsboard.server.dao.service.Validator;
|
import org.thingsboard.server.dao.service.Validator;
|
||||||
@ -135,6 +136,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
|
|||||||
if (ruleChain == null) {
|
if (ruleChain == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
ConstraintValidator.validateFields(ruleChainMetaData);
|
||||||
|
|
||||||
if (CollectionUtils.isNotEmpty(ruleChainMetaData.getConnections())) {
|
if (CollectionUtils.isNotEmpty(ruleChainMetaData.getConnections())) {
|
||||||
validateCircles(ruleChainMetaData.getConnections());
|
validateCircles(ruleChainMetaData.getConnections());
|
||||||
|
|||||||
@ -37,7 +37,6 @@ public class TbMqttNodeConfiguration implements NodeConfiguration<TbMqttNodeConf
|
|||||||
public TbMqttNodeConfiguration defaultConfiguration() {
|
public TbMqttNodeConfiguration defaultConfiguration() {
|
||||||
TbMqttNodeConfiguration configuration = new TbMqttNodeConfiguration();
|
TbMqttNodeConfiguration configuration = new TbMqttNodeConfiguration();
|
||||||
configuration.setTopicPattern("my-topic");
|
configuration.setTopicPattern("my-topic");
|
||||||
configuration.setHost("localhost");
|
|
||||||
configuration.setPort(1883);
|
configuration.setPort(1883);
|
||||||
configuration.setConnectTimeoutSec(10);
|
configuration.setConnectTimeoutSec(10);
|
||||||
configuration.setCleanSession(true);
|
configuration.setCleanSession(true);
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -45,6 +45,9 @@
|
|||||||
<mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('unique')">
|
<mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('unique')">
|
||||||
{{ 'device-profile.alarm-type-unique' | translate }}
|
{{ 'device-profile.alarm-type-unique' | translate }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
<mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('maxlength')">
|
||||||
|
{{ 'device-profile.alarm-type-max-length' | translate }}
|
||||||
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<mat-expansion-panel class="advanced-settings" [expanded]="false">
|
<mat-expansion-panel class="advanced-settings" [expanded]="false">
|
||||||
|
|||||||
@ -93,7 +93,7 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.alarmFormGroup = this.fb.group({
|
this.alarmFormGroup = this.fb.group({
|
||||||
id: [null, Validators.required],
|
id: [null, Validators.required],
|
||||||
alarmType: [null, Validators.required],
|
alarmType: [null, [Validators.required, Validators.maxLength(255)]],
|
||||||
createRules: [null],
|
createRules: [null],
|
||||||
clearRule: [null],
|
clearRule: [null],
|
||||||
propagate: [null],
|
propagate: [null],
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
class="tb-json-input__form"
|
class="tb-json-input__form"
|
||||||
[formGroup]="attributeUpdateFormGroup"
|
[formGroup]="attributeUpdateFormGroup"
|
||||||
(ngSubmit)="save()">
|
(ngSubmit)="save()">
|
||||||
<div fxLayout="column" fxLayoutGap="10px" fxFlex *ngIf="entityDetected && isValidParameter && dataKeyDetected">
|
<div fxLayout="column" fxLayoutGap="10px" fxFlex *ngIf="datasourceDetected && !errorMessage; else errorContainer">
|
||||||
<fieldset fxFlex>
|
<fieldset fxFlex>
|
||||||
<tb-json-object-edit
|
<tb-json-object-edit
|
||||||
[editorStyle]="{minHeight: '100px'}"
|
[editorStyle]="{minHeight: '100px'}"
|
||||||
@ -51,19 +51,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div fxLayout="column" fxLayoutAlign="center center" fxFlex *ngIf="!entityDetected || !dataKeyDetected || !isValidParameter">
|
<ng-template #errorContainer>
|
||||||
<div class="tb-json-input__error"
|
<div class="tb-json-input__error" fxLayout="column" fxLayoutAlign="center center" fxFlex>
|
||||||
*ngIf="!entityDetected">
|
|
||||||
{{ 'widgets.input-widgets.no-entity-selected' | translate }}
|
|
||||||
</div>
|
|
||||||
<div class="tb-json-input__error"
|
|
||||||
*ngIf="entityDetected && !dataKeyDetected">
|
|
||||||
{{ 'widgets.input-widgets.no-datakey-selected' | translate }}
|
|
||||||
</div>
|
|
||||||
<div class="tb-json-input__error"
|
|
||||||
*ngIf="dataKeyDetected && !isValidParameter">
|
|
||||||
{{ errorMessage | translate }}
|
{{ errorMessage | translate }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ng-template>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -25,7 +25,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__error {
|
&__error {
|
||||||
text-align: center;
|
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
color: #a0a0a0;
|
color: #a0a0a0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,7 @@ import { AttributeService } from '@core/http/attribute.service';
|
|||||||
import { AttributeData, AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
|
import { AttributeData, AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
|
||||||
import { EntityId } from '@shared/models/id/entity-id';
|
import { EntityId } from '@shared/models/id/entity-id';
|
||||||
import { EntityType } from '@shared/models/entity-type.models';
|
import { EntityType } from '@shared/models/entity-type.models';
|
||||||
import { createLabelFromDatasource } from '@core/utils';
|
import { createLabelFromDatasource, isDefinedAndNotNull } from '@core/utils';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
enum JsonInputWidgetMode {
|
enum JsonInputWidgetMode {
|
||||||
@ -63,9 +63,7 @@ export class JsonInputWidgetComponent extends PageComponent implements OnInit {
|
|||||||
|
|
||||||
labelValue: string;
|
labelValue: string;
|
||||||
|
|
||||||
entityDetected = false;
|
datasourceDetected = false;
|
||||||
dataKeyDetected = false;
|
|
||||||
isValidParameter = false;
|
|
||||||
errorMessage: string;
|
errorMessage: string;
|
||||||
|
|
||||||
isFocused: boolean;
|
isFocused: boolean;
|
||||||
@ -111,30 +109,26 @@ export class JsonInputWidgetComponent extends PageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private validateDatasources() {
|
private validateDatasources() {
|
||||||
if (this.datasource?.type === DatasourceType.entity) {
|
this.datasourceDetected = isDefinedAndNotNull(this.datasource);
|
||||||
this.entityDetected = true;
|
if (!this.datasourceDetected) {
|
||||||
if (this.datasource.dataKeys.length) {
|
return;
|
||||||
this.dataKeyDetected = true;
|
}
|
||||||
|
if (this.datasource.type === DatasourceType.entity) {
|
||||||
if (this.settings.widgetMode === JsonInputWidgetMode.ATTRIBUTE) {
|
if (this.settings.widgetMode === JsonInputWidgetMode.ATTRIBUTE) {
|
||||||
if (this.datasource.dataKeys[0].type === DataKeyType.attribute) {
|
if (this.datasource.dataKeys[0].type === DataKeyType.attribute) {
|
||||||
if (this.settings.attributeScope === AttributeScope.SERVER_SCOPE || this.datasource.entityType === EntityType.DEVICE) {
|
if (this.settings.attributeScope !== AttributeScope.SERVER_SCOPE && this.datasource.entityType !== EntityType.DEVICE) {
|
||||||
this.isValidParameter = true;
|
|
||||||
} else {
|
|
||||||
this.errorMessage = 'widgets.input-widgets.not-allowed-entity';
|
this.errorMessage = 'widgets.input-widgets.not-allowed-entity';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.errorMessage = 'widgets.input-widgets.no-attribute-selected';
|
this.errorMessage = 'widgets.input-widgets.no-attribute-selected';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.datasource.dataKeys[0].type === DataKeyType.timeseries) {
|
if (this.datasource.dataKeys[0].type !== DataKeyType.timeseries) {
|
||||||
this.isValidParameter = true;
|
|
||||||
} else {
|
|
||||||
this.errorMessage = 'widgets.input-widgets.no-timeseries-selected';
|
this.errorMessage = 'widgets.input-widgets.no-timeseries-selected';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
}
|
this.errorMessage = 'widgets.input-widgets.no-entity-selected';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +146,7 @@ export class JsonInputWidgetComponent extends PageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private updateWidgetData(data: Array<DatasourceData>) {
|
private updateWidgetData(data: Array<DatasourceData>) {
|
||||||
if (this.isValidParameter) {
|
if (!this.errorMessage) {
|
||||||
let value = {};
|
let value = {};
|
||||||
if (data[0].data[0][1] !== '') {
|
if (data[0].data[0][1] !== '') {
|
||||||
try {
|
try {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -32,6 +32,9 @@
|
|||||||
|| ruleNodeFormGroup.get('name').hasError('pattern')">
|
|| ruleNodeFormGroup.get('name').hasError('pattern')">
|
||||||
{{ 'rulenode.name-required' | translate }}
|
{{ 'rulenode.name-required' | translate }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
<mat-error *ngIf="ruleNodeFormGroup.get('name').hasError('maxlength')">
|
||||||
|
{{ 'rulenode.name-max-length' | translate }}
|
||||||
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-checkbox formControlName="debugMode">
|
<mat-checkbox formControlName="debugMode">
|
||||||
{{ 'rulenode.debug-mode' | translate }}
|
{{ 'rulenode.debug-mode' | translate }}
|
||||||
|
|||||||
@ -78,7 +78,7 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O
|
|||||||
if (this.ruleNode.component.type !== RuleNodeType.RULE_CHAIN) {
|
if (this.ruleNode.component.type !== RuleNodeType.RULE_CHAIN) {
|
||||||
|
|
||||||
this.ruleNodeFormGroup = this.fb.group({
|
this.ruleNodeFormGroup = this.fb.group({
|
||||||
name: [this.ruleNode.name, [Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*')]],
|
name: [this.ruleNode.name, [Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*'), Validators.maxLength(255)]],
|
||||||
debugMode: [this.ruleNode.debugMode, []],
|
debugMode: [this.ruleNode.debugMode, []],
|
||||||
configuration: [this.ruleNode.configuration, [Validators.required]],
|
configuration: [this.ruleNode.configuration, [Validators.required]],
|
||||||
additionalInfo: this.fb.group(
|
additionalInfo: this.fb.group(
|
||||||
|
|||||||
@ -1169,6 +1169,7 @@
|
|||||||
"alarm-type": "Alarm type",
|
"alarm-type": "Alarm type",
|
||||||
"alarm-type-required": "Alarm type is required.",
|
"alarm-type-required": "Alarm type is required.",
|
||||||
"alarm-type-unique": "Alarm type must be unique within the device profile alarm rules.",
|
"alarm-type-unique": "Alarm type must be unique within the device profile alarm rules.",
|
||||||
|
"alarm-type-max-length": "Alarm type should be less than 256",
|
||||||
"create-alarm-pattern": "Create <b>{{alarmType}}</b> alarm",
|
"create-alarm-pattern": "Create <b>{{alarmType}}</b> alarm",
|
||||||
"create-alarm-rules": "Create alarm rules",
|
"create-alarm-rules": "Create alarm rules",
|
||||||
"no-create-alarm-rules": "No create conditions configured",
|
"no-create-alarm-rules": "No create conditions configured",
|
||||||
@ -2578,6 +2579,7 @@
|
|||||||
"add": "Add rule node",
|
"add": "Add rule node",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"name-required": "Name is required.",
|
"name-required": "Name is required.",
|
||||||
|
"name-max-length": "Name should be less than 256",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"delete": "Delete rule node",
|
"delete": "Delete rule node",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user