diff --git a/application/pom.xml b/application/pom.xml index 112cc82ff7..d97f6d92e3 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -390,6 +390,10 @@ + + io.github.git-commit-id + git-commit-id-maven-plugin + org.apache.maven.plugins maven-compiler-plugin diff --git a/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg index 5576c939c4..bfd711cd38 100644 --- a/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg @@ -18,12 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = ctx.properties.maxValue;\n }\n \n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = Number.MAX_SAFE_INTEGER;\n }\n \n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { @@ -33,12 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = ctx.properties.minValue;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = Number.MIN_SAFE_INTEGER;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg index b7d5e0184b..5dd6dd43a3 100644 --- a/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg @@ -18,12 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = ctx.properties.maxValue;\n }\n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = Number.MAX_SAFE_INTEGER;\n }\n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { @@ -33,12 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = ctx.properties.minValue;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = Number.MIN_SAFE_INTEGER;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg index e69daae548..7b43164cce 100644 --- a/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg @@ -18,12 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.properties.highCriticalScale;\nvar value = ctx.values.value;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.properties.highCriticalScale;\nvar value = ctx.values.value;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nvar value = ctx.values.value;\n\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nvar value = ctx.values.value;\n\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = Number.MAX_SAFE_INTEGER;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { @@ -33,12 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\n\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n var childrenElement = element.children();\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\n\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n var childrenElement = element.children();\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = Number.MIN_SAFE_INTEGER;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg index 7f9244eaea..8cceb84a74 100644 --- a/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg @@ -18,12 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalScale = ctx.properties.highCriticalScale;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalScale, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalScale = ctx.properties.highCriticalScale;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalScale, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = Number.MAX_SAFE_INTEGER;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { @@ -33,12 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = Number.MIN_SAFE_INTEGER;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { diff --git a/application/src/main/data/upgrade/basic/schema_update.sql b/application/src/main/data/upgrade/basic/schema_update.sql index 9390238139..ebe63a6b3a 100644 --- a/application/src/main/data/upgrade/basic/schema_update.sql +++ b/application/src/main/data/upgrade/basic/schema_update.sql @@ -143,7 +143,7 @@ $$ LOOP generatedBundleId := NULL; -- migrate android config - IF (qrCodeRecord.android_config IS NOT NULL AND qrCodeRecord.android_config::jsonb -> 'appPackage' IS NOT NULL) THEN + IF (qrCodeRecord.android_config::jsonb ->> 'appPackage' IS NOT NULL) THEN androidPkgName := qrCodeRecord.android_config::jsonb ->> 'appPackage'; SELECT id into androidAppId FROM mobile_app WHERE pkg_name = androidPkgName AND platform_type = 'ANDROID'; IF androidAppId IS NULL THEN @@ -154,17 +154,16 @@ $$ generatedBundleId := uuid_generate_v4(); INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, android_app_id) VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, androidPkgName || ' (autogenerated)', androidAppId); - UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId, - android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id; + UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId; ELSE UPDATE mobile_app SET store_info = qrCodeRecord.android_config::jsonb - 'appPackage' - 'enabled' WHERE id = androidAppId; - UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.android_app_id = androidAppId), - android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id; + UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.android_app_id = androidAppId); END IF; END IF; + UPDATE qr_code_settings SET android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id; -- migrate ios config - IF (qrCodeRecord.ios_config IS NOT NULL AND qrCodeRecord.ios_config::jsonb -> 'appId' IS NOT NULL) THEN + IF (qrCodeRecord.ios_config::jsonb ->> 'appId' IS NOT NULL) THEN iosPkgName := substring(qrCodeRecord.ios_config::jsonb ->> 'appId', strpos(qrCodeRecord.ios_config::jsonb ->> 'appId', '.') + 1); SELECT id INTO iosAppId FROM mobile_app WHERE pkg_name = iosPkgName AND platform_type = 'IOS'; IF iosAppId IS NULL THEN @@ -176,24 +175,35 @@ $$ generatedBundleId := uuid_generate_v4(); INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, ios_app_id) VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, iosPkgName || ' (autogenerated)', iosAppId); - UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId, - ios_enabled = (qrCodeRecord.ios_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id; + UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId; ELSE UPDATE mobile_app_bundle SET ios_app_id = iosAppId WHERE id = generatedBundleId; END IF; ELSE - UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.ios_app_id = iosAppId), - ios_enabled = (qrCodeRecord.ios_config::jsonb -> 'enabled')::boolean WHERE id = qrCodeRecord.id; + UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.ios_app_id = iosAppId); UPDATE mobile_app SET store_info = qrCodeRecord.ios_config::jsonb - 'enabled' WHERE id = iosAppId; END IF; END IF; + UPDATE qr_code_settings SET ios_enabled = (qrCodeRecord.ios_config::jsonb -> 'enabled')::boolean WHERE id = qrCodeRecord.id; END LOOP; ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_tenant_id_unq_key TO qr_code_settings_tenant_id_unq_key; + ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_pkey TO qr_code_settings_pkey; END IF; ALTER TABLE qr_code_settings DROP COLUMN IF EXISTS android_config, DROP COLUMN IF EXISTS ios_config; END; $$; +-- update constraint name +DO +$$ + BEGIN + ALTER TABLE domain DROP CONSTRAINT IF EXISTS domain_unq_key; + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'domain_name_key') THEN + ALTER TABLE domain ADD CONSTRAINT domain_name_key UNIQUE (name); + END IF; + END; +$$; + -- UPDATE RESOURCE JS_MODULE SUB TYPE START UPDATE resource SET resource_sub_type = 'EXTENSION' WHERE resource_type = 'JS_MODULE' AND resource_sub_type IS NULL; diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 0070eba4a1..c3c524b2bb 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -173,7 +173,10 @@ public class DefaultTbContext implements TbContext { if (!msg.isValid()) { return; } - TbMsg tbMsg = msg.copyWithRuleChainId(ruleChainId); + TbMsg tbMsg = msg.copy() + .ruleChainId(ruleChainId) + .resetRuleNodeId() + .build(); tbMsg.pushToStack(nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId()); TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getQueueName(), getTenantId(), tbMsg.getOriginator()); doEnqueue(tpi, tbMsg, new SimpleTbQueueCallback(md -> ack(msg), t -> tellFailure(msg, t))); @@ -361,19 +364,28 @@ public class DefaultTbContext implements TbContext { nodeCtx.setSelf(self); } - @Override - public TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) { - return newMsg(queueName, type, originator, null, metaData, data); - } - @Override public TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return TbMsg.newMsg(queueName, type, originator, customerId, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId()); + return TbMsg.newMsg() + .queueName(queueName) + .type(type) + .originator(originator) + .customerId(customerId) + .copyMetaData(metaData) + .data(data) + .ruleChainId(nodeCtx.getSelf().getRuleChainId()) + .ruleNodeId(nodeCtx.getSelf().getId()) + .build(); } @Override public TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { - return TbMsg.transformMsg(origMsg, type, originator, metaData, data); + return origMsg.transform() + .type(type) + .originator(originator) + .metaData(metaData) + .data(data) + .build(); } @Override @@ -383,22 +395,41 @@ public class DefaultTbContext implements TbContext { @Override public TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return TbMsg.newMsg(queueName, type, originator, customerId, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId()); + return TbMsg.newMsg() + .queueName(queueName) + .type(type) + .originator(originator) + .customerId(customerId) + .copyMetaData(metaData) + .data(data) + .ruleChainId(nodeCtx.getSelf().getRuleChainId()) + .ruleNodeId(nodeCtx.getSelf().getId()) + .build(); } @Override public TbMsg transformMsg(TbMsg origMsg, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { - return TbMsg.transformMsg(origMsg, type, originator, metaData, data); + return origMsg.transform() + .type(type) + .originator(originator) + .metaData(metaData) + .data(data) + .build(); } @Override public TbMsg transformMsg(TbMsg origMsg, TbMsgMetaData metaData, String data) { - return TbMsg.transformMsg(origMsg, metaData, data); + return origMsg.transform() + .metaData(metaData) + .data(data) + .build(); } @Override public TbMsg transformMsgOriginator(TbMsg origMsg, EntityId originator) { - return TbMsg.transformMsgOriginator(origMsg, originator); + return origMsg.transform() + .originator(originator) + .build(); } @Override @@ -497,7 +528,14 @@ public class DefaultTbContext implements TbContext { defaultQueueName = profile.getDefaultQueueName(); defaultRuleChainId = profile.getDefaultRuleChainId(); } - return TbMsg.newMsg(defaultQueueName, action, id, msgMetaData, msgData, defaultRuleChainId, null); + return TbMsg.newMsg() + .queueName(defaultQueueName) + .type(action) + .originator(id) + .copyMetaData(msgMetaData) + .data(msgData) + .ruleChainId(defaultRuleChainId) + .build(); } public TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, TbMsgType actionMsgType, K profile) { @@ -515,7 +553,14 @@ public class DefaultTbContext implements TbContext { defaultQueueName = profile.getDefaultQueueName(); defaultRuleChainId = profile.getDefaultRuleChainId(); } - return TbMsg.newMsg(defaultQueueName, actionMsgType, id, msgMetaData, msgData, defaultRuleChainId, null); + return TbMsg.newMsg() + .queueName(defaultQueueName) + .type(actionMsgType) + .originator(id) + .copyMetaData(msgMetaData) + .data(msgData) + .ruleChainId(defaultRuleChainId) + .build(); } @Override diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index 460da228c3..be0812795d 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -16,6 +16,7 @@ package org.thingsboard.server.actors.ruleChain; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.DebugModeUtil; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.TbActorRef; @@ -35,7 +36,6 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; -import org.thingsboard.common.util.DebugModeUtil; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.plugin.RuleNodeUpdatedMsg; @@ -217,7 +217,10 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor reply(new LocalRequestMetaData(msg, currentUser, result), reply)); } diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java index b1733a9af0..fa69c99245 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -47,6 +47,10 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; +import org.thingsboard.rule.engine.api.TimeseriesDeleteRequest; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.EntityType; @@ -399,7 +403,7 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityAttributesV2( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, - @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope")AttributeScope scope, + @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope") AttributeScope scope, @io.swagger.v3.oas.annotations.parameters.RequestBody(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return saveAttributes(getTenantId(), entityId, scope, request); @@ -423,8 +427,8 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityTelemetry( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, - @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")String scope, - @io.swagger.v3.oas.annotations.parameters.RequestBody(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException { + @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope") String scope, + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody String requestBody) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return saveTelemetry(getTenantId(), entityId, requestBody, 0L); } @@ -447,9 +451,9 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityTelemetryWithTTL( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, - @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")String scope, - @Parameter(description = "A long value representing TTL (Time to Live) parameter.", required = true)@PathVariable("ttl")Long ttl, - @io.swagger.v3.oas.annotations.parameters.RequestBody(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException { + @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope") String scope, + @Parameter(description = "A long value representing TTL (Time to Live) parameter.", required = true) @PathVariable("ttl") Long ttl, + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody String requestBody) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return saveTelemetry(getTenantId(), entityId, requestBody, ttl); } @@ -518,19 +522,25 @@ public class TelemetryController extends BaseController { for (String key : keys) { deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted, deleteLatest)); } - tsSubService.deleteTimeseriesAndNotify(tenantId, entityId, keys, deleteTsKvQueries, new FutureCallback<>() { - @Override - public void onSuccess(@Nullable Void tmp) { - logTimeseriesDeleted(user, entityId, keys, deleteFromTs, deleteToTs, null); - result.setResult(new ResponseEntity<>(HttpStatus.OK)); - } - - @Override - public void onFailure(Throwable t) { - logTimeseriesDeleted(user, entityId, keys, deleteFromTs, deleteToTs, t); - result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); - } - }); + tsSubService.deleteTimeseries(TimeseriesDeleteRequest.builder() + .tenantId(tenantId) + .entityId(entityId) + .keys(keys) + .deleteHistoryQueries(deleteTsKvQueries) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable List tmp) { + logTimeseriesDeleted(user, entityId, keys, deleteFromTs, deleteToTs, null); + result.setResult(new ResponseEntity<>(HttpStatus.OK)); + } + + @Override + public void onFailure(Throwable t) { + logTimeseriesDeleted(user, entityId, keys, deleteFromTs, deleteToTs, t); + result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + } + }) + .build()); }); } @@ -550,8 +560,8 @@ public class TelemetryController extends BaseController { @ResponseBody public DeferredResult deleteDeviceAttributes( @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(DEVICE_ID) String deviceIdStr, - @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope")AttributeScope scope, - @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true)@RequestParam(name = "keys")String keysStr) throws ThingsboardException { + @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope") AttributeScope scope, + @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); return deleteAttributes(entityId, scope, keysStr); } @@ -573,8 +583,8 @@ public class TelemetryController extends BaseController { public DeferredResult deleteEntityAttributes( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, - @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"})) @PathVariable("scope")AttributeScope scope, - @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true)@RequestParam(name = "keys")String keysStr) throws ThingsboardException { + @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"})) @PathVariable("scope") AttributeScope scope, + @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return deleteAttributes(entityId, scope, keysStr); } @@ -587,24 +597,30 @@ public class TelemetryController extends BaseController { SecurityUser user = getCurrentUser(); return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> { - tsSubService.deleteAndNotify(tenantId, entityId, scope.name(), keys, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - logAttributesDeleted(user, entityId, scope, keys, null); - if (entityIdSrc.getEntityType().equals(EntityType.DEVICE)) { - DeviceId deviceId = new DeviceId(entityId.getId()); - tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete( - user.getTenantId(), deviceId, scope.name(), keys), null); - } - result.setResult(new ResponseEntity<>(HttpStatus.OK)); - } - - @Override - public void onFailure(Throwable t) { - logAttributesDeleted(user, entityId, scope, keys, t); - result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); - } - }); + tsSubService.deleteAttributes(AttributesDeleteRequest.builder() + .tenantId(tenantId) + .entityId(entityId) + .scope(scope) + .keys(keys) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void tmp) { + logAttributesDeleted(user, entityId, scope, keys, null); + if (entityIdSrc.getEntityType().equals(EntityType.DEVICE)) { + DeviceId deviceId = new DeviceId(entityId.getId()); + tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete( + user.getTenantId(), deviceId, scope.name(), keys), null); + } + result.setResult(new ResponseEntity<>(HttpStatus.OK)); + } + + @Override + public void onFailure(Throwable t) { + logAttributesDeleted(user, entityId, scope, keys, t); + result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + } + }) + .build()); }); } @@ -624,19 +640,25 @@ public class TelemetryController extends BaseController { } SecurityUser user = getCurrentUser(); return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> { - tsSubService.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - logAttributesUpdated(user, entityId, scope, attributes, null); - result.setResult(new ResponseEntity(HttpStatus.OK)); - } - - @Override - public void onFailure(Throwable t) { - logAttributesUpdated(user, entityId, scope, attributes, t); - AccessValidator.handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR); - } - }); + tsSubService.saveAttributes(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(entityId) + .scope(scope) + .entries(attributes) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void tmp) { + logAttributesUpdated(user, entityId, scope, attributes, null); + result.setResult(new ResponseEntity(HttpStatus.OK)); + } + + @Override + public void onFailure(Throwable t) { + logAttributesUpdated(user, entityId, scope, attributes, t); + AccessValidator.handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR); + } + }) + .build()); }); } else { return getImmediateDeferredResult("Request is not a JSON object", HttpStatus.BAD_REQUEST); @@ -672,19 +694,26 @@ public class TelemetryController extends BaseController { TenantProfile tenantProfile = tenantProfileCache.get(tenantId); tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays()); } - tsSubService.saveAndNotify(tenantId, user.getCustomerId(), entityId, entries, tenantTtl, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - logTelemetryUpdated(user, entityId, entries, null); - result.setResult(new ResponseEntity(HttpStatus.OK)); - } - - @Override - public void onFailure(Throwable t) { - logTelemetryUpdated(user, entityId, entries, t); - AccessValidator.handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR); - } - }); + tsSubService.saveTimeseries(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .customerId(user.getCustomerId()) + .entityId(entityId) + .entries(entries) + .ttl(tenantTtl) + .callback(new FutureCallback() { + @Override + public void onSuccess(@Nullable Void tmp) { + logTelemetryUpdated(user, entityId, entries, null); + result.setResult(new ResponseEntity(HttpStatus.OK)); + } + + @Override + public void onFailure(Throwable t) { + logTelemetryUpdated(user, entityId, entries, t); + AccessValidator.handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR); + } + }) + .build()); }); } diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java index 8d39e6c83d..307343f6fb 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java @@ -90,7 +90,7 @@ public class WidgetsBundleController extends BaseController { WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); var result = checkWidgetsBundleId(widgetsBundleId, Operation.READ); if (inlineImages) { - imageService.inlineImage(result); + result = imageService.inlineImage(result); } return result; } diff --git a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java index ab6d32fb44..868adf645a 100644 --- a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java +++ b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java @@ -172,7 +172,14 @@ public class EntityActionService { if (tenantId != null && !tenantId.isSysTenantId()) { processNotificationRules(tenantId, entityId, entity, actionType, user, additionalInfo); } - TbMsg tbMsg = TbMsg.newMsg(msgType.get(), entityId, customerId, metaData, TbMsgDataType.JSON, JacksonUtil.toString(entityNode)); + TbMsg tbMsg = TbMsg.newMsg() + .type(msgType.get()) + .originator(entityId) + .customerId(customerId) + .copyMetaData(metaData) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(entityNode)) + .build(); tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, null); } catch (Exception e) { log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java index 60bf529ace..3a351528e4 100644 --- a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java @@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.rule.engine.api.MailService; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.ApiFeature; import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.ApiUsageRecordState; @@ -91,9 +92,9 @@ import java.util.stream.Collectors; public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService implements TbApiUsageStateService { public static final String HOURLY = "Hourly"; - public static final FutureCallback VOID_CALLBACK = new FutureCallback() { + public static final FutureCallback VOID_CALLBACK = new FutureCallback() { @Override - public void onSuccess(@Nullable Integer result) { + public void onSuccess(@Nullable Void result) { } @Override @@ -214,7 +215,12 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService updateLock.unlock(); } log.trace("[{}][{}] Saving new stats: {}", tenantId, ownerId, updatedEntries); - tsWsService.saveAndNotifyInternal(tenantId, usageState.getApiUsageState().getId(), updatedEntries, VOID_CALLBACK); + tsWsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(usageState.getApiUsageState().getId()) + .entries(updatedEntries) + .callback(VOID_CALLBACK) + .build()); if (!result.isEmpty()) { persistAndNotify(usageState, result); } @@ -321,7 +327,12 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService } } if (!profileThresholds.isEmpty()) { - tsWsService.saveAndNotifyInternal(tenantId, id, profileThresholds, VOID_CALLBACK); + tsWsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(id) + .entries(profileThresholds) + .callback(VOID_CALLBACK) + .build()); } } @@ -348,7 +359,12 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService long ts = System.currentTimeMillis(); List stateTelemetry = new ArrayList<>(); result.forEach((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name())))); - tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK); + tsWsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() + .tenantId(state.getTenantId()) + .entityId(state.getApiUsageState().getId()) + .entries(stateTelemetry) + .callback(VOID_CALLBACK) + .build()); if (state.getEntityType() == EntityType.TENANT && !state.getEntityId().equals(TenantId.SYS_TENANT_ID)) { String email = tenantService.findTenantById(state.getTenantId()).getEmail(); @@ -436,7 +452,12 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService .map(key -> new BasicTsKvEntry(state.getCurrentCycleTs(), new LongDataEntry(key.getApiCountKey(), 0L))) .collect(Collectors.toList()); - tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), counts, VOID_CALLBACK); + tsWsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() + .tenantId(state.getTenantId()) + .entityId(state.getApiUsageState().getId()) + .entries(counts) + .callback(VOID_CALLBACK) + .build()); } BaseApiUsageState getOrFetchState(TenantId tenantId, EntityId ownerId) { diff --git a/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java b/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java index a77b7d0874..f82f4c74d5 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java +++ b/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java @@ -28,6 +28,8 @@ import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Customer; @@ -37,7 +39,6 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.customer.CustomerService; @@ -178,11 +179,12 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService { return Futures.immediateFuture(new ReclaimResult(unassignedCustomer)); } SettableFuture result = SettableFuture.create(); - telemetryService.saveAndNotify( - tenantId, savedDevice.getId(), AttributeScope.SERVER_SCOPE, List.of( - new BaseAttributeKvEntry(new BooleanDataEntry(CLAIM_ATTRIBUTE_NAME, true), System.currentTimeMillis()) - ), - new FutureCallback<>() { + telemetryService.saveAttributes(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(savedDevice.getId()) + .scope(AttributeScope.SERVER_SCOPE) + .entry(new BooleanDataEntry(CLAIM_ATTRIBUTE_NAME, true)) + .callback(new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) { result.set(new ReclaimResult(unassignedCustomer)); @@ -192,7 +194,8 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService { public void onFailure(Throwable t) { result.setException(t); } - }); + }) + .build()); return result; } cacheEviction(device.getId()); @@ -221,18 +224,13 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService { cache.evict(data.getKey()); } SettableFuture result = SettableFuture.create(); - telemetryService.deleteAndNotify(device.getTenantId(), - device.getId(), AttributeScope.SERVER_SCOPE, Arrays.asList(CLAIM_ATTRIBUTE_NAME, CLAIM_DATA_ATTRIBUTE_NAME), new FutureCallback<>() { - @Override - public void onSuccess(@Nullable Void tmp) { - result.set(tmp); - } - - @Override - public void onFailure(Throwable t) { - result.setException(t); - } - }); + telemetryService.deleteAttributes(AttributesDeleteRequest.builder() + .tenantId(device.getTenantId()) + .entityId(device.getId()) + .scope(AttributeScope.SERVER_SCOPE) + .keys(Arrays.asList(CLAIM_ATTRIBUTE_NAME, CLAIM_DATA_ATTRIBUTE_NAME)) + .future(result) + .build()); return result; } diff --git a/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java b/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java index f5b6411bf7..0fddcff793 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java +++ b/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java @@ -253,7 +253,13 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, TbMsgType type) { try { JsonNode entityNode = JacksonUtil.valueToTree(request); - TbMsg msg = TbMsg.newMsg(type, device.getId(), device.getCustomerId(), createTbMsgMetaData(device), JacksonUtil.toString(entityNode)); + TbMsg msg = TbMsg.newMsg() + .type(type) + .originator(device.getId()) + .customerId(device.getCustomerId()) + .copyMetaData(createTbMsgMetaData(device)) + .data(JacksonUtil.toString(entityNode)) + .build(); sendToRuleEngine(device.getTenantId(), msg, null); } catch (IllegalArgumentException e) { log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), type, e); @@ -263,7 +269,13 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { private void pushDeviceCreatedEventToRuleEngine(Device device) { try { ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device); - TbMsg msg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, device.getId(), device.getCustomerId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.ENTITY_CREATED) + .originator(device.getId()) + .customerId(device.getCustomerId()) + .copyMetaData(createTbMsgMetaData(device)) + .data(JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)) + .build(); sendToRuleEngine(device.getTenantId(), msg, null); } catch (JsonProcessingException | IllegalArgumentException e) { log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), TbMsgType.ENTITY_CREATED.name(), e); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java index 71387aa542..9af0046f53 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java @@ -32,6 +32,8 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.cache.TbTransactionalCache; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.AttributeScope; @@ -41,7 +43,6 @@ import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.msg.TbMsgType; @@ -68,7 +69,6 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import java.io.IOException; import java.io.InputStream; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -413,23 +413,27 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i if (Boolean.TRUE.equals(sessionNewEvents.get(edgeId))) { log.trace("[{}][{}] Set session new events flag to false", tenantId, edgeId.getId()); sessionNewEvents.put(edgeId, false); - processEdgeEventMigrationIfNeeded(session, edgeId); session.processHighPriorityEvents(); - Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() { - @Override - public void onSuccess(Boolean newEventsAdded) { - if (Boolean.TRUE.equals(newEventsAdded)) { - sessionNewEvents.put(edgeId, true); + processEdgeEventMigrationIfNeeded(session, edgeId); + if (Boolean.TRUE.equals(edgeEventsMigrationProcessed.get(edgeId))) { + Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() { + @Override + public void onSuccess(Boolean newEventsAdded) { + if (Boolean.TRUE.equals(newEventsAdded)) { + sessionNewEvents.put(edgeId, true); + } + scheduleEdgeEventsCheck(session); } - scheduleEdgeEventsCheck(session); - } - - @Override - public void onFailure(Throwable t) { - log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), t); - scheduleEdgeEventsCheck(session); - } - }, ctx.getGrpcCallbackExecutorService()); + + @Override + public void onFailure(Throwable t) { + log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), t); + scheduleEdgeEventsCheck(session); + } + }, ctx.getGrpcCallbackExecutorService()); + } else { + scheduleEdgeEventsCheck(session); + } } else { scheduleEdgeEventsCheck(session); } @@ -457,8 +461,6 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i scheduleEdgeEventsCheck(session); } else if (Boolean.FALSE.equals(eventsExist)) { edgeEventsMigrationProcessed.put(edgeId, true); - } else { - scheduleEdgeEventsCheck(session); } } } @@ -503,24 +505,40 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i private void save(TenantId tenantId, EdgeId edgeId, String key, long value) { log.debug("[{}][{}] Updating long edge telemetry [{}] [{}]", tenantId, edgeId, key, value); if (persistToTelemetry) { - tsSubService.saveAndNotify( - tenantId, edgeId, - Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value))), - new AttributeSaveCallback(tenantId, edgeId, key, value)); + tsSubService.saveTimeseries(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(edgeId) + .entry(new LongDataEntry(key, value)) + .callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) + .build()); } else { - tsSubService.saveAttrAndNotify(tenantId, edgeId, AttributeScope.SERVER_SCOPE, key, value, new AttributeSaveCallback(tenantId, edgeId, key, value)); + tsSubService.saveAttributes(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(edgeId) + .scope(AttributeScope.SERVER_SCOPE) + .entry(new LongDataEntry(key, value)) + .callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) + .build()); } } private void save(TenantId tenantId, EdgeId edgeId, String key, boolean value) { log.debug("[{}][{}] Updating boolean edge telemetry [{}] [{}]", tenantId, edgeId, key, value); if (persistToTelemetry) { - tsSubService.saveAndNotify( - tenantId, edgeId, - Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))), - new AttributeSaveCallback(tenantId, edgeId, key, value)); + tsSubService.saveTimeseries(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(edgeId) + .entry(new BooleanDataEntry(key, value)) + .callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) + .build()); } else { - tsSubService.saveAttrAndNotify(tenantId, edgeId, AttributeScope.SERVER_SCOPE, key, value, new AttributeSaveCallback(tenantId, edgeId, key, value)); + tsSubService.saveAttributes(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(edgeId) + .scope(AttributeScope.SERVER_SCOPE) + .entry(new BooleanDataEntry(key, value)) + .callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) + .build()); } } @@ -575,7 +593,13 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i md.putValue("edgeName", edge.getName()); md.putValue("edgeType", edge.getType()); } - TbMsg tbMsg = TbMsg.newMsg(msgType, edgeId, md, TbMsgDataType.JSON, data); + TbMsg tbMsg = TbMsg.newMsg() + .type(msgType) + .originator(edgeId) + .copyMetaData(md) + .dataType(TbMsgDataType.JSON) + .data(data) + .build(); clusterService.pushMsgToRuleEngine(tenantId, edgeId, tbMsg, null); } catch (Exception e) { log.warn("[{}][{}] Failed to push {}", tenantId, edge.getId(), msgType, e); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index f0bcbf2768..7c37b0ac54 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -110,7 +110,7 @@ public abstract class EdgeGrpcSession implements Closeable { private static final String QUEUE_START_TS_ATTR_KEY = "queueStartTs"; private static final String QUEUE_START_SEQ_ID_ATTR_KEY = "queueStartSeqId"; - private static final int MAX_DOWNLINK_ATTEMPTS = 10; + private static final int MAX_DOWNLINK_ATTEMPTS = 3; private static final String RATE_LIMIT_REACHED = "Rate limit reached"; protected static final ConcurrentLinkedQueue highPriorityQueue = new ConcurrentLinkedQueue<>(); @@ -135,7 +135,7 @@ public abstract class EdgeGrpcSession implements Closeable { private StreamObserver inputStream; private StreamObserver outputStream; - private boolean connected; + private volatile boolean connected; private volatile boolean syncCompleted; private EdgeVersion edgeVersion; @@ -536,13 +536,15 @@ public abstract class EdgeGrpcSession implements Closeable { public void processHighPriorityEvents() { try { - List highPriorityEvents = new ArrayList<>(); - EdgeEvent event; - while ((event = highPriorityQueue.poll()) != null) { - highPriorityEvents.add(event); + if (isConnected() && isSyncCompleted()) { + List highPriorityEvents = new ArrayList<>(); + EdgeEvent event; + while ((event = highPriorityQueue.poll()) != null) { + highPriorityEvents.add(event); + } + List downlinkMsgsPack = convertToDownlinkMsgsPack(highPriorityEvents); + sendDownlinkMsgsPack(downlinkMsgsPack).get(); } - List downlinkMsgsPack = convertToDownlinkMsgsPack(highPriorityEvents); - sendDownlinkMsgsPack(downlinkMsgsPack).get(); } catch (Exception e) { log.error("[{}] Failed to process high priority events", sessionId, e); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java index e448fbd937..eaf3c1a95a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java @@ -343,7 +343,14 @@ public abstract class BaseEdgeProcessor { protected void pushEntityEventToRuleEngine(TenantId tenantId, EntityId entityId, CustomerId customerId, TbMsgType msgType, String msgData, TbMsgMetaData metaData) { - TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, msgData); + TbMsg tbMsg = TbMsg.newMsg() + .type(msgType) + .originator(entityId) + .customerId(customerId) + .copyMetaData(metaData) + .dataType(TbMsgDataType.JSON) + .data(msgData) + .build(); edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, entityId, tbMsg, new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java index e453cbc26f..c4a57e4a74 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java @@ -190,8 +190,13 @@ public abstract class DeviceEdgeProcessor extends BaseDeviceProcessor implements ObjectNode data = JacksonUtil.newObjectNode(); data.put("method", deviceRpcCallMsg.getRequestMsg().getMethod()); data.put("params", deviceRpcCallMsg.getRequestMsg().getParams()); - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.TO_SERVER_RPC_REQUEST, deviceId, null, metaData, - TbMsgDataType.JSON, JacksonUtil.toString(data)); + TbMsg tbMsg = TbMsg.newMsg() + .type(TbMsgType.TO_SERVER_RPC_REQUEST) + .originator(deviceId) + .copyMetaData(metaData) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java index d94d4d8939..488fc3d8b9 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java @@ -31,6 +31,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.DataConstants; @@ -208,7 +209,15 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); metaData.putValue("ts", tsKv.getTs() + ""); var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), TbMsgType.POST_TELEMETRY_REQUEST, entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); + TbMsg tbMsg = TbMsg.newMsg() + .queueName(defaultQueueAndRuleChain.getKey()) + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(entityId) + .customerId(customerId) + .copyMetaData(metaData) + .data(gson.toJson(json)) + .ruleChainId(defaultQueueAndRuleChain.getValue()) + .build(); edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { @@ -252,7 +261,15 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { SettableFuture futureToSet = SettableFuture.create(); JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); + TbMsg tbMsg = TbMsg.newMsg() + .queueName(defaultQueueAndRuleChain.getKey()) + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .customerId(customerId) + .copyMetaData(metaData) + .data(gson.toJson(json)) + .ruleChainId(defaultQueueAndRuleChain.getValue()) + .build(); edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { @@ -277,16 +294,36 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); List attributes = new ArrayList<>(JsonConverter.convertToAttributes(json)); String scope = metaData.getValue("scope"); - tsSubService.saveAndNotify(tenantId, entityId, AttributeScope.valueOf(scope), attributes, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), TbMsgType.ATTRIBUTES_UPDATED, entityId, - customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); - edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { + tsSubService.saveAttributes(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(entityId) + .scope(AttributeScope.valueOf(scope)) + .entries(attributes) + .callback(new FutureCallback<>() { @Override - public void onSuccess(TbQueueMsgMetadata metadata) { - futureToSet.set(null); + public void onSuccess(@Nullable Void tmp) { + var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); + TbMsg tbMsg = TbMsg.newMsg() + .queueName(defaultQueueAndRuleChain.getKey()) + .type(TbMsgType.ATTRIBUTES_UPDATED) + .originator(entityId) + .customerId(customerId) + .copyMetaData(metaData) + .data(gson.toJson(json)) + .ruleChainId(defaultQueueAndRuleChain.getValue()) + .build(); + edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { + @Override + public void onSuccess(TbQueueMsgMetadata metadata) { + futureToSet.set(null); + } + + @Override + public void onFailure(Throwable t) { + log.error("[{}] Can't process attributes update [{}]", tenantId, msg, t); + futureToSet.setException(t); + } + }); } @Override @@ -294,15 +331,8 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { log.error("[{}] Can't process attributes update [{}]", tenantId, msg, t); futureToSet.setException(t); } - }); - } - - @Override - public void onFailure(Throwable t) { - log.error("[{}] Can't process attributes update [{}]", tenantId, msg, t); - futureToSet.setException(t); - } - }); + }) + .build()); return futureToSet; } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java index bce599c0e3..80368c085f 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java @@ -250,8 +250,14 @@ public class EntityStateSourcingListener { private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) { String data = JacksonUtil.toString(JacksonUtil.valueToTree(assignedDevice)); if (data != null) { - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_ASSIGNED_FROM_TENANT, assignedDevice.getId(), - assignedDevice.getCustomerId(), getMetaDataForAssignedFrom(currentTenant), TbMsgDataType.JSON, data); + TbMsg tbMsg = TbMsg.newMsg() + .type(TbMsgType.ENTITY_ASSIGNED_FROM_TENANT) + .originator(assignedDevice.getId()) + .customerId(assignedDevice.getCustomerId()) + .copyMetaData(getMetaDataForAssignedFrom(currentTenant)) + .dataType(TbMsgDataType.JSON) + .data(data) + .build(); tbClusterService.pushMsgToRuleEngine(newTenantId, assignedDevice.getId(), tbMsg, null); } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java index 82877283cd..686e85cb1e 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java @@ -25,6 +25,10 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.util.ConcurrentReferenceHashMap; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; +import org.thingsboard.rule.engine.api.TimeseriesDeleteRequest; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; @@ -55,6 +59,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; @@ -273,36 +278,41 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen return Futures.transform(getAttrFuture, attributeKvEntries -> { List attributes; if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) { - attributes = - attributeKvEntries.stream() - .filter(attributeKvEntry -> { - long startTime = entityView.getStartTimeMs(); - long endTime = entityView.getEndTimeMs(); - long lastUpdateTs = attributeKvEntry.getLastUpdateTs(); - return startTime == 0 && endTime == 0 || - (endTime == 0 && startTime < lastUpdateTs) || - (startTime == 0 && endTime > lastUpdateTs) || - (startTime < lastUpdateTs && endTime > lastUpdateTs); - }).collect(Collectors.toList()); - tsSubService.saveAndNotify(entityView.getTenantId(), entityId, scope, attributes, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - try { - logAttributesUpdated(entityView.getTenantId(), user, entityId, scope, attributes, null); - } catch (ThingsboardException e) { - log.error("Failed to log attribute updates", e); - } - } - - @Override - public void onFailure(Throwable t) { - try { - logAttributesUpdated(entityView.getTenantId(), user, entityId, scope, attributes, t); - } catch (ThingsboardException e) { - log.error("Failed to log attribute updates", e); - } - } - }); + attributes = attributeKvEntries.stream() + .filter(attributeKvEntry -> { + long startTime = entityView.getStartTimeMs(); + long endTime = entityView.getEndTimeMs(); + long lastUpdateTs = attributeKvEntry.getLastUpdateTs(); + return startTime == 0 && endTime == 0 || + (endTime == 0 && startTime < lastUpdateTs) || + (startTime == 0 && endTime > lastUpdateTs) || + (startTime < lastUpdateTs && endTime > lastUpdateTs); + }).collect(Collectors.toList()); + tsSubService.saveAttributes(AttributesSaveRequest.builder() + .tenantId(entityView.getTenantId()) + .entityId(entityId) + .scope(scope) + .entries(attributes) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void tmp) { + try { + logAttributesUpdated(entityView.getTenantId(), user, entityId, scope, attributes, null); + } catch (ThingsboardException e) { + log.error("Failed to log attribute updates", e); + } + } + + @Override + public void onFailure(Throwable t) { + try { + logAttributesUpdated(entityView.getTenantId(), user, entityId, scope, attributes, t); + } catch (ThingsboardException e) { + log.error("Failed to log attribute updates", e); + } + } + }) + .build()); } return null; }, MoreExecutors.directExecutor()); @@ -334,15 +344,22 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen }, MoreExecutors.directExecutor()); return Futures.transform(latestFuture, latestValues -> { if (latestValues != null && !latestValues.isEmpty()) { - tsSubService.saveLatestAndNotify(entityView.getTenantId(), entityId, latestValues, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - } + tsSubService.saveTimeseries(TimeseriesSaveRequest.builder() + .tenantId(entityView.getTenantId()) + .entityId(entityId) + .entries(latestValues) + .onlyLatest(true) + .callback(new FutureCallback() { + @Override + public void onSuccess(@Nullable Void tmp) { + } - @Override - public void onFailure(Throwable t) { - } - }); + @Override + public void onFailure(Throwable t) { + log.error("[{}][{}] Failed to save entity view latest timeseries: {}", tenantId, entityView.getId(), latestValues, t); + } + }) + .build()); } return null; }, MoreExecutors.directExecutor()); @@ -352,27 +369,33 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen EntityViewId entityId = entityView.getId(); SettableFuture resultFuture = SettableFuture.create(); if (keys != null && !keys.isEmpty()) { - tsSubService.deleteAndNotify(entityView.getTenantId(), entityId, scope, keys, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - try { - logAttributesDeleted(entityView.getTenantId(), user, entityId, scope, keys, null); - } catch (ThingsboardException e) { - log.error("Failed to log attribute delete", e); - } - resultFuture.set(tmp); - } + tsSubService.deleteAttributes(AttributesDeleteRequest.builder() + .tenantId(entityView.getTenantId()) + .entityId(entityId) + .scope(scope) + .keys(keys) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void tmp) { + try { + logAttributesDeleted(entityView.getTenantId(), user, entityId, scope, keys, null); + } catch (ThingsboardException e) { + log.error("Failed to log attribute delete", e); + } + resultFuture.set(tmp); + } - @Override - public void onFailure(Throwable t) { - try { - logAttributesDeleted(entityView.getTenantId(), user, entityId, scope, keys, t); - } catch (ThingsboardException e) { - log.error("Failed to log attribute delete", e); - } - resultFuture.setException(t); - } - }); + @Override + public void onFailure(Throwable t) { + try { + logAttributesDeleted(entityView.getTenantId(), user, entityId, scope, keys, t); + } catch (ThingsboardException e) { + log.error("Failed to log attribute delete", e); + } + resultFuture.setException(t); + } + }) + .build()); } else { resultFuture.set(null); } @@ -382,51 +405,32 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen private ListenableFuture deleteLatestFromEntityView(EntityView entityView, List keys, User user) { EntityViewId entityId = entityView.getId(); SettableFuture resultFuture = SettableFuture.create(); - if (keys != null && !keys.isEmpty()) { - tsSubService.deleteLatest(entityView.getTenantId(), entityId, keys, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - try { - logTimeseriesDeleted(entityView.getTenantId(), user, entityId, keys, null); - } catch (ThingsboardException e) { - log.error("Failed to log timeseries delete", e); - } - resultFuture.set(tmp); - } - - @Override - public void onFailure(Throwable t) { - try { - logTimeseriesDeleted(entityView.getTenantId(), user, entityId, keys, t); - } catch (ThingsboardException e) { - log.error("Failed to log timeseries delete", e); - } - resultFuture.setException(t); - } - }); - } else { - tsSubService.deleteAllLatest(entityView.getTenantId(), entityId, new FutureCallback>() { - @Override - public void onSuccess(@Nullable Collection keys) { - try { - logTimeseriesDeleted(entityView.getTenantId(), user, entityId, new ArrayList<>(keys), null); - } catch (ThingsboardException e) { - log.error("Failed to log timeseries delete", e); + tsSubService.deleteTimeseries(TimeseriesDeleteRequest.builder() + .tenantId(entityView.getTenantId()) + .entityId(entityId) + .keys(keys) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable List result) { + try { + logTimeseriesDeleted(entityView.getTenantId(), user, entityId, result, null); + } catch (ThingsboardException e) { + log.error("Failed to log timeseries delete", e); + } + resultFuture.set(null); } - resultFuture.set(null); - } - @Override - public void onFailure(Throwable t) { - try { - logTimeseriesDeleted(entityView.getTenantId(), user, entityId, Collections.emptyList(), t); - } catch (ThingsboardException e) { - log.error("Failed to log timeseries delete", e); + @Override + public void onFailure(Throwable t) { + try { + logTimeseriesDeleted(entityView.getTenantId(), user, entityId, Optional.ofNullable(keys).orElse(Collections.emptyList()), t); + } catch (ThingsboardException e) { + log.error("Failed to log timeseries delete", e); + } + resultFuture.setException(t); } - resultFuture.setException(t); - } - }); - } + }) + .build()); return resultFuture; } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java index af63256f0f..9a293ada7e 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java @@ -149,7 +149,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple return; // if originated by rule - just ignore delivery method } } - if (ruleId == null) { + if (ruleId == null && !notificationTemplate.getNotificationType().isSystem()) { if (targets.stream().noneMatch(target -> target.getConfiguration().getType().getSupportedDeliveryMethods().contains(deliveryMethod))) { throw new IllegalArgumentException("Recipients for " + deliveryMethod.getName() + " delivery method not chosen"); } @@ -225,13 +225,13 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple NotificationTemplate notificationTemplate = notificationTemplateService.findTenantOrSystemNotificationTemplate(tenantId, type) .orElseThrow(() -> new IllegalArgumentException("No notification template found for type " + type)); NotificationRequest notificationRequest = NotificationRequest.builder() - .tenantId(TenantId.SYS_TENANT_ID) + .tenantId(tenantId) .targets(List.of(targetId.getId())) .templateId(notificationTemplate.getId()) .info(info) .originatorEntityId(TenantId.SYS_TENANT_ID) .build(); - processNotificationRequest(TenantId.SYS_TENANT_ID, notificationRequest, null); + processNotificationRequest(tenantId, notificationRequest, null); } private void processNotificationRequestAsync(NotificationProcessingContext ctx, List targets, FutureCallback callback) { diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java index dd31642aac..5bcc4f88d2 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java @@ -54,7 +54,6 @@ import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -106,22 +105,11 @@ public class DefaultNotificationRuleProcessor implements NotificationRuleProcess log.debug("Processing notification rule '{}' for trigger type {}", rule.getName(), rule.getTriggerType()); if (matchesClearRule(trigger, triggerConfig)) { - List notificationRequests = findAlreadySentNotificationRequests(rule, trigger); - if (notificationRequests.isEmpty()) { - return; - } - - List targets = notificationRequests.stream() - .filter(NotificationRequest::isSent) - .flatMap(notificationRequest -> notificationRequest.getTargets().stream()) - .distinct().collect(Collectors.toList()); - NotificationInfo notificationInfo = constructNotificationInfo(trigger, triggerConfig); - submitNotificationRequest(targets, rule, trigger.getOriginatorEntityId(), notificationInfo, 0); - - notificationRequests.forEach(notificationRequest -> { - if (notificationRequest.isScheduled()) { - notificationCenter.deleteNotificationRequest(rule.getTenantId(), notificationRequest.getId()); - } + List scheduledRequests = notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityIdAndStatus( + rule.getTenantId(), rule.getId(), trigger.getOriginatorEntityId(), NotificationRequestStatus.SCHEDULED + ); + scheduledRequests.forEach(notificationRequest -> { + notificationCenter.deleteNotificationRequest(rule.getTenantId(), notificationRequest.getId()); }); return; } @@ -139,10 +127,6 @@ public class DefaultNotificationRuleProcessor implements NotificationRuleProcess } } - private List findAlreadySentNotificationRequests(NotificationRule rule, NotificationRuleTrigger trigger) { - return notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityId(rule.getTenantId(), rule.getId(), trigger.getOriginatorEntityId()); - } - private void submitNotificationRequest(List targets, NotificationRule rule, EntityId originatorEntityId, NotificationInfo notificationInfo, int delayInSec) { NotificationRequestConfig config = new NotificationRequestConfig(); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java index b9047cac6b..a51d6689a7 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java @@ -46,10 +46,10 @@ public class AlarmTriggerProcessor implements NotificationRuleTriggerProcessor() { - @Override - public void onSuccess(@Nullable Void tmp) { - log.trace("[{}] Success save firmware status!", deviceId); - } + telemetryService.saveTimeseries(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(deviceId) + .entries(telemetry) + .callback(new FutureCallback() { + @Override + public void onSuccess(@Nullable Void tmp) { + log.trace("[{}] Success save firmware status!", deviceId); + } - @Override - public void onFailure(Throwable t) { - log.error("[{}] Failed to save firmware status!", deviceId, t); - } - }); + @Override + public void onFailure(Throwable t) { + log.error("[{}] Failed to save firmware status!", deviceId, t); + } + }) + .build()); } @@ -282,19 +289,24 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(otaPackageType, STATE), OtaPackageUpdateStatus.INITIATED.name())); - telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() { - @Override - public void onSuccess(@Nullable Void tmp) { - log.trace("[{}] Success save telemetry with target {} for device!", deviceId, otaPackage); - updateAttributes(device, otaPackage, ts, tenantId, deviceId, otaPackageType); - } + telemetryService.saveTimeseries(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(deviceId) + .entry(status) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void tmp) { + log.trace("[{}] Success save telemetry with target {} for device!", deviceId, otaPackage); + updateAttributes(device, otaPackage, ts, tenantId, deviceId, otaPackageType); + } - @Override - public void onFailure(Throwable t) { - log.error("[{}] Failed to save telemetry with target {} for device!", deviceId, otaPackage, t); - updateAttributes(device, otaPackage, ts, tenantId, deviceId, otaPackageType); - } - }); + @Override + public void onFailure(Throwable t) { + log.error("[{}] Failed to save telemetry with target {} for device!", deviceId, otaPackage, t); + updateAttributes(device, otaPackage, ts, tenantId, deviceId, otaPackageType); + } + }) + .build()); } private void updateAttributes(Device device, OtaPackageInfo otaPackage, long ts, TenantId tenantId, DeviceId deviceId, OtaPackageType otaPackageType) { @@ -336,17 +348,23 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { remove(device, otaPackageType, attrToRemove); - telemetryService.saveAndNotify(tenantId, deviceId, AttributeScope.SHARED_SCOPE, attributes, new FutureCallback<>() { - @Override - public void onSuccess(@Nullable Void tmp) { - log.trace("[{}] Success save attributes with target firmware!", deviceId); - } + telemetryService.saveAttributes(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(deviceId) + .scope(AttributeScope.SHARED_SCOPE) + .entries(attributes) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void tmp) { + log.trace("[{}] Success save attributes with target firmware!", deviceId); + } - @Override - public void onFailure(Throwable t) { - log.error("[{}] Failed to save attributes with target firmware!", deviceId, t); - } - }); + @Override + public void onFailure(Throwable t) { + log.error("[{}] Failed to save attributes with target firmware!", deviceId, t); + } + }) + .build()); } private void remove(Device device, OtaPackageType otaPackageType) { @@ -354,8 +372,12 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { } private void remove(Device device, OtaPackageType otaPackageType, List attributesKeys) { - telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), AttributeScope.SHARED_SCOPE, attributesKeys, - new FutureCallback<>() { + telemetryService.deleteAttributes(AttributesDeleteRequest.builder() + .tenantId(device.getTenantId()) + .entityId(device.getId()) + .scope(AttributeScope.SHARED_SCOPE) + .keys(attributesKeys) + .callback(new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) { log.trace("[{}] Success remove target {} attributes!", device.getId(), otaPackageType); @@ -366,6 +388,8 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { public void onFailure(Throwable t) { log.error("[{}] Failed to remove target {} attributes!", device.getId(), otaPackageType, t); } - }); + }) + .build()); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java index 301f8e8838..92ad0e446a 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java @@ -290,11 +290,16 @@ public class DefaultTbClusterService implements TbClusterService { boolean isQueueTransform = targetQueueName != null && !targetQueueName.equals(tbMsg.getQueueName()); if (isRuleChainTransform && isQueueTransform) { - tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId, targetQueueName); + tbMsg = tbMsg.transform() + .queueName(targetQueueName) + .ruleChainId(targetRuleChainId) + .build(); } else if (isRuleChainTransform) { - tbMsg = TbMsg.transformMsgRuleChainId(tbMsg, targetRuleChainId); + tbMsg = tbMsg.transform() + .ruleChainId(targetRuleChainId) + .build(); } else if (isQueueTransform) { - tbMsg = TbMsg.transformMsgQueueName(tbMsg, targetQueueName); + tbMsg = tbMsg.transform(targetQueueName); } } return tbMsg; diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java index e8cce43aa2..5ee741fd1f 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java @@ -183,7 +183,14 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService { entityNode.put(DataConstants.ADDITIONAL_INFO, msg.getAdditionalInfo()); try { - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), Optional.ofNullable(currentUser).map(User::getCustomerId).orElse(null), metaData, TbMsgDataType.JSON, JacksonUtil.toString(entityNode)); + TbMsg tbMsg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(msg.getDeviceId()) + .customerId(Optional.ofNullable(currentUser).map(User::getCustomerId).orElse(null)) + .copyMetaData(metaData) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(entityNode)) + .build(); clusterService.pushMsgToRuleEngine(msg.getTenantId(), msg.getDeviceId(), tbMsg, null); } catch (IllegalArgumentException e) { throw new RuntimeException(e); diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java index 133818592e..8b4941323f 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java @@ -63,7 +63,12 @@ public class TbRpcService { } private void pushRpcMsgToRuleEngine(TenantId tenantId, Rpc rpc) { - TbMsg msg = TbMsg.newMsg(TbMsgType.valueOf("RPC_" + rpc.getStatus().name()), rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc)); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.valueOf("RPC_" + rpc.getStatus().name())) + .originator(rpc.getDeviceId()) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.toString(rpc)) + .build(); tbClusterService.pushMsgToRuleEngine(tenantId, rpc.getDeviceId(), msg, null); } diff --git a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java index a022d6a5b1..c756e50de4 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java +++ b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java @@ -147,6 +147,10 @@ public class RuleNodeJsScriptEngine extends RuleNodeScriptEngine ListenableFuture wrongResultType(Object result) { diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index 6025e874b9..c58cb4fd5a 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -38,6 +38,8 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.AttributeScope; @@ -51,6 +53,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.KvEntry; @@ -857,7 +860,14 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService(deviceId, key, value)); - } else { - tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, AttributeScope.SERVER_SCOPE, key, value, new TelemetrySaveCallback<>(deviceId, key, value)); - } + save(deviceId, new LongDataEntry(key, value), getCurrentTimeMillis()); } private void save(DeviceId deviceId, String key, boolean value) { + save(deviceId, new BooleanDataEntry(key, value), getCurrentTimeMillis()); + } + + private void save(DeviceId deviceId, KvEntry kvEntry, long ts) { if (persistToTelemetry) { - tsSubService.saveAndNotifyInternal( - TenantId.SYS_TENANT_ID, deviceId, - Collections.singletonList(new BasicTsKvEntry(getCurrentTimeMillis(), new BooleanDataEntry(key, value))), - telemetryTtl, new TelemetrySaveCallback<>(deviceId, key, value)); + tsSubService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() + .tenantId(TenantId.SYS_TENANT_ID) + .entityId(deviceId) + .entry(new BasicTsKvEntry(ts, kvEntry)) + .ttl(telemetryTtl) + .callback(new TelemetrySaveCallback<>(deviceId, kvEntry)) + .build()); } else { - tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, AttributeScope.SERVER_SCOPE, key, value, new TelemetrySaveCallback<>(deviceId, key, value)); + tsSubService.saveAttributes(AttributesSaveRequest.builder() + .tenantId(TenantId.SYS_TENANT_ID) + .entityId(deviceId) + .scope(AttributeScope.SERVER_SCOPE) + .entry(new BaseAttributeKvEntry(ts, kvEntry)) + .callback(new TelemetrySaveCallback<>(deviceId, kvEntry)) + .build()); } } @@ -892,23 +908,21 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService implements FutureCallback { private final DeviceId deviceId; - private final String key; - private final Object value; + private final KvEntry kvEntry; - TelemetrySaveCallback(DeviceId deviceId, String key, Object value) { + TelemetrySaveCallback(DeviceId deviceId, KvEntry kvEntry) { this.deviceId = deviceId; - this.key = key; - this.value = value; + this.kvEntry = kvEntry; } @Override public void onSuccess(@Nullable T result) { - log.trace("[{}] Successfully updated attribute [{}] with value [{}]", deviceId, key, value); + log.trace("[{}] Successfully updated entry {}", deviceId, kvEntry); } @Override public void onFailure(Throwable t) { - log.warn("[{}] Failed to update attribute [{}] with value [{}]", deviceId, key, value, t); + log.warn("[{}] Failed to update entry {}", deviceId, kvEntry, t); } } } diff --git a/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java b/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java index e0c78e0afa..91766c0f5d 100644 --- a/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java +++ b/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java @@ -22,6 +22,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.id.QueueStatsId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; @@ -37,7 +38,6 @@ import org.thingsboard.server.queue.util.TbRuleEngineComponent; import org.thingsboard.server.service.queue.TbRuleEngineConsumerStats; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; -import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -53,9 +53,9 @@ import java.util.stream.Collectors; public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsService { public static final String RULE_ENGINE_EXCEPTION = "ruleEngineException"; - public static final FutureCallback CALLBACK = new FutureCallback() { + public static final FutureCallback CALLBACK = new FutureCallback() { @Override - public void onSuccess(@Nullable Integer result) { + public void onSuccess(@Nullable Void result) { } @@ -89,7 +89,13 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS if (!tsList.isEmpty()) { long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getQueueStatsTtlDays); ttl = TimeUnit.DAYS.toSeconds(ttl); - tsService.saveAndNotifyInternal(tenantId, queueStatsId, tsList, ttl, CALLBACK); + tsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(queueStatsId) + .entries(tsList) + .ttl(ttl) + .callback(CALLBACK) + .build()); } } } catch (Exception e) { @@ -103,7 +109,13 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS TsKvEntry tsKv = new BasicTsKvEntry(e.getTs(), new JsonDataEntry(RULE_ENGINE_EXCEPTION, e.toJsonString(maxErrorMessageLength))); long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getRuleEngineExceptionsTtlDays); ttl = TimeUnit.DAYS.toSeconds(ttl); - tsService.saveAndNotifyInternal(tenantId, getQueueStatsId(tenantId, queueName), Collections.singletonList(tsKv), ttl, CALLBACK); + tsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(getQueueStatsId(tenantId, queueName)) + .entry(tsKv) + .ttl(ttl) + .callback(CALLBACK) + .build()); } catch (Exception e2) { if (!"Asset is referencing to non-existent tenant!".equalsIgnoreCase(e2.getMessage())) { log.debug("[{}] Failed to store the statistics", tenantId, e2); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/AssetProfileExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/AssetProfileExportService.java index 0ff15a0874..5b973bbb46 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/AssetProfileExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/AssetProfileExportService.java @@ -34,7 +34,6 @@ public class AssetProfileExportService extends BaseEntityExportService ctx, I entityId) throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DeviceProfileExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DeviceProfileExportService.java index 60aaf34a3a..69389e7d57 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DeviceProfileExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DeviceProfileExportService.java @@ -34,7 +34,6 @@ public class DeviceProfileExportService extends BaseEntityExportService fqns = widgetTypeService.findWidgetFqnsByWidgetsBundleId(ctx.getTenantId(), widgetsBundle.getId()); exportData.setFqns(fqns); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java index 50d8b9c371..3edb7c9be0 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java @@ -32,6 +32,8 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.EntityType; @@ -206,20 +208,27 @@ public abstract class AbstractBulkImportService { TenantProfile tenantProfile = tenantProfileCache.get(tenantId); long tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays()); - tsSubscriptionService.saveAndNotify(tenantId, user.getCustomerId(), entityId, timeseries, tenantTtl, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, - ActionType.TIMESERIES_UPDATED, null, timeseries); - } - - @Override - public void onFailure(Throwable t) { - entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, - ActionType.TIMESERIES_UPDATED, BaseController.toException(t), timeseries); - throw new RuntimeException(t); - } - }); + tsSubscriptionService.saveTimeseries(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .customerId(user.getCustomerId()) + .entityId(entityId) + .entries(timeseries) + .ttl(tenantTtl) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void tmp) { + entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, + ActionType.TIMESERIES_UPDATED, null, timeseries); + } + + @Override + public void onFailure(Throwable t) { + entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, + ActionType.TIMESERIES_UPDATED, BaseController.toException(t), timeseries); + throw new RuntimeException(t); + } + }) + .build()); }); } @@ -229,23 +238,27 @@ public abstract class AbstractBulkImportService attributes = new ArrayList<>(JsonConverter.convertToAttributes(kvsEntry.getValue())); accessValidator.validateEntityAndCallback(user, Operation.WRITE_ATTRIBUTES, entity.getId(), (result, tenantId, entityId) -> { - tsSubscriptionService.saveAndNotify(tenantId, entityId, AttributeScope.valueOf(scope), attributes, new FutureCallback<>() { - - @Override - public void onSuccess(Void unused) { - entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, - null, ActionType.ATTRIBUTES_UPDATED, null, AttributeScope.valueOf(scope), attributes); - } - - @Override - public void onFailure(Throwable throwable) { - entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, - null, ActionType.ATTRIBUTES_UPDATED, BaseController.toException(throwable), - AttributeScope.valueOf(scope), attributes); - throw new RuntimeException(throwable); - } - - }); + tsSubscriptionService.saveAttributes(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(entityId) + .scope(AttributeScope.valueOf(scope)) + .entries(attributes) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(Void unused) { + entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, + null, ActionType.ATTRIBUTES_UPDATED, null, AttributeScope.valueOf(scope), attributes); + } + + @Override + public void onFailure(Throwable throwable) { + entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, + null, ActionType.ATTRIBUTES_UPDATED, BaseController.toException(throwable), + AttributeScope.valueOf(scope), attributes); + throw new RuntimeException(throwable); + } + }) + .build()); }); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java index f88844bd09..0d9c67823a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java @@ -24,6 +24,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; @@ -257,16 +258,22 @@ public abstract class BaseEntityImportService() { - @Override - public void onSuccess(@Nullable Void unused) { - } + tsSubService.saveAttributes(AttributesSaveRequest.builder() + .tenantId(user.getTenantId()) + .entityId(entity.getId()) + .scope(scope) + .entries(attributeKvEntries) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void unused) { + } - @Override - public void onFailure(Throwable thr) { - log.error("Failed to import attributes for {} {}", entity.getId().getEntityType(), entity.getId(), thr); - } - }); + @Override + public void onFailure(Throwable thr) { + log.error("Failed to import attributes for {} {}", entity.getId().getEntityType(), entity.getId(), thr); + } + }) + .build()); }); }); } diff --git a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java index 4016352f2d..42d468575a 100644 --- a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java +++ b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java @@ -27,6 +27,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.SmsService; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.FeaturesInfo; @@ -71,9 +72,9 @@ import static org.thingsboard.common.util.SystemUtil.getTotalMemory; @Slf4j public class DefaultSystemInfoService extends TbApplicationEventListener implements SystemInfoService { - public static final FutureCallback CALLBACK = new FutureCallback<>() { + public static final FutureCallback CALLBACK = new FutureCallback<>() { @Override - public void onSuccess(@Nullable Integer result) { + public void onSuccess(@Nullable Void result) { } @Override @@ -200,7 +201,13 @@ public class DefaultSystemInfoService extends TbApplicationEventListener telemetry) { ApiUsageState apiUsageState = apiUsageStateClient.getApiUsageState(TenantId.SYS_TENANT_ID); - telemetryService.saveAndNotifyInternal(TenantId.SYS_TENANT_ID, apiUsageState.getId(), telemetry, systemInfoTtlSeconds, CALLBACK); + telemetryService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() + .tenantId(TenantId.SYS_TENANT_ID) + .entityId(apiUsageState.getId()) + .entries(telemetry) + .ttl(systemInfoTtlSeconds) + .callback(CALLBACK) + .build()); } private List getSystemData(ServiceInfo serviceInfo) { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index 5513c41929..d4444d5d7f 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -19,29 +19,28 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; -import com.google.common.util.concurrent.SettableFuture; import jakarta.annotation.Nullable; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; +import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; +import org.thingsboard.rule.engine.api.TimeseriesDeleteRequest; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.ApiUsageRecordKey; -import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; -import org.thingsboard.server.common.data.kv.BooleanDataEntry; -import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; -import org.thingsboard.server.common.data.kv.DoubleDataEntry; -import org.thingsboard.server.common.data.kv.LongDataEntry; -import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvLatestRemovingResult; import org.thingsboard.server.common.msg.queue.TbCallback; @@ -54,8 +53,6 @@ import org.thingsboard.server.service.entitiy.entityview.TbEntityViewService; import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -64,13 +61,14 @@ import java.util.Objects; import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.function.Consumer; /** * Created by ashvayka on 27.03.18. */ @Service @Slf4j -public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionService implements TelemetrySubscriptionService { +public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionService implements TelemetrySubscriptionService, RuleEngineTelemetryService { private final AttributesService attrService; private final TimeseriesService tsService; @@ -115,76 +113,92 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } @Override - public ListenableFuture saveAndNotify(TenantId tenantId, EntityId entityId, TsKvEntry ts) { - SettableFuture future = SettableFuture.create(); - saveAndNotify(tenantId, entityId, Collections.singletonList(ts), new VoidFutureCallback(future)); - return future; + public void saveTimeseries(TimeseriesSaveRequest request) { + TenantId tenantId = request.getTenantId(); + EntityId entityId = request.getEntityId(); + checkInternalEntity(entityId); + boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null; + if (sysTenant || request.isOnlyLatest() || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { + KvUtils.validate(request.getEntries(), valueNoXssValidation); + ListenableFuture future = saveTimeseriesInternal(request); + if (!request.isOnlyLatest()) { + FutureCallback callback = getApiUsageCallback(tenantId, request.getCustomerId(), sysTenant, request.getCallback()); + Futures.addCallback(future, callback, tsCallBackExecutor); + } + } else { + request.getCallback().onFailure(new RuntimeException("DB storage writes are disabled due to API limits!")); + } } @Override - public void saveAndNotify(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback) { - saveAndNotify(tenantId, null, entityId, ts, 0L, callback); - } + public ListenableFuture saveTimeseriesInternal(TimeseriesSaveRequest request) { + TenantId tenantId = request.getTenantId(); + EntityId entityId = request.getEntityId(); + ListenableFuture saveFuture; + if (request.isOnlyLatest()) { + saveFuture = Futures.transform(tsService.saveLatest(tenantId, entityId, request.getEntries()), result -> 0, MoreExecutors.directExecutor()); + } else if (request.isSaveLatest()) { + saveFuture = tsService.save(tenantId, entityId, request.getEntries(), request.getTtl()); + } else { + saveFuture = tsService.saveWithoutLatest(tenantId, entityId, request.getEntries(), request.getTtl()); + } - @Override - public void saveAndNotify(TenantId tenantId, CustomerId customerId, EntityId entityId, List ts, long ttl, FutureCallback callback) { - doSaveAndNotify(tenantId, customerId, entityId, ts, ttl, callback, true); + addMainCallback(saveFuture, request.getCallback()); + addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, request.getEntries())); + if (request.isSaveLatest() && !request.isOnlyLatest()) { + addEntityViewCallback(tenantId, entityId, request.getEntries()); + } + return saveFuture; } @Override - public void saveWithoutLatestAndNotify(TenantId tenantId, CustomerId customerId, EntityId entityId, List ts, long ttl, FutureCallback callback) { - doSaveAndNotify(tenantId, customerId, entityId, ts, ttl, callback, false); + public void saveAttributes(AttributesSaveRequest request) { + checkInternalEntity(request.getEntityId()); + saveAttributesInternal(request); } - private void doSaveAndNotify(TenantId tenantId, CustomerId customerId, EntityId entityId, List ts, long ttl, FutureCallback callback, boolean saveLatest) { - checkInternalEntity(entityId); - boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null; - if (sysTenant || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { - KvUtils.validate(ts, valueNoXssValidation); - if (saveLatest) { - saveAndNotifyInternal(tenantId, entityId, ts, ttl, getCallback(tenantId, customerId, sysTenant, callback)); - } else { - saveWithoutLatestAndNotifyInternal(tenantId, entityId, ts, ttl, getCallback(tenantId, customerId, sysTenant, callback)); - } - } else { - callback.onFailure(new RuntimeException("DB storage writes are disabled due to API limits!")); - } + @Override + public void saveAttributesInternal(AttributesSaveRequest request) { + log.trace("Executing saveInternal [{}]", request); + ListenableFuture> saveFuture = attrService.save(request.getTenantId(), request.getEntityId(), request.getScope(), request.getEntries()); + addMainCallback(saveFuture, request.getCallback()); + addWsCallback(saveFuture, success -> onAttributesUpdate(request.getTenantId(), request.getEntityId(), request.getScope().name(), request.getEntries(), request.isNotifyDevice())); } - private FutureCallback getCallback(TenantId tenantId, CustomerId customerId, boolean sysTenant, FutureCallback callback) { - return new FutureCallback<>() { - @Override - public void onSuccess(Integer result) { - if (!sysTenant && result != null && result > 0) { - apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.STORAGE_DP_COUNT, result); - } - callback.onSuccess(null); - } - - @Override - public void onFailure(Throwable t) { - callback.onFailure(t); - } - }; + @Override + public void deleteAttributes(AttributesDeleteRequest request) { + checkInternalEntity(request.getEntityId()); + deleteAttributesInternal(request); } @Override - public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback) { - saveAndNotifyInternal(tenantId, entityId, ts, 0L, callback); + public void deleteAttributesInternal(AttributesDeleteRequest request) { + ListenableFuture> deleteFuture = attrService.removeAll(request.getTenantId(), request.getEntityId(), request.getScope(), request.getKeys()); + addMainCallback(deleteFuture, request.getCallback()); + addWsCallback(deleteFuture, success -> onAttributesDelete(request.getTenantId(), request.getEntityId(), request.getScope().name(), request.getKeys(), request.isNotifyDevice())); } @Override - public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, long ttl, FutureCallback callback) { - ListenableFuture saveFuture = tsService.save(tenantId, entityId, ts, ttl); - addMainCallback(saveFuture, callback); - addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts)); - addEntityViewCallback(tenantId, entityId, ts); + public void deleteTimeseries(TimeseriesDeleteRequest request) { + checkInternalEntity(request.getEntityId()); + deleteTimeseriesInternal(request); } - private void saveWithoutLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, long ttl, FutureCallback callback) { - ListenableFuture saveFuture = tsService.saveWithoutLatest(tenantId, entityId, ts, ttl); - addMainCallback(saveFuture, callback); - addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts)); + @Override + public void deleteTimeseriesInternal(TimeseriesDeleteRequest request) { + if (CollectionUtils.isNotEmpty(request.getKeys())) { + ListenableFuture> deleteFuture; + if (request.getDeleteHistoryQueries() == null) { + deleteFuture = tsService.removeLatest(request.getTenantId(), request.getEntityId(), request.getKeys()); + } else { + deleteFuture = tsService.remove(request.getTenantId(), request.getEntityId(), request.getDeleteHistoryQueries()); + addWsCallback(deleteFuture, result -> onTimeSeriesDelete(request.getTenantId(), request.getEntityId(), request.getKeys(), result)); + } + addMainCallback(deleteFuture, __ -> request.getCallback().onSuccess(request.getKeys()), request.getCallback()::onFailure); + } else { + ListenableFuture> deleteFuture = tsService.removeAllLatest(request.getTenantId(), request.getEntityId()); + addMainCallback(deleteFuture, request.getCallback()::onSuccess, request.getCallback()::onFailure); + } } private void addEntityViewCallback(TenantId tenantId, EntityId entityId, List ts) { @@ -214,15 +228,21 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } } if (!entityViewLatest.isEmpty()) { - saveLatestAndNotify(tenantId, entityView.getId(), entityViewLatest, new FutureCallback<>() { - @Override - public void onSuccess(@Nullable Void tmp) { - } - - @Override - public void onFailure(Throwable t) { - } - }); + saveTimeseries(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(entityView.getId()) + .entries(entityViewLatest) + .onlyLatest(true) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void tmp) {} + + @Override + public void onFailure(Throwable t) { + log.error("[{}][{}] Failed to save entity view latest timeseries: {}", tenantId, entityView.getId(), entityViewLatest, t); + } + }) + .build()); } } } @@ -236,233 +256,6 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } } - @Override - public void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List attributes, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, attributes, true, callback); - } - - @Override - public void saveAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, attributes, true, callback); - } - - @Override - public void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List attributes, boolean notifyDevice, FutureCallback callback) { - checkInternalEntity(entityId); - saveAndNotifyInternal(tenantId, entityId, scope, attributes, notifyDevice, callback); - } - - @Override - public void saveAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes, boolean notifyDevice, FutureCallback callback) { - checkInternalEntity(entityId); - saveAndNotifyInternal(tenantId, entityId, scope, attributes, notifyDevice, callback); - } - - @Override - public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List attributes, boolean notifyDevice, FutureCallback callback) { - ListenableFuture> saveFuture = attrService.save(tenantId, entityId, scope, attributes); - addVoidCallback(saveFuture, callback); - addWsCallback(saveFuture, success -> onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice)); - } - - @Override - public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes, boolean notifyDevice, FutureCallback callback) { - ListenableFuture> saveFuture = attrService.save(tenantId, entityId, scope, attributes); - addVoidCallback(saveFuture, callback); - addWsCallback(saveFuture, success -> onAttributesUpdate(tenantId, entityId, scope.name(), attributes, notifyDevice)); - } - - @Override - public void saveLatestAndNotify(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback) { - checkInternalEntity(entityId); - saveLatestAndNotifyInternal(tenantId, entityId, ts, callback); - } - - @Override - public void saveLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback) { - ListenableFuture> saveFuture = tsService.saveLatest(tenantId, entityId, ts); - addVoidCallback(saveFuture, callback); - addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts)); - } - - @Override - public void deleteAndNotify(TenantId tenantId, EntityId entityId, String scope, List keys, FutureCallback callback) { - checkInternalEntity(entityId); - deleteAndNotifyInternal(tenantId, entityId, scope, keys, false, callback); - } - - @Override - public void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, FutureCallback callback) { - checkInternalEntity(entityId); - deleteAndNotifyInternal(tenantId, entityId, scope, keys, false, callback); - } - - @Override - public void deleteAndNotify(TenantId tenantId, EntityId entityId, String scope, List keys, boolean notifyDevice, FutureCallback callback) { - checkInternalEntity(entityId); - deleteAndNotifyInternal(tenantId, entityId, scope, keys, notifyDevice, callback); - } - - @Override - public void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, boolean notifyDevice, FutureCallback callback) { - checkInternalEntity(entityId); - deleteAndNotifyInternal(tenantId, entityId, scope, keys, notifyDevice, callback); - } - - @Override - public void deleteAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List keys, boolean notifyDevice, FutureCallback callback) { - ListenableFuture> deleteFuture = attrService.removeAll(tenantId, entityId, scope, keys); - addVoidCallback(deleteFuture, callback); - addWsCallback(deleteFuture, success -> onAttributesDelete(tenantId, entityId, scope, keys, notifyDevice)); - } - - @Override - public void deleteAndNotifyInternal(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, boolean notifyDevice, FutureCallback callback) { - ListenableFuture> deleteFuture = attrService.removeAll(tenantId, entityId, scope, keys); - addVoidCallback(deleteFuture, callback); - addWsCallback(deleteFuture, success -> onAttributesDelete(tenantId, entityId, scope.name(), keys, notifyDevice)); - } - - @Override - public void deleteLatest(TenantId tenantId, EntityId entityId, List keys, FutureCallback callback) { - checkInternalEntity(entityId); - deleteLatestInternal(tenantId, entityId, keys, callback); - } - - @Override - public void deleteLatestInternal(TenantId tenantId, EntityId entityId, List keys, FutureCallback callback) { - ListenableFuture> deleteFuture = tsService.removeLatest(tenantId, entityId, keys); - addVoidCallback(deleteFuture, callback); - } - - @Override - public void deleteAllLatest(TenantId tenantId, EntityId entityId, FutureCallback> callback) { - ListenableFuture> deleteFuture = tsService.removeAllLatest(tenantId, entityId); - Futures.addCallback(deleteFuture, new FutureCallback>() { - @Override - public void onSuccess(@Nullable Collection result) { - callback.onSuccess(result); - } - - @Override - public void onFailure(Throwable t) { - callback.onFailure(t); - } - }, tsCallBackExecutor); - } - - @Override - public void deleteTimeseriesAndNotify(TenantId tenantId, EntityId entityId, List keys, List deleteTsKvQueries, FutureCallback callback) { - ListenableFuture> deleteFuture = tsService.remove(tenantId, entityId, deleteTsKvQueries); - addVoidCallback(deleteFuture, callback); - addWsCallback(deleteFuture, list -> onTimeSeriesDelete(tenantId, entityId, keys, list)); - } - - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, long value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new LongDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - - - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, long value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new LongDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, String value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, String value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, double value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new DoubleDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, double value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new DoubleDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, boolean value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new BooleanDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, boolean value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new BooleanDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, long value) { - SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); - return future; - } - - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, long value) { - SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); - return future; - } - - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, String value) { - SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); - return future; - } - - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, String value) { - SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); - return future; - } - - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, double value) { - SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); - return future; - } - - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, double value) { - SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); - return future; - } - - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, boolean value) { - SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); - return future; - } - - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, boolean value) { - SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); - return future; - } - private void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List attributes, boolean notifyDevice) { forwardToSubscriptionManagerService(tenantId, entityId, subscriptionManagerService -> { subscriptionManagerService.onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice, TbCallback.EMPTY); @@ -509,33 +302,13 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer }); } - private void addVoidCallback(ListenableFuture saveFuture, final FutureCallback callback) { + private void addMainCallback(ListenableFuture saveFuture, final FutureCallback callback) { if (callback == null) return; - Futures.addCallback(saveFuture, new FutureCallback() { - @Override - public void onSuccess(@Nullable S result) { - callback.onSuccess(null); - } - - @Override - public void onFailure(Throwable t) { - callback.onFailure(t); - } - }, tsCallBackExecutor); + addMainCallback(saveFuture, result -> callback.onSuccess(null), callback::onFailure); } - private void addMainCallback(ListenableFuture saveFuture, final FutureCallback callback) { - Futures.addCallback(saveFuture, new FutureCallback() { - @Override - public void onSuccess(@Nullable S result) { - callback.onSuccess(result); - } - - @Override - public void onFailure(Throwable t) { - callback.onFailure(t); - } - }, tsCallBackExecutor); + private void addMainCallback(ListenableFuture saveFuture, Consumer onSuccess, Consumer onFailure) { + DonAsynchron.withCallback(saveFuture, onSuccess, onFailure, tsCallBackExecutor); } private void checkInternalEntity(EntityId entityId) { @@ -544,22 +317,21 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } } - private static class VoidFutureCallback implements FutureCallback { - private final SettableFuture future; - - public VoidFutureCallback(SettableFuture future) { - this.future = future; - } - - @Override - public void onSuccess(Void result) { - future.set(null); - } + private FutureCallback getApiUsageCallback(TenantId tenantId, CustomerId customerId, boolean sysTenant, FutureCallback callback) { + return new FutureCallback<>() { + @Override + public void onSuccess(Integer result) { + if (!sysTenant && result != null && result > 0) { + apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.STORAGE_DP_COUNT, result); + } + callback.onSuccess(null); + } - @Override - public void onFailure(Throwable t) { - future.setException(t); - } + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }; } } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java index a6dc6d1aae..8e45b84a75 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java @@ -15,37 +15,24 @@ */ package org.thingsboard.server.service.telemetry; -import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; -import org.thingsboard.server.common.data.AttributeScope; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.TsKvEntry; - -import java.util.List; +import org.thingsboard.rule.engine.api.TimeseriesDeleteRequest; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; /** * Created by ashvayka on 27.03.18. */ public interface InternalTelemetryService extends RuleEngineTelemetryService { - void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback); - - void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, long ttl, FutureCallback callback); - - @Deprecated(since = "3.7.0") - void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List attributes, boolean notifyDevice, FutureCallback callback); - - void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes, boolean notifyDevice, FutureCallback callback); - - void saveLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback); + ListenableFuture saveTimeseriesInternal(TimeseriesSaveRequest request); - @Deprecated(since = "3.7.0") - void deleteAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List keys, boolean notifyDevice, FutureCallback callback); + void saveAttributesInternal(AttributesSaveRequest request); - void deleteAndNotifyInternal(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, boolean notifyDevice, FutureCallback callback); + void deleteTimeseriesInternal(TimeseriesDeleteRequest request); - void deleteLatestInternal(TenantId tenantId, EntityId entityId, List keys, FutureCallback callback); + void deleteAttributesInternal(AttributesDeleteRequest request); } diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index 51d37e291a..09fc1be1c8 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -362,7 +362,14 @@ public class DefaultTransportApiService implements TransportApiService { DeviceId deviceId = device.getId(); JsonNode entityNode = JacksonUtil.valueToTree(device); - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, deviceId, customerId, metaData, TbMsgDataType.JSON, JacksonUtil.toString(entityNode)); + TbMsg tbMsg = TbMsg.newMsg() + .type(TbMsgType.ENTITY_CREATED) + .originator(deviceId) + .customerId(customerId) + .copyMetaData(metaData) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(entityNode)) + .build(); tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, null); } else { JsonNode deviceAdditionalInfo = device.getAdditionalInfo(); diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index ef6d2204d8..32fd62670d 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1302,6 +1302,30 @@ coap: # - A value between 0 and <= 4: SingleNodeConnectionIdGenerator is used # - A value that are > 4: MultiNodeConnectionIdGenerator is used connection_id_length: "${COAP_DTLS_CONNECTION_ID_LENGTH:}" + # Specify the MTU (Maximum Transmission Unit). + # Should be used if LAN MTU is not used, e.g. if IP tunnels are used or if the client uses a smaller value than the LAN MTU. + # Default = 1024 + # Minimum value = 64 + # If set to 0 - LAN MTU is used. + max_transmission_unit: "${COAP_DTLS_MAX_TRANSMISSION_UNIT:1024}" + # DTLS maximum fragment length (RFC 6066, Section 4). + # Default = 1024 + # Possible values: 512, 1024, 2048, 4096. + # If set to 0, the default maximum fragment size of 2^14 bytes (16,384 bytes) is used. + # Without this extension, TLS specifies a fixed maximum plaintext fragment length of 2^14 bytes. + # It may be desirable for constrained clients to negotiate a smaller maximum fragment length due to memory limitations or bandwidth limitations. + # In order to negotiate smaller maximum fragment lengths, + # clients MAY include an extension of type "max_fragment_length" in the (extended) client hello. + # The "extension_data" field of this extension SHALL contain: + # enum { + # 2^9(1) == 512, + # 2^10(2) == 1024, + # 2^11(3) == 2048, + # 2^12(4) == 4096, + # (255) + # } MaxFragmentLength; + # TLS already requires clients and servers to support fragmentation of handshake messages. + max_fragment_length: "${COAP_DTLS_MAX_FRAGMENT_LENGTH:1024}" # Server DTLS credentials credentials: # Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore) @@ -1422,11 +1446,11 @@ edges: # Max number of high priority edge events per edge session. No persistence - stored in memory max_high_priority_queue_size_per_session: "${EDGES_MAX_HIGH_PRIORITY_QUEUE_SIZE_PER_SESSION:10000}" # Number of threads that are used to check DB for edge events - scheduler_pool_size: "${EDGES_SCHEDULER_POOL_SIZE:1}" + scheduler_pool_size: "${EDGES_SCHEDULER_POOL_SIZE:4}" # Number of threads that are used to send downlink messages to edge over gRPC - send_scheduler_pool_size: "${EDGES_SEND_SCHEDULER_POOL_SIZE:1}" + send_scheduler_pool_size: "${EDGES_SEND_SCHEDULER_POOL_SIZE:4}" # Number of threads that are used to convert edge events from DB into downlink messages and send them for delivery - grpc_callback_thread_pool_size: "${EDGES_GRPC_CALLBACK_POOL_SIZE:1}" + grpc_callback_thread_pool_size: "${EDGES_GRPC_CALLBACK_POOL_SIZE:4}" state: # Persist state of edge (active, last connect, last disconnect) into timeseries or attributes tables. 'false' means to store edge state into attributes table persistToTelemetry: "${EDGES_PERSIST_STATE_TO_TELEMETRY:false}" diff --git a/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java b/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java index 21a4a45c6f..29d96347ea 100644 --- a/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java +++ b/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java @@ -928,15 +928,32 @@ class DefaultTbContextTest { } private TbMsg getTbMsgWithCallback(TbMsgCallback callback) { - return TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TENANT_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING, callback); + return TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TENANT_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .callback(callback) + .build(); } private TbMsg getTbMsgWithQueueName() { - return TbMsg.newMsg(DataConstants.MAIN_QUEUE_NAME, TbMsgType.POST_TELEMETRY_REQUEST, TENANT_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + return TbMsg.newMsg() + .queueName(DataConstants.MAIN_QUEUE_NAME) + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TENANT_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); } private TbMsg getTbMsg() { - return TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TENANT_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + return TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TENANT_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); } private static long getUntilTime() { diff --git a/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java index 73182587fb..dac48a1c06 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java @@ -83,6 +83,7 @@ public class AuthControllerTest extends AbstractControllerTest { assertThat(user.getEmail()).isEqualTo(CUSTOMER_USER_EMAIL); user = getUser(customerUserId); assertThat(user.getAdditionalInfo().get("userCredentialsEnabled").asBoolean()).isTrue(); + assertThat(user.getAdditionalInfo().get("userActivated").asBoolean()).isTrue(); assertThat(user.getAdditionalInfo().get("lastLoginTs").asLong()).isCloseTo(System.currentTimeMillis(), within(10000L)); } @@ -243,6 +244,7 @@ public class AuthControllerTest extends AbstractControllerTest { user.setAuthority(Authority.TENANT_ADMIN); user.setEmail("tenant-admin-2@thingsboard.org"); user = doPost("/api/user", user, User.class); + assertThat(getUser(user.getId()).getAdditionalInfo().get("userActivated").asBoolean()).isFalse(); UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, user.getUuidId()); assertThat(userCredentials.getActivateTokenExpTime()).isCloseTo(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(ttl), Offset.offset(120000L)); @@ -289,6 +291,7 @@ public class AuthControllerTest extends AbstractControllerTest { doPost("/api/noauth/activate", JacksonUtil.newObjectNode() .put("activateToken", newActivationToken) .put("password", "wefewe")).andExpect(status().isOk()); + assertThat(getUser(user.getId()).getAdditionalInfo().get("userActivated").asBoolean()).isTrue(); } @Test diff --git a/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java index f6cc64fda8..d54810a394 100644 --- a/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java @@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpMethod; import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.ImageDescriptor; import org.thingsboard.server.common.data.ResourceExportData; import org.thingsboard.server.common.data.ResourceSubType; @@ -321,6 +322,24 @@ public class ImageControllerTest extends AbstractControllerTest { assertThat(systemParams.getMaxResourceSize()).isEqualTo(0); } + @Test + public void testInlineImages() throws Exception { + TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", "my_png_image.png", "image/png", PNG_IMAGE); + DeviceProfile deviceProfile = createDeviceProfile("Test"); + deviceProfile.setImage("tb-image;" + imageInfo.getLink()); + deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); + + DeviceProfile deviceProfileInlined = doGet("/api/deviceProfile/" + deviceProfile.getUuidId() + "?inlineImages=true", DeviceProfile.class); + assertThat(deviceProfileInlined.getImage()).isEqualTo("tb-image:" + + Base64.getEncoder().encodeToString(imageInfo.getResourceKey().getBytes()) + ":" + + Base64.getEncoder().encodeToString(imageInfo.getName().getBytes()) + ":" + + Base64.getEncoder().encodeToString(imageInfo.getResourceSubType().name().getBytes()) + + ";data:image/png;base64," + Base64.getEncoder().encodeToString(PNG_IMAGE)); + + deviceProfile = doGet("/api/deviceProfile/" + deviceProfile.getUuidId(), DeviceProfile.class); + assertThat(deviceProfile.getImage()).isEqualTo("tb-image;" + imageInfo.getLink()); + } + private TbResourceInfo updateImagePublicStatus(String filename, boolean isPublic) throws Exception { return doPut("/api/images/tenant/" + filename + "/public/" + isPublic, "", TbResourceInfo.class); } diff --git a/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java index 9c9322dc38..91dbb9714d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java @@ -62,7 +62,12 @@ public class RuleEngineControllerTest extends AbstractControllerTest { @Test public void testHandleRuleEngineRequestWithMsgOriginatorUser() throws Exception { loginSysAdmin(); - TbMsg responseMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, currentUserId, TbMsgMetaData.EMPTY, RESPONSE_BODY); + TbMsg responseMsg = TbMsg.newMsg() + .type(TbMsgType.REST_API_REQUEST) + .originator(currentUserId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(RESPONSE_BODY) + .build(); mockRestApiCallToRuleEngine(responseMsg); JsonNode apiResponse = doPostAsyncWithTypedResponse("/api/rule-engine/", REQUEST_BODY, new TypeReference<>() { @@ -86,7 +91,12 @@ public class RuleEngineControllerTest extends AbstractControllerTest { loginTenantAdmin(); Device device = createDevice("Test", "123"); DeviceId deviceId = device.getId(); - TbMsg responseMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, RESPONSE_BODY); + TbMsg responseMsg = TbMsg.newMsg() + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(RESPONSE_BODY) + .build(); mockRestApiCallToRuleEngine(responseMsg); JsonNode apiResponse = doPostAsyncWithTypedResponse("/api/rule-engine/DEVICE/" + deviceId.getId(), REQUEST_BODY, new TypeReference<>() { @@ -110,7 +120,12 @@ public class RuleEngineControllerTest extends AbstractControllerTest { loginTenantAdmin(); Device device = createDevice("Test", "123"); DeviceId deviceId = device.getId(); - TbMsg responseMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, RESPONSE_BODY); + TbMsg responseMsg = TbMsg.newMsg() + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(RESPONSE_BODY) + .build(); mockRestApiCallToRuleEngine(responseMsg); JsonNode apiResponse = doPostAsyncWithTypedResponse("/api/rule-engine/DEVICE/" + deviceId.getId() + "/15000", REQUEST_BODY, new TypeReference<>() { @@ -156,7 +171,13 @@ public class RuleEngineControllerTest extends AbstractControllerTest { loginTenantAdmin(); Device device = createDevice("Test", "123"); DeviceId deviceId = device.getId(); - TbMsg responseMsg = TbMsg.newMsg(DataConstants.HP_QUEUE_NAME, TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, RESPONSE_BODY); + TbMsg responseMsg = TbMsg.newMsg() + .queueName(DataConstants.HP_QUEUE_NAME) + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(RESPONSE_BODY) + .build(); mockRestApiCallToRuleEngine(responseMsg); JsonNode apiResponse = doPostAsyncWithTypedResponse("/api/rule-engine/DEVICE/" + deviceId.getId() + "/HighPriority/1000", REQUEST_BODY, new TypeReference<>() { @@ -195,7 +216,13 @@ public class RuleEngineControllerTest extends AbstractControllerTest { assignDeviceToCustomer(deviceId, customerId); loginCustomerUser(); - TbMsg responseMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, customerId, TbMsgMetaData.EMPTY, RESPONSE_BODY); + TbMsg responseMsg = TbMsg.newMsg() + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .customerId(customerId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(RESPONSE_BODY) + .build(); mockRestApiCallToRuleEngine(responseMsg); JsonNode apiResponse = doPostAsyncWithTypedResponse("/api/rule-engine/DEVICE/" + deviceId.getId(), REQUEST_BODY, new TypeReference<>() { diff --git a/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java index c8cc72c5b5..e78cd67ba3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java @@ -743,7 +743,12 @@ public class TenantControllerTest extends AbstractControllerTest { } private TbMsg publishTbMsg(TenantId tenantId, TopicPartitionInfo tpi) { - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, tenantId, TbMsgMetaData.EMPTY, "{\"test\":1}"); + TbMsg tbMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(tenantId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data("{\"test\":1}") + .build(); TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder() .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) diff --git a/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java b/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java index e3695d7efb..b64c87ccbd 100644 --- a/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java @@ -29,6 +29,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.TestPropertySource; import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmSeverity; @@ -804,19 +806,24 @@ public class WebsocketApiTest extends AbstractControllerTest { private void sendTelemetry(Device device, List tsData) throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); - tsService.saveAndNotify(device.getTenantId(), null, device.getId(), tsData, 0, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void result) { - log.debug("sendTelemetry callback onSuccess"); - latch.countDown(); - } - - @Override - public void onFailure(Throwable t) { - log.error("Failed to send telemetry", t); - latch.countDown(); - } - }); + tsService.saveTimeseries(TimeseriesSaveRequest.builder() + .tenantId(device.getTenantId()) + .entityId(device.getId()) + .entries(tsData) + .callback(new FutureCallback() { + @Override + public void onSuccess(@Nullable Void result) { + log.debug("sendTelemetry callback onSuccess"); + latch.countDown(); + } + + @Override + public void onFailure(Throwable t) { + log.error("Failed to send telemetry", t); + latch.countDown(); + } + }) + .build()); assertThat(latch.await(TIMEOUT, TimeUnit.SECONDS)).as("await sendTelemetry callback"); } @@ -826,19 +833,26 @@ public class WebsocketApiTest extends AbstractControllerTest { private void sendAttributes(TenantId tenantId, EntityId entityId, TbAttributeSubscriptionScope scope, List attrData) throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); - tsService.saveAndNotify(tenantId, entityId, scope.getAttributeScope(), attrData, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void result) { - log.debug("sendAttributes callback onSuccess"); - latch.countDown(); - } - - @Override - public void onFailure(Throwable t) { - log.error("Failed to sendAttributes", t); - latch.countDown(); - } - }); + tsService.saveAttributes(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(entityId) + .scope(scope.getAttributeScope()) + .entries(attrData) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void result) { + log.debug("sendAttributes callback onSuccess"); + latch.countDown(); + } + + @Override + public void onFailure(Throwable t) { + log.error("Failed to sendAttributes", t); + latch.countDown(); + } + }) + .build()); assertThat(latch.await(TIMEOUT, TimeUnit.SECONDS)).as("await sendAttributes callback").isTrue(); } + } diff --git a/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java index fba5f2187f..1ec1ed4fb6 100644 --- a/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java @@ -184,7 +184,13 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); Mockito.when(tbMsgCallback.isMsgValid()).thenReturn(true); - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, device.getId(), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, tbMsgCallback); + TbMsg tbMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(device.getId()) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .callback(tbMsgCallback) + .build(); QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null); // Pushing Message to the system actorSystem.tell(qMsg); @@ -309,7 +315,13 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); Mockito.when(tbMsgCallback.isMsgValid()).thenReturn(true); - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, device.getId(), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, tbMsgCallback); + TbMsg tbMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(device.getId()) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .callback(tbMsgCallback) + .build(); QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null); // Pushing Message to the system actorSystem.tell(qMsg); diff --git a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java index d8bd02ec7f..1c67689814 100644 --- a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java @@ -142,7 +142,13 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac log.warn("attr updated"); TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); Mockito.when(tbMsgCallback.isMsgValid()).thenReturn(true); - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, device.getId(), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, tbMsgCallback); + TbMsg tbMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(device.getId()) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .callback(tbMsgCallback) + .build(); QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(tenantId, tbMsg, null, null); // Pushing Message to the system log.warn("before tell tbMsgCallback"); diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index a5fb3b05bf..3cfb2dc42b 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -274,7 +274,6 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { Device device = createDevice("Device with alarm", "233"); NotificationTarget target = createNotificationTarget(tenantAdminUserId); defaultNotifications.create(tenantId, DefaultNotifications.newAlarm, target.getId()); - defaultNotifications.create(tenantId, DefaultNotifications.entityAction, target.getId()); notificationRulesCache.evict(tenantId); Alarm alarm = new Alarm(); @@ -287,7 +286,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .pollDelay(2, TimeUnit.SECONDS) .untilAsserted(() -> { List notifications = getMyNotifications(false, 10); - assertThat(notifications).hasSize(1).first().matches(notification -> { + assertThat(notifications).singleElement().matches(notification -> { return notification.getType() == NotificationType.ALARM && notification.getSubject().equals("New alarm 'testAlarm'"); }); @@ -353,12 +352,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .findFirst().orElse(null); assertThat(scheduledNotificationRequest).extracting(NotificationRequest::getInfo).isEqualTo(notification.getInfo()); - getWsClient().registerWaitForUpdate(); alarmSubscriptionService.clearAlarm(tenantId, alarm.getId(), System.currentTimeMillis(), null); - getWsClient().waitForUpdate(true); - notification = getWsClient().getLastDataUpdate().getUpdate(); - assertThat(notification.getSubject()).isEqualTo("critical alarm '" + alarmType + "' is CLEARED_UNACK"); - await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(findNotificationRequests(EntityType.ALARM).getData()).filteredOn(NotificationRequest::isScheduled).isEmpty(); }); diff --git a/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java b/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java index 25fe589a08..4dae5a6427 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java @@ -291,7 +291,13 @@ public class DefaultTbClusterServiceTest { TbQueueCallback callback = mock(TbQueueCallback.class); TenantId tenantId = TenantId.fromUUID(UUID.fromString("3c8bd350-1239-4a3b-b9c3-4dd76f8e20f1")); - TbMsg requestMsg = TbMsg.newMsg(DataConstants.HP_QUEUE_NAME, TbMsgType.REST_API_REQUEST, tenantId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg requestMsg = TbMsg.newMsg() + .queueName(DataConstants.HP_QUEUE_NAME) + .type(TbMsgType.REST_API_REQUEST) + .originator(tenantId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); when(producerProvider.getRuleEngineMsgProducer()).thenReturn(tbREQueueProducer); @@ -305,7 +311,12 @@ public class DefaultTbClusterServiceTest { public void testPushMsgToRuleEngineWithTenantIdIsNullUuidAndEntityIsDevice() { TenantId tenantId = TenantId.SYS_TENANT_ID; DeviceId deviceId = new DeviceId(UUID.fromString("aa6d112d-2914-4a22-a9e3-bee33edbdb14")); - TbMsg requestMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg requestMsg = TbMsg.newMsg() + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); TbQueueCallback callback = mock(TbQueueCallback.class); clusterService.pushMsgToRuleEngine(tenantId, deviceId, requestMsg, false, callback); @@ -321,7 +332,13 @@ public class DefaultTbClusterServiceTest { TenantId tenantId = TenantId.fromUUID(UUID.fromString("3c8bd350-1239-4a3b-b9c3-4dd76f8e20f1")); DeviceId deviceId = new DeviceId(UUID.fromString("adbb9d41-3367-40fd-9e74-7dd7cc5d30cf")); DeviceProfile deviceProfile = new DeviceProfile(new DeviceProfileId(UUID.fromString("552f5d6d-0b2b-43e1-a7d2-a51cb2a96927"))); - TbMsg requestMsg = TbMsg.newMsg(DataConstants.HP_QUEUE_NAME, TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg requestMsg = TbMsg.newMsg() + .queueName(DataConstants.HP_QUEUE_NAME) + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); when(deviceProfileCache.get(any(TenantId.class), any(DeviceId.class))).thenReturn(deviceProfile); when(producerProvider.getRuleEngineMsgProducer()).thenReturn(tbREQueueProducer); @@ -341,7 +358,12 @@ public class DefaultTbClusterServiceTest { DeviceId deviceId = new DeviceId(UUID.fromString("016c2abb-f46f-49f9-a83d-4d28b803cfe6")); DeviceProfile deviceProfile = new DeviceProfile(new DeviceProfileId(UUID.fromString("dc5766e2-1a32-4022-859b-743050097ab7"))); deviceProfile.setDefaultQueueName(DataConstants.MAIN_QUEUE_NAME); - TbMsg requestMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg requestMsg = TbMsg.newMsg() + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); when(deviceProfileCache.get(any(TenantId.class), any(DeviceId.class))).thenReturn(deviceProfile); when(producerProvider.getRuleEngineMsgProducer()).thenReturn(tbREQueueProducer); @@ -349,7 +371,7 @@ public class DefaultTbClusterServiceTest { clusterService.pushMsgToRuleEngine(tenantId, deviceId, requestMsg, false, callback); verify(producerProvider).getRuleEngineMsgProducer(); - TbMsg expectedMsg = TbMsg.transformMsgQueueName(requestMsg, DataConstants.MAIN_QUEUE_NAME); + TbMsg expectedMsg = requestMsg.transform(DataConstants.MAIN_QUEUE_NAME); ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); verify(ruleEngineProducerService).sendToRuleEngine(eq(tbREQueueProducer), eq(tenantId), actualMsg.capture(), eq(callback)); assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); @@ -375,12 +397,12 @@ public class DefaultTbClusterServiceTest { device.setDeviceProfileId(deviceProfileId); // device updated - TbMsg tbMsg = TbMsg.builder().internalType(TbMsgType.ENTITY_UPDATED).build(); + TbMsg tbMsg = TbMsg.newMsg().type(TbMsgType.ENTITY_UPDATED).build(); ((DefaultTbClusterService) clusterService).getRuleEngineProfileForEntityOrElseNull(tenantId, deviceId, tbMsg); verify(deviceProfileCache, times(1)).get(tenantId, deviceId); // device deleted - tbMsg = TbMsg.builder().internalType(TbMsgType.ENTITY_DELETED).data(JacksonUtil.toString(device)).build(); + tbMsg = TbMsg.newMsg().type(TbMsgType.ENTITY_DELETED).data(JacksonUtil.toString(device)).build(); ((DefaultTbClusterService) clusterService).getRuleEngineProfileForEntityOrElseNull(tenantId, deviceId, tbMsg); verify(deviceProfileCache, times(1)).get(tenantId, deviceProfileId); } @@ -395,12 +417,12 @@ public class DefaultTbClusterServiceTest { asset.setAssetProfileId(assetProfileId); // asset updated - TbMsg tbMsg = TbMsg.builder().internalType(TbMsgType.ENTITY_UPDATED).build(); + TbMsg tbMsg = TbMsg.newMsg().type(TbMsgType.ENTITY_UPDATED).build(); ((DefaultTbClusterService) clusterService).getRuleEngineProfileForEntityOrElseNull(tenantId, assetId, tbMsg); verify(assetProfileCache, times(1)).get(tenantId, assetId); // asset deleted - tbMsg = TbMsg.builder().internalType(TbMsgType.ENTITY_DELETED).data(JacksonUtil.toString(asset)).build(); + tbMsg = TbMsg.newMsg().type(TbMsgType.ENTITY_DELETED).data(JacksonUtil.toString(asset)).build(); ((DefaultTbClusterService) clusterService).getRuleEngineProfileForEntityOrElseNull(tenantId, assetId, tbMsg); verify(assetProfileCache, times(1)).get(tenantId, assetProfileId); } diff --git a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java index 66e3de13d1..0b6e70b84c 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java @@ -782,7 +782,12 @@ public class TbRuleEngineQueueConsumerManagerTest { } public void setUpTestMsg() { - testMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, new DeviceId(UUID.randomUUID()), new TbMsgMetaData(), "{}"); + testMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(new DeviceId(UUID.randomUUID())) + .copyMetaData(new TbMsgMetaData()) + .data("{}") + .build(); } } diff --git a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java index 098c622e31..a24582099e 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java @@ -248,7 +248,7 @@ public class TbRuleEngineStrategyTest { } private static TbMsg createRandomMsg() { - return TbMsg.builder() + return TbMsg.newMsg() .id(UUID.randomUUID()) .type("test type") .originator(deviceId) diff --git a/application/src/test/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcServiceTest.java b/application/src/test/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcServiceTest.java index 2cab5f991a..be079017c3 100644 --- a/application/src/test/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcServiceTest.java @@ -46,7 +46,12 @@ class DefaultTbRuleEngineRpcServiceTest { String serviceId = "tb-core-0"; UUID requestId = UUID.fromString("f64a20df-eb1e-46a3-ba6f-0b3ae053ee0a"); DeviceId deviceId = new DeviceId(UUID.fromString("1d9f771a-7cdc-4ac7-838c-ba193d05a012")); - TbMsg msg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); var restApiCallResponseMsgProto = TransportProtos.RestApiCallResponseMsgProto.newBuilder() .setRequestIdMSB(requestId.getMostSignificantBits()) .setRequestIdLSB(requestId.getLeastSignificantBits()) diff --git a/application/src/test/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallServiceTest.java b/application/src/test/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallServiceTest.java index c49149bd07..a4112ef8d6 100644 --- a/application/src/test/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallServiceTest.java @@ -85,7 +85,13 @@ public class DefaultRuleEngineCallServiceTest { metaData.put("serviceId", "core"); metaData.put("requestUUID", requestId.toString()); metaData.put("expirationTime", Long.toString(expTime)); - TbMsg msg = TbMsg.newMsg(DataConstants.MAIN_QUEUE_NAME, TbMsgType.REST_API_REQUEST, TENANT_ID, new TbMsgMetaData(metaData), "{\"key\":\"value\"}"); + TbMsg msg = TbMsg.newMsg() + .queueName(DataConstants.MAIN_QUEUE_NAME) + .type(TbMsgType.REST_API_REQUEST) + .originator(TENANT_ID) + .copyMetaData(new TbMsgMetaData(metaData)) + .data("{\"key\":\"value\"}") + .build(); Consumer anyConsumer = TbMsg::getData; doAnswer(invocation -> { @@ -113,7 +119,13 @@ public class DefaultRuleEngineCallServiceTest { metaData.put("serviceId", "core"); metaData.put("requestUUID", requestId.toString()); metaData.put("expirationTime", Long.toString(expTime)); - TbMsg msg = TbMsg.newMsg(DataConstants.MAIN_QUEUE_NAME, TbMsgType.REST_API_REQUEST, TENANT_ID, new TbMsgMetaData(metaData), "{\"key\":\"value\"}"); + TbMsg msg = TbMsg.newMsg() + .queueName(DataConstants.MAIN_QUEUE_NAME) + .type(TbMsgType.REST_API_REQUEST) + .originator(TENANT_ID) + .copyMetaData(new TbMsgMetaData(metaData)) + .data("{\"key\":\"value\"}") + .build(); Consumer anyConsumer = TbMsg::getData; doAnswer(invocation -> { diff --git a/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java b/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java index 02281abac3..079bf8e1e2 100644 --- a/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java @@ -131,11 +131,13 @@ public class SequentialTimeseriesPersistenceTest extends AbstractControllerTest void saveLatestTsForAssetAndDevice(List devices, Asset asset, int idx) throws ExecutionException, InterruptedException, TimeoutException { for (Device device : devices) { - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, - device.getId(), - getTbMsgMetadata(device.getName(), ts.get(idx)), - TbMsgDataType.JSON, - getTbMsgData(msgValue.get(idx))); + TbMsg tbMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(device.getId()) + .copyMetaData(getTbMsgMetadata(device.getName(), ts.get(idx))) + .dataType(TbMsgDataType.JSON) + .data(getTbMsgData(msgValue.get(idx))) + .build(); saveDeviceTsEntry(device.getId(), tbMsg, msgValue.get(idx)); saveAssetTsEntry(asset, device.getName(), msgValue.get(idx), TbMsgTimeseriesNode.computeTs(tbMsg, configuration.isUseServerTs())); idx++; diff --git a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java index 296191d61b..b58d19e0e5 100644 --- a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java @@ -27,6 +27,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Device; @@ -72,7 +73,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; @@ -82,6 +83,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.thingsboard.server.service.state.DefaultDeviceStateService.ACTIVITY_STATE; @@ -208,9 +210,12 @@ public class DefaultDeviceStateServiceTest { service.onDeviceConnect(tenantId, deviceId, lastConnectTime); // THEN - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), eq(LAST_CONNECT_TIME), eq(lastConnectTime), any() - ); + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && + request.getScope().equals(AttributeScope.SERVER_SCOPE) && + request.getEntries().get(0).getKey().equals(LAST_CONNECT_TIME) && + request.getEntries().get(0).getValue().equals(lastConnectTime) + )); var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(clusterService).should().pushMsgToRuleEngine(eq(tenantId), eq(deviceId), msgCaptor.capture(), any()); @@ -292,10 +297,12 @@ public class DefaultDeviceStateServiceTest { service.onDeviceDisconnect(tenantId, deviceId, lastDisconnectTime); // THEN - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), - eq(LAST_DISCONNECT_TIME), eq(lastDisconnectTime), any() - ); + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && + request.getScope().equals(AttributeScope.SERVER_SCOPE) && + request.getEntries().get(0).getKey().equals(LAST_DISCONNECT_TIME) && + request.getEntries().get(0).getValue().equals(lastDisconnectTime) + )); var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(clusterService).should().pushMsgToRuleEngine(eq(tenantId), eq(deviceId), msgCaptor.capture(), any()); @@ -413,14 +420,18 @@ public class DefaultDeviceStateServiceTest { service.onDeviceInactivity(tenantId, deviceId, lastInactivityTime); // THEN - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), - eq(INACTIVITY_ALARM_TIME), eq(lastInactivityTime), any() - ); - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), - eq(ACTIVITY_STATE), eq(false), any() - ); + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && + request.getScope().equals(AttributeScope.SERVER_SCOPE) && + request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) && + request.getEntries().get(0).getValue().equals(lastInactivityTime) + )); + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && + request.getScope().equals(AttributeScope.SERVER_SCOPE) && + request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(false) + )); var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(clusterService).should() @@ -453,14 +464,17 @@ public class DefaultDeviceStateServiceTest { service.updateInactivityStateIfExpired(System.currentTimeMillis(), deviceId, deviceStateData); // THEN - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), - eq(INACTIVITY_ALARM_TIME), anyLong(), any() - ); - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), - eq(ACTIVITY_STATE), eq(false), any() - ); + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && + request.getScope().equals(AttributeScope.SERVER_SCOPE) && + request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) + )); + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && + request.getScope().equals(AttributeScope.SERVER_SCOPE) && + request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(false) + )); var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(clusterService).should() @@ -612,7 +626,9 @@ public class DefaultDeviceStateServiceTest { long newTimeout = System.currentTimeMillis() - deviceState.getLastActivityTime() + increase; service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); - verify(telemetrySubscriptionService, never()).saveAttrAndNotify(any(), eq(deviceId), any(AttributeScope.class), eq(ACTIVITY_STATE), any(), any()); + verify(telemetrySubscriptionService, never()).saveAttributes(argThat(request -> + request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) + )); Thread.sleep(defaultTimeout + increase); service.checkStates(); activityVerify(false); @@ -651,7 +667,9 @@ public class DefaultDeviceStateServiceTest { long newTimeout = 1; Thread.sleep(newTimeout); - verify(telemetrySubscriptionService, never()).saveAttrAndNotify(any(), eq(deviceId), any(AttributeScope.class), eq(ACTIVITY_STATE), any(), any()); + verify(telemetrySubscriptionService, never()).saveAttributes(argThat(request -> + request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) + )); } @Test @@ -672,8 +690,6 @@ public class DefaultDeviceStateServiceTest { service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); activityVerify(true); - verify(telemetrySubscriptionService, never()).saveAttrAndNotify(any(), eq(deviceId), any(AttributeScope.class), eq(ACTIVITY_STATE), any(), any()); - long newTimeout = 1; Thread.sleep(newTimeout); @@ -713,11 +729,17 @@ public class DefaultDeviceStateServiceTest { long newTimeout = 1; service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); - verify(telemetrySubscriptionService, never()).saveAttrAndNotify(any(), eq(deviceId), any(AttributeScope.class), eq(ACTIVITY_STATE), any(), any()); + verify(telemetrySubscriptionService, never()).saveAttributes(argThat(request -> + request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) + )); } private void activityVerify(boolean isActive) { - verify(telemetrySubscriptionService).saveAttrAndNotify(any(), eq(deviceId), any(AttributeScope.class), eq(ACTIVITY_STATE), eq(isActive), any()); + verify(telemetrySubscriptionService).saveAttributes(argThat(request -> + request.getEntityId().equals(deviceId) && + request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(isActive) + )); } @Test @@ -763,21 +785,27 @@ public class DefaultDeviceStateServiceTest { // THEN assertThat(deviceState.isActive()).isEqualTo(true); assertThat(deviceState.getLastActivityTime()).isEqualTo(lastReportedActivity); - then(telemetrySubscriptionService).should().saveAttrAndNotify( - any(), eq(deviceId), any(AttributeScope.class), eq(LAST_ACTIVITY_TIME), eq(lastReportedActivity), any() - ); + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + request.getEntityId().equals(deviceId) && + request.getEntries().get(0).getKey().equals(LAST_ACTIVITY_TIME) && + request.getEntries().get(0).getValue().equals(lastReportedActivity) + )); assertThat(deviceState.getLastInactivityAlarmTime()).isEqualTo(expectedInactivityAlarmTime); if (shouldSetInactivityAlarmTimeToZero) { - then(telemetrySubscriptionService).should().saveAttrAndNotify( - any(), eq(deviceId), any(AttributeScope.class), eq(INACTIVITY_ALARM_TIME), eq(0L), any() - ); + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + request.getEntityId().equals(deviceId) && + request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) && + request.getEntries().get(0).getValue().equals(0L) + )); } if (shouldUpdateActivityStateToActive) { - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), eq(ACTIVITY_STATE), eq(true), any() - ); + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + request.getEntityId().equals(deviceId) && + request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(true) + )); var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(clusterService).should().pushMsgToRuleEngine(eq(tenantId), eq(deviceId), msgCaptor.capture(), any()); @@ -796,28 +824,28 @@ public class DefaultDeviceStateServiceTest { private static Stream provideParametersForUpdateActivityState() { return Stream.of( - Arguments.of(true, 100, 120, 80, 80, false, false), + Arguments.of(true, 100, 120, 80, 80, false, false), - Arguments.of(true, 100, 120, 100, 100, false, false), + Arguments.of(true, 100, 120, 100, 100, false, false), Arguments.of(false, 100, 120, 110, 110, false, true), - Arguments.of(true, 100, 100, 80, 80, false, false), + Arguments.of(true, 100, 100, 80, 80, false, false), - Arguments.of(true, 100, 100, 100, 100, false, false), + Arguments.of(true, 100, 100, 100, 100, false, false), - Arguments.of(false, 100, 100, 110, 0, true, true), + Arguments.of(false, 100, 100, 110, 0, true, true), - Arguments.of(false, 100, 110, 110, 0, true, true), + Arguments.of(false, 100, 110, 110, 0, true, true), - Arguments.of(false, 100, 110, 120, 0, true, true), + Arguments.of(false, 100, 110, 120, 0, true, true), - Arguments.of(true, 0, 0, 0, 0, false, false), + Arguments.of(true, 0, 0, 0, 0, false, false), - Arguments.of(false, 0, 0, 0, 0, true, true) + Arguments.of(false, 0, 0, 0, 0, true, true) ); } @@ -857,9 +885,10 @@ public class DefaultDeviceStateServiceTest { assertThat(deviceState.getInactivityTimeout()).isEqualTo(newInactivityTimeout); assertThat(deviceState.isActive()).isEqualTo(expectedActivityState); if (activityState && !expectedActivityState) { - then(telemetrySubscriptionService).should().saveAttrAndNotify( - any(), eq(deviceId), any(AttributeScope.class), eq(ACTIVITY_STATE), eq(false), any() - ); + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(false) + )); } } @@ -954,9 +983,10 @@ public class DefaultDeviceStateServiceTest { assertThat(state.getLastInactivityAlarmTime()).isEqualTo(expectedLastInactivityAlarmTime); if (shouldUpdateActivityStateToInactive) { - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), eq(ACTIVITY_STATE), eq(false), any() - ); + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(false) + )); var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(clusterService).should().pushMsgToRuleEngine(eq(tenantId), eq(deviceId), msgCaptor.capture(), any()); @@ -971,72 +1001,74 @@ public class DefaultDeviceStateServiceTest { assertThat(actualNotification.getDeviceId()).isEqualTo(deviceId); assertThat(actualNotification.isActive()).isFalse(); - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), - eq(INACTIVITY_ALARM_TIME), eq(expectedLastInactivityAlarmTime), any() - ); + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && + request.getScope().equals(AttributeScope.SERVER_SCOPE) && + request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) && + request.getEntries().get(0).getValue().equals(expectedLastInactivityAlarmTime) + )); } } private static Stream provideParametersForUpdateInactivityStateIfExpired() { return Stream.of( - Arguments.of(false, 100, 70, 90, 70, 60, false, 90, false), + Arguments.of(false, 100, 70, 90, 70, 60, false, 90, false), - Arguments.of(false, 100, 40, 50, 70, 10, false, 50, false), + Arguments.of(false, 100, 40, 50, 70, 10, false, 50, false), - Arguments.of(false, 100, 25, 60, 75, 25, false, 60, false), + Arguments.of(false, 100, 25, 60, 75, 25, false, 60, false), - Arguments.of(false, 100, 60, 70, 10, 50, false, 70, false), + Arguments.of(false, 100, 60, 70, 10, 50, false, 70, false), - Arguments.of(false, 100, 10, 15, 90, 10, false, 15, false), + Arguments.of(false, 100, 10, 15, 90, 10, false, 15, false), - Arguments.of(false, 100, 0, 40, 75, 0, false, 40, false), + Arguments.of(false, 100, 0, 40, 75, 0, false, 40, false), - Arguments.of(true, 100, 90, 80, 80, 50, true, 80, false), + Arguments.of(true, 100, 90, 80, 80, 50, true, 80, false), - Arguments.of(true, 100, 95, 90, 10, 50, true, 90, false), + Arguments.of(true, 100, 95, 90, 10, 50, true, 90, false), - Arguments.of(true, 100, 10, 10, 90, 10, false, 100, true), + Arguments.of(true, 100, 10, 10, 90, 10, false, 100, true), - Arguments.of(true, 100, 10, 10, 90, 11, true, 10, false), + Arguments.of(true, 100, 10, 10, 90, 11, true, 10, false), - Arguments.of(true, 100, 15, 10, 85, 5, false, 100, true), + Arguments.of(true, 100, 15, 10, 85, 5, false, 100, true), - Arguments.of(true, 100, 15, 10, 75, 5, false, 100, true), + Arguments.of(true, 100, 15, 10, 75, 5, false, 100, true), - Arguments.of(true, 100, 95, 90, 5, 50, false, 100, true), + Arguments.of(true, 100, 95, 90, 5, 50, false, 100, true), - Arguments.of(true, 100, 0, 0, 101, 0, true, 0, false), + Arguments.of(true, 100, 0, 0, 101, 0, true, 0, false), - Arguments.of(true, 100, 0, 0, 100, 0, false, 100, true), + Arguments.of(true, 100, 0, 0, 100, 0, false, 100, true), - Arguments.of(true, 100, 0, 0, 99, 0, false, 100, true), + Arguments.of(true, 100, 0, 0, 99, 0, false, 100, true), - Arguments.of(true, 100, 0, 0, 120, 10, true, 0, false), + Arguments.of(true, 100, 0, 0, 120, 10, true, 0, false), - Arguments.of(true, 100, 50, 0, 100, 0, true, 0, false), + Arguments.of(true, 100, 50, 0, 100, 0, true, 0, false), - Arguments.of(true, 100, 10, 0, 91, 0, true, 0, false), + Arguments.of(true, 100, 10, 0, 91, 0, true, 0, false), - Arguments.of(true, 100, 90, 0, 10, 0, false, 100, true), + Arguments.of(true, 100, 90, 0, 10, 0, false, 100, true), - Arguments.of(true, 100, 100, 100, 1, 0, true, 100, false), + Arguments.of(true, 100, 100, 100, 1, 0, true, 100, false), - Arguments.of(true, 100, 100, 100, 100, 100, true, 100, false), + Arguments.of(true, 100, 100, 100, 100, 100, true, 100, false), - Arguments.of(false, 100, 59, 60, 30, 10, false, 60, false), + Arguments.of(false, 100, 59, 60, 30, 10, false, 60, false), - Arguments.of(true, 100, 60, 60, 30, 10, false, 100, true), + Arguments.of(true, 100, 60, 60, 30, 10, false, 100, true), - Arguments.of(true, 100, 61, 60, 30, 10, false, 100, true), + Arguments.of(true, 100, 61, 60, 30, 10, false, 100, true), - Arguments.of(true, 0, 0, 0, 1, 0, true, 0, false), + Arguments.of(true, 0, 0, 0, 1, 0, true, 0, false), - Arguments.of(true, 0, 0, 0, 0, 0, false, 0, true), + Arguments.of(true, 0, 0, 0, 0, 0, false, 0, true), - Arguments.of(true, 100, 90, 80, 20, 70, true, 80, false), + Arguments.of(true, 100, 90, 80, 20, 70, true, 80, false), - Arguments.of(true, 100, 80, 90, 30, 70, true, 90, false) + Arguments.of(true, 100, 80, 90, 30, 70, true, 90, false) ); } @@ -1100,7 +1132,10 @@ public class DefaultDeviceStateServiceTest { // THEN await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(false); - then(telemetrySubscriptionService).should().saveAttrAndNotify(eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), eq(ACTIVITY_STATE), eq(false), any()); + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(false) + )); }); } @@ -1127,10 +1162,31 @@ public class DefaultDeviceStateServiceTest { service.onDeviceActivity(tenantId, deviceId, currentTime); // THEN + ArgumentCaptor attributeRequestCaptor = ArgumentCaptor.forClass(AttributesSaveRequest.class); + then(telemetrySubscriptionService).should(times(2)).saveAttributes(attributeRequestCaptor.capture()); + await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(true); - then(telemetrySubscriptionService).should().saveAttrAndNotify(eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), eq(LAST_ACTIVITY_TIME), eq(currentTime), any()); - then(telemetrySubscriptionService).should().saveAttrAndNotify(eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), eq(ACTIVITY_STATE), eq(true), any()); + + assertThat(attributeRequestCaptor.getAllValues()).hasSize(2) + .anySatisfy(request -> { + assertThat(request.getTenantId()).isEqualTo(TenantId.SYS_TENANT_ID); + assertThat(request.getEntityId()).isEqualTo(deviceId); + assertThat(request.getScope()).isEqualTo(AttributeScope.SERVER_SCOPE); + assertThat(request.getEntries()).singleElement().satisfies(attributeKvEntry -> { + assertThat(attributeKvEntry.getKey()).isEqualTo(LAST_ACTIVITY_TIME); + assertThat(attributeKvEntry.getLongValue()).hasValue(currentTime); + }); + }) + .anySatisfy(request -> { + assertThat(request.getTenantId()).isEqualTo(TenantId.SYS_TENANT_ID); + assertThat(request.getEntityId()).isEqualTo(deviceId); + assertThat(request.getScope()).isEqualTo(AttributeScope.SERVER_SCOPE); + assertThat(request.getEntries()).singleElement().satisfies(attributeKvEntry -> { + assertThat(attributeKvEntry.getKey()).isEqualTo(ACTIVITY_STATE); + assertThat(attributeKvEntry.getBooleanValue()).hasValue(true); + }); + }); }); } @@ -1174,7 +1230,10 @@ public class DefaultDeviceStateServiceTest { // THEN await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(true); - then(telemetrySubscriptionService).should().saveAttrAndNotify(eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), eq(ACTIVITY_STATE), eq(true), any()); + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(true) + )); }); } diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java index f83a20b139..2f0127643a 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java @@ -21,6 +21,7 @@ import org.eclipse.californium.elements.config.Configuration; import org.eclipse.californium.elements.util.SslContextUtil; import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.dtls.CertificateType; +import org.eclipse.californium.scandium.dtls.MaxFragmentLengthExtension.Length; import org.eclipse.californium.scandium.dtls.x509.SingleCertificateProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -44,6 +45,8 @@ import static org.eclipse.californium.elements.config.CertificateAuthenticationM import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CLIENT_AUTHENTICATION_MODE; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_NODE_ID; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_MAX_FRAGMENT_LENGTH; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_MAX_TRANSMISSION_UNIT; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RETRANSMISSION_TIMEOUT; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_ROLE; import static org.eclipse.californium.scandium.config.DtlsConfig.DtlsRole.SERVER_ONLY; @@ -66,6 +69,12 @@ public class TbCoapDtlsSettings { @Value("${coap.dtls.connection_id_length:}") private Integer cIdLength; + @Value("${coap.dtls.max_transmission_unit:1024}") + private Integer maxTransmissionUnit; + + @Value("${coap.dtls.max_fragment_length:1024}") + private Integer maxFragmentLength; + @Bean @ConfigurationProperties(prefix = "coap.dtls.credentials") public SslCredentialsConfig coapDtlsCredentials() { @@ -108,6 +117,15 @@ public class TbCoapDtlsSettings { configBuilder.set(DTLS_CONNECTION_ID_NODE_ID, null); } } + if (maxTransmissionUnit > 0) { + configBuilder.set(DTLS_MAX_TRANSMISSION_UNIT, maxTransmissionUnit); + } + if (maxFragmentLength > 0) { + Length length = fromLength(maxFragmentLength); + if (length != null) { + configBuilder.set(DTLS_MAX_FRAGMENT_LENGTH, length); + } + } configBuilder.setAdvancedCertificateVerifier( new TbCoapDtlsCertificateVerifier( transportService, @@ -127,4 +145,14 @@ public class TbCoapDtlsSettings { return new InetSocketAddress(addr, port); } + + private static Length fromLength(int length) { + for (Length l : Length.values()) { + if (l.length() == length) { + return l; + } + } + return null; + } } + diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java index a205ee9b84..8668551fbb 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java @@ -37,16 +37,10 @@ public interface AttributesService { ListenableFuture> findAll(TenantId tenantId, EntityId entityId, AttributeScope scope); - @Deprecated(since = "3.7.0") - ListenableFuture> save(TenantId tenantId, EntityId entityId, String scope, List attributes); - ListenableFuture> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes); ListenableFuture save(TenantId tenantId, EntityId entityId, AttributeScope scope, AttributeKvEntry attribute); - @Deprecated(since = "3.7.0") - ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, String scope, List attributeKeys); - ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributeKeys); List findAllKeysByDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java index 4a80775390..e2a0a3e467 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java @@ -43,7 +43,7 @@ public interface NotificationRequestService { List findNotificationRequestsIdsByStatusAndRuleId(TenantId tenantId, NotificationRequestStatus requestStatus, NotificationRuleId ruleId); - List findNotificationRequestsByRuleIdAndOriginatorEntityId(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId); + List findNotificationRequestsByRuleIdAndOriginatorEntityIdAndStatus(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId, NotificationRequestStatus status); void deleteNotificationRequest(TenantId tenantId, NotificationRequest request); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java index fc282f6b74..a83b95ae34 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java @@ -64,7 +64,7 @@ public interface ImageService { boolean updateImagesUsage(WidgetTypeDetails widgetType); - void inlineImage(HasImage entity); + T inlineImage(T entity); Collection getUsedImages(Dashboard dashboard); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java index eaba144042..49918f3823 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java @@ -56,7 +56,7 @@ public interface TimeseriesService { ListenableFuture> removeLatest(TenantId tenantId, EntityId entityId, Collection keys); - ListenableFuture> removeAllLatest(TenantId tenantId, EntityId entityId); + ListenableFuture> removeAllLatest(TenantId tenantId, EntityId entityId); List findAllKeysByDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId); diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java index 47fb80c90e..64e05770fe 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java @@ -19,8 +19,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -44,8 +42,6 @@ import java.util.UUID; */ @Data @Slf4j -@AllArgsConstructor(access = AccessLevel.PRIVATE) -@Builder(toBuilder = true) public final class TbMsg implements Serializable { public static final String EMPTY_JSON_OBJECT = "{}"; @@ -77,279 +73,55 @@ public final class TbMsg implements Serializable { @JsonIgnore transient private final TbMsgCallback callback; - public int getAndIncrementRuleNodeCounter() { - return ctx.getAndIncrementRuleNodeCounter(); - } - - @Deprecated(since = "3.6.0", forRemoval = true) - public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return newMsg(queueName, type, originator, null, metaData, data, ruleChainId, ruleNodeId); - } - - /** - * Creates a new TbMsg instance with the specified parameters. - * - *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to - * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, - * it is recommended to use the {@link #newMsg(String, TbMsgType, EntityId, CustomerId, TbMsgMetaData, String, RuleChainId, RuleNodeId)} - * method instead.

- * - * @param queueName the name of the queue where the message will be sent - * @param type the type of the message - * @param originator the originator of the message - * @param customerId the ID of the customer associated with the message - * @param metaData the metadata of the message - * @param data the data of the message - * @param ruleChainId the ID of the rule chain associated with the message - * @param ruleNodeId the ID of the rule node associated with the message - * @return new TbMsg instance - */ - @Deprecated(since = "3.6.0") - public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, customerId, - metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY); - } - - @Deprecated(since = "3.6.0", forRemoval = true) - public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) { - return newMsg(type, originator, null, metaData, data); - } - - @Deprecated(since = "3.6.0", forRemoval = true) - public static TbMsg newMsg(String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, customerId, - metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY); - } - - public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return newMsg(queueName, type, originator, null, metaData, data, ruleChainId, ruleNodeId); - } - - public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, - metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY); - } - - public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { - return newMsg(type, originator, null, metaData, data); - } - - public static TbMsg newMsg(TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, - metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY); - } - - public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data, long ts) { - return new TbMsg(null, UUID.randomUUID(), ts, type, originator, null, - metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY); - } - - // REALLY NEW MSG - - /** - * Creates a new TbMsg instance with the specified parameters. - * - *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to - * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, - * it is recommended to use the {@link #newMsg(String, TbMsgType, EntityId, TbMsgMetaData, String)} - * method instead.

- * - * @param queueName the name of the queue where the message will be sent - * @param type the type of the message - * @param originator the originator of the message - * @param metaData the metadata of the message - * @param data the data of the message - * @return new TbMsg instance - */ - @Deprecated(since = "3.6.0") - public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) { - return newMsg(queueName, type, originator, null, metaData, data); - } - - /** - * Creates a new TbMsg instance with the specified parameters. - * - *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to - * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, - * it is recommended to use the {@link #newMsg(String, TbMsgType, EntityId, CustomerId, TbMsgMetaData, String)} - * method instead.

- * - * @param queueName the name of the queue where the message will be sent - * @param type the type of the message - * @param originator the originator of the message - * @param customerId the ID of the customer associated with the message - * @param metaData the metadata of the message - * @param data the data of the message - * @return new TbMsg instance - */ - @Deprecated(since = "3.6.0") - public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, customerId, - metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY); - } - - @Deprecated(since = "3.6.0", forRemoval = true) - public static TbMsg newMsg(String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, customerId, - metaData.copy(), dataType, data, null, null, null, TbMsgCallback.EMPTY); - } - - /** - * Creates a new TbMsg instance with the specified parameters. - * - *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to - * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, - * it is recommended to use the {@link #newMsg(TbMsgType, EntityId, TbMsgMetaData, TbMsgDataType, String)} - * method instead.

- * - * @param type the type of the message - * @param originator the originator of the message - * @param metaData the metadata of the message - * @param dataType the dataType of the message - * @param data the data of the message - * @return new TbMsg instance - */ - @Deprecated(since = "3.6.0") - public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { - return newMsg(type, originator, null, metaData, dataType, data); - } - - public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { - return newMsg(queueName, type, originator, null, metaData, data); - } - - public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, - metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY); - } - - public static TbMsg newMsg(TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, - metaData.copy(), dataType, data, null, null, null, TbMsgCallback.EMPTY); - } - - public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { - return newMsg(type, originator, null, metaData, dataType, data); - } - - // For Tests only - - @Deprecated(since = "3.6.0", forRemoval = true) - public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, null, - metaData.copy(), dataType, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY); - } - - @Deprecated(since = "3.6.0", forRemoval = true) - public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, null, - metaData.copy(), TbMsgDataType.JSON, data, null, null, null, callback); - } - - /** - * Transforms an existing TbMsg instance by changing its message type, originator, metadata, and data. - * - *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to - * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, - * it is recommended to use the {@link #transformMsg(TbMsg, TbMsgType, EntityId, TbMsgMetaData, String)} - * method instead.

- * - * - * @param tbMsg the TbMsg instance to transform - * @param type the new message type - * @param originator the new originator - * @param metaData the new metadata - * @param data the new data - * @return the transformed TbMsg instance - */ - @Deprecated(since = "3.6.0") - public static TbMsg transformMsg(TbMsg tbMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, null, type, originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType, - data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.callback); + public static TbMsgBuilder newMsg() { + return new TbMsgBuilder(); } - public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), type, originator, null, - metaData.copy(), dataType, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY); + public TbMsgBuilder transform() { + return new TbMsgTransformer(this); } - public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), type, originator, null, - metaData.copy(), TbMsgDataType.JSON, data, null, null, null, callback); + public TbMsgBuilder copy() { + return new TbMsgBuilder(this); } - public static TbMsg transformMsg(TbMsg tbMsg, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, type, type.name(), originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType, - data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.callback); + public TbMsg transform(String queueName) { + return transform() + .queueName(queueName) + .resetRuleNodeId() + .build(); } - public static TbMsg transformMsgOriginator(TbMsg tbMsg, EntityId originatorId) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, originatorId, tbMsg.getCustomerId(), tbMsg.metaData, tbMsg.dataType, - tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsgData(TbMsg tbMsg, String data) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, - data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsgMetadata(TbMsg tbMsg, TbMsgMetaData metadata) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, metadata.copy(), tbMsg.dataType, - tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsg(TbMsg tbMsg, TbMsgMetaData metadata, String data) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, metadata, tbMsg.dataType, - data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsgCustomerId(TbMsg tbMsg, CustomerId customerId) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, customerId, tbMsg.metaData, tbMsg.dataType, - tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsgRuleChainId(TbMsg tbMsg, RuleChainId ruleChainId) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, - tbMsg.data, ruleChainId, null, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsgQueueName(TbMsg tbMsg, String queueName) { - return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, - tbMsg.data, tbMsg.getRuleChainId(), null, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, String queueName) { - return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, - tbMsg.data, ruleChainId, null, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - //used for enqueueForTellNext + // used for enqueueForTellNext public static TbMsg newMsg(TbMsg tbMsg, String queueName, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(queueName, UUID.randomUUID(), tbMsg.getTs(), tbMsg.getInternalType(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.customerId, tbMsg.getMetaData().copy(), - tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), TbMsgCallback.EMPTY); - } - - private TbMsg(String queueName, UUID id, long ts, TbMsgType internalType, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data, - RuleChainId ruleChainId, RuleNodeId ruleNodeId, TbMsgProcessingCtx ctx, TbMsgCallback callback) { - this(queueName, id, ts, internalType, internalType.name(), originator, customerId, metaData, dataType, data, ruleChainId, ruleNodeId, ctx, callback); + return tbMsg.transform() + .id(UUID.randomUUID()) + .queueName(queueName) + .metaData(tbMsg.getMetaData()) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .callback(TbMsgCallback.EMPTY) + .build(); } - private TbMsg(String queueName, UUID id, long ts, TbMsgType internalType, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data, - RuleChainId ruleChainId, RuleNodeId ruleNodeId, TbMsgProcessingCtx ctx, TbMsgCallback callback) { - this(queueName, id, ts, internalType, type, originator, customerId, metaData, dataType, data, ruleChainId, ruleNodeId, null, null, ctx, callback); + public TbMsg copyWithNewCtx() { + return copy() + .ctx(ctx.copy()) + .callback(TbMsgCallback.EMPTY) + .build(); } private TbMsg(String queueName, UUID id, long ts, TbMsgType internalType, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId, UUID correlationId, Integer partition, TbMsgProcessingCtx ctx, TbMsgCallback callback) { - this.id = id; + this.id = id != null ? id : UUID.randomUUID(); this.queueName = queueName; if (ts > 0) { this.ts = ts; } else { this.ts = System.currentTimeMillis(); } - this.type = type; this.internalType = internalType != null ? internalType : getInternalType(type); + this.type = type != null ? type : this.internalType.name(); this.originator = originator; if (customerId == null || customerId.isNullUid()) { if (originator != null && originator.getEntityType() == EntityType.CUSTOMER) { @@ -361,7 +133,7 @@ public final class TbMsg implements Serializable { this.customerId = customerId; } this.metaData = metaData; - this.dataType = dataType; + this.dataType = dataType != null ? dataType : TbMsgDataType.JSON; this.data = data; this.ruleChainId = ruleChainId; this.ruleNodeId = ruleNodeId; @@ -458,23 +230,8 @@ public final class TbMsg implements Serializable { } } - public TbMsg copyWithRuleChainId(RuleChainId ruleChainId) { - return copyWithRuleChainId(ruleChainId, this.id); - } - - public TbMsg copyWithRuleChainId(RuleChainId ruleChainId, UUID msgId) { - return new TbMsg(this.queueName, msgId, this.ts, this.internalType, this.type, this.originator, this.customerId, - this.metaData, this.dataType, this.data, ruleChainId, null, this.correlationId, this.partition, this.ctx, callback); - } - - public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId, UUID msgId) { - return new TbMsg(this.queueName, msgId, this.ts, this.internalType, this.type, this.originator, this.customerId, - this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.correlationId, this.partition, this.ctx, callback); - } - - public TbMsg copyWithNewCtx() { - return new TbMsg(this.queueName, this.id, this.ts, this.internalType, this.type, this.originator, this.customerId, - this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.correlationId, this.partition, this.ctx.copy(), TbMsgCallback.EMPTY); + public int getAndIncrementRuleNodeCounter() { + return ctx.getAndIncrementRuleNodeCounter(); } public TbMsgCallback getCallback() { @@ -510,11 +267,13 @@ public final class TbMsg implements Serializable { } private TbMsgType getInternalType(String type) { - try { - return TbMsgType.valueOf(type); - } catch (IllegalArgumentException e) { - return TbMsgType.NA; + if (type != null) { + try { + return TbMsgType.valueOf(type); + } catch (IllegalArgumentException ignored) { + } } + return TbMsgType.NA; } public boolean isTypeOf(TbMsgType tbMsgType) { @@ -530,4 +289,194 @@ public final class TbMsg implements Serializable { return false; } + public static class TbMsgTransformer extends TbMsgBuilder { + + TbMsgTransformer(TbMsg tbMsg) { + super(tbMsg); + } + + /* + * metadata is only copied if specified explicitly during transform + * */ + @Override + public TbMsgTransformer metaData(TbMsgMetaData metaData) { + this.metaData = metaData.copy(); + return this; + } + + /* + * setting ruleNodeId to null when updating ruleChainId + * */ + @Override + public TbMsgBuilder ruleChainId(RuleChainId ruleChainId) { + this.ruleChainId = ruleChainId; + this.ruleNodeId = null; + return this; + } + + @Override + public TbMsg build() { + /* + * always copying ctx when transforming + * */ + if (this.ctx != null) { + this.ctx = this.ctx.copy(); + } + return super.build(); + } + + } + + public static class TbMsgBuilder { + + protected String queueName; + protected UUID id; + protected long ts; + protected String type; + protected TbMsgType internalType; + protected EntityId originator; + protected CustomerId customerId; + protected TbMsgMetaData metaData; + protected TbMsgDataType dataType; + protected String data; + protected RuleChainId ruleChainId; + protected RuleNodeId ruleNodeId; + protected UUID correlationId; + protected Integer partition; + protected TbMsgProcessingCtx ctx; + protected TbMsgCallback callback; + + TbMsgBuilder() {} + + TbMsgBuilder(TbMsg tbMsg) { + this.queueName = tbMsg.queueName; + this.id = tbMsg.id; + this.ts = tbMsg.ts; + this.type = tbMsg.type; + this.internalType = tbMsg.internalType; + this.originator = tbMsg.originator; + this.customerId = tbMsg.customerId; + this.metaData = tbMsg.metaData; + this.dataType = tbMsg.dataType; + this.data = tbMsg.data; + this.ruleChainId = tbMsg.ruleChainId; + this.ruleNodeId = tbMsg.ruleNodeId; + this.correlationId = tbMsg.correlationId; + this.partition = tbMsg.partition; + this.ctx = tbMsg.ctx; + this.callback = tbMsg.callback; + } + + public TbMsgBuilder queueName(String queueName) { + this.queueName = queueName; + return this; + } + + public TbMsgBuilder id(UUID id) { + this.id = id; + return this; + } + + public TbMsgBuilder ts(long ts) { + this.ts = ts; + return this; + } + + /** + *

Deprecated: This should only be used when you need to specify a custom message type that doesn't exist in the {@link TbMsgType} enum. + * Prefer using {@link #type(TbMsgType)} instead. + * + * */ + @Deprecated + public TbMsgBuilder type(String type) { + this.type = type; + this.internalType = null; + return this; + } + + public TbMsgBuilder type(TbMsgType internalType) { + this.internalType = internalType; + this.type = internalType.name(); + return this; + } + + public TbMsgBuilder originator(EntityId originator) { + this.originator = originator; + return this; + } + + public TbMsgBuilder customerId(CustomerId customerId) { + this.customerId = customerId; + return this; + } + + public TbMsgBuilder metaData(TbMsgMetaData metaData) { + this.metaData = metaData; + return this; + } + + public TbMsgBuilder copyMetaData(TbMsgMetaData metaData) { + this.metaData = metaData.copy(); + return this; + } + + public TbMsgBuilder dataType(TbMsgDataType dataType) { + this.dataType = dataType; + return this; + } + + public TbMsgBuilder data(String data) { + this.data = data; + return this; + } + + public TbMsgBuilder ruleChainId(RuleChainId ruleChainId) { + this.ruleChainId = ruleChainId; + return this; + } + + public TbMsgBuilder ruleNodeId(RuleNodeId ruleNodeId) { + this.ruleNodeId = ruleNodeId; + return this; + } + + public TbMsgBuilder resetRuleNodeId() { + return ruleNodeId(null); + } + + public TbMsgBuilder correlationId(UUID correlationId) { + this.correlationId = correlationId; + return this; + } + + public TbMsgBuilder partition(Integer partition) { + this.partition = partition; + return this; + } + + public TbMsgBuilder ctx(TbMsgProcessingCtx ctx) { + this.ctx = ctx; + return this; + } + + public TbMsgBuilder callback(TbMsgCallback callback) { + this.callback = callback; + return this; + } + + public TbMsg build() { + return new TbMsg(queueName, id, ts, internalType, type, originator, customerId, metaData, dataType, data, ruleChainId, ruleNodeId, correlationId, partition, ctx, callback); + } + + public String toString() { + return "TbMsg.TbMsgBuilder(queueName=" + this.queueName + ", id=" + this.id + ", ts=" + this.ts + + ", type=" + this.type + ", internalType=" + this.internalType + ", originator=" + this.originator + + ", customerId=" + this.customerId + ", metaData=" + this.metaData + ", dataType=" + this.dataType + + ", data=" + this.data + ", ruleChainId=" + this.ruleChainId + ", ruleNodeId=" + this.ruleNodeId + + ", correlationId=" + this.correlationId + ", partition=" + this.partition + ", ctx=" + this.ctx + + ", callback=" + this.callback + ")"; + } + + } + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/TbRuleEngineProducerService.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/TbRuleEngineProducerService.java index 49b40e3a6d..09f1f4608d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/TbRuleEngineProducerService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/TbRuleEngineProducerService.java @@ -47,7 +47,7 @@ public class TbRuleEngineProducerService { Integer partition = tpi.getPartition().orElse(null); UUID id = i > 0 ? UUID.randomUUID() : tbMsg.getId(); - tbMsg = tbMsg.toBuilder() + tbMsg = tbMsg.transform() .id(id) .correlationId(correlationId) .partition(partition) diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index 44f61270b4..af9dca8583 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -45,7 +45,6 @@ import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.device.data.PowerMode; -import org.thingsboard.server.common.data.exception.TenantNotFoundException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -1137,7 +1136,15 @@ public class DefaultTransportService extends TransportActivityManager implements queueName = deviceProfile.getDefaultQueueName(); } - TbMsg tbMsg = TbMsg.newMsg(queueName, tbMsgType, deviceId, customerId, metaData, gson.toJson(json), ruleChainId, null); + TbMsg tbMsg = TbMsg.newMsg() + .queueName(queueName) + .type(tbMsgType) + .originator(deviceId) + .customerId(customerId) + .copyMetaData(metaData) + .data(gson.toJson(json)) + .ruleChainId(ruleChainId) + .build(); ruleEngineProducerService.sendToRuleEngine(ruleEngineMsgProducer, tenantId, tbMsg, new StatsCallback(callback, ruleEngineProducerStats)); ruleEngineProducerStats.incrementTotal(); } diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java index 33e15baccf..8498b4a7e4 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java @@ -51,7 +51,9 @@ import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.filter.RevFilter; import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.FetchResult; +import org.eclipse.jgit.transport.PushResult; import org.eclipse.jgit.transport.RefSpec; +import org.eclipse.jgit.transport.RemoteRefUpdate; import org.eclipse.jgit.transport.SshTransport; import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; @@ -85,6 +87,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.function.Function; @@ -350,8 +353,16 @@ public class GitRepository { return; } log.debug("Executing push [{}][{}]", settings.getRepositoryUri(), remoteBranch); - execute(git.push() + Iterable result = execute(git.push() .setRefSpecs(new RefSpec(localBranch + ":" + remoteBranch))); + result.forEach(pushResult -> { + for (RemoteRefUpdate update : pushResult.getRemoteUpdates()) { + RemoteRefUpdate.Status status = update.getStatus(); + if (status != RemoteRefUpdate.Status.OK && status != RemoteRefUpdate.Status.UP_TO_DATE) { + throw new RuntimeException("Failed to push changes: " + Optional.ofNullable(update.getMessage()).orElseGet(status::name)); + } + } + }); } public String getContentsDiff(String content1, String content2) throws IOException { diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java index 88d6757de3..0694178540 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java @@ -101,14 +101,6 @@ public class BaseAttributesService implements AttributesService { return attributesDao.save(tenantId, entityId, scope, attribute); } - @Override - public ListenableFuture> save(TenantId tenantId, EntityId entityId, String scope, List attributes) { - validate(entityId, scope); - AttributeUtils.validate(attributes, valueNoXssValidation); - List> saveFutures = attributes.stream().map(attribute -> attributesDao.save(tenantId, entityId, AttributeScope.valueOf(scope), attribute)).collect(Collectors.toList()); - return Futures.allAsList(saveFutures); - } - @Override public ListenableFuture> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes) { validate(entityId, scope); @@ -117,12 +109,6 @@ public class BaseAttributesService implements AttributesService { return Futures.allAsList(saveFutures); } - @Override - public ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, String scope, List attributeKeys) { - validate(entityId, scope); - return Futures.allAsList(attributesDao.removeAll(tenantId, entityId, AttributeScope.valueOf(scope), attributeKeys)); - } - @Override public ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributeKeys) { validate(entityId, scope); diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java index 3de90355ed..1ebd5c0ba4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java @@ -222,11 +222,6 @@ public class CachedAttributesService implements AttributesService { return doSave(tenantId, entityId, scope, attribute); } - @Override - public ListenableFuture> save(TenantId tenantId, EntityId entityId, String scope, List attributes) { - return save(tenantId, entityId, AttributeScope.valueOf(scope), attributes); - } - @Override public ListenableFuture> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes) { validate(entityId, scope); @@ -255,11 +250,6 @@ public class CachedAttributesService implements AttributesService { log.trace("[{}][{}][{}] after cache put.", entityId, scope, key); } - @Override - public ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, String scope, List attributeKeys) { - return removeAll(tenantId, entityId, AttributeScope.valueOf(scope), attributeKeys); - } - @Override public ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributeKeys) { validate(entityId, scope); diff --git a/dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java index 8a735303a7..1582d634b2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java @@ -35,6 +35,7 @@ import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.oauth2.OAuth2ClientDao; +import org.thingsboard.server.dao.service.validator.DomainDataValidator; import java.util.Comparator; import java.util.List; @@ -53,17 +54,20 @@ public class DomainServiceImpl extends AbstractEntityService implements DomainSe private OAuth2ClientDao oauth2ClientDao; @Autowired private DomainDao domainDao; + @Autowired + private DomainDataValidator domainDataValidator; @Override public Domain saveDomain(TenantId tenantId, Domain domain) { log.trace("Executing saveDomain [{}]", domain); try { + domainDataValidator.validate(domain, Domain::getTenantId); Domain savedDomain = domainDao.save(tenantId, domain); eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entityId(savedDomain.getId()).entity(savedDomain).build()); return savedDomain; } catch (Exception e) { checkConstraintViolation(e, - Map.of("domain_unq_key", "Domain with such name and scheme already exists!")); + Map.of("domain_name_key", "Domain with such name and scheme already exists!")); throw e; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java index ebeb39ed91..b845cee74d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java @@ -82,8 +82,8 @@ public class DefaultNotificationRequestService implements NotificationRequestSer } @Override - public List findNotificationRequestsByRuleIdAndOriginatorEntityId(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId) { - return notificationRequestDao.findByRuleIdAndOriginatorEntityId(tenantId, ruleId, originatorEntityId); + public List findNotificationRequestsByRuleIdAndOriginatorEntityIdAndStatus(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId, NotificationRequestStatus status) { + return notificationRequestDao.findByRuleIdAndOriginatorEntityIdAndStatus(tenantId, ruleId, originatorEntityId, status); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java index fb362f235b..c51091300a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java @@ -40,7 +40,7 @@ public interface NotificationRequestDao extends Dao { List findIdsByRuleId(TenantId tenantId, NotificationRequestStatus requestStatus, NotificationRuleId ruleId); - List findByRuleIdAndOriginatorEntityId(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId); + List findByRuleIdAndOriginatorEntityIdAndStatus(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId, NotificationRequestStatus status); PageData findAllByStatus(NotificationRequestStatus status, PageLink pageLink); diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java index 1cf719d2dc..172b91a32c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -27,6 +27,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.cache.CaffeineTbTransactionalCache; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; @@ -526,9 +527,16 @@ public class BaseImageService extends BaseResourceService implements ImageServic } @Override - public void inlineImage(HasImage entity) { + public T inlineImage(T entity) { log.trace("Executing inlineImage [{}] [{}] [{}]", entity.getTenantId(), entity.getClass().getSimpleName(), entity.getName()); + if (StringUtils.isEmpty(entity.getImage())) { + return entity; + } + if (cache instanceof CaffeineTbTransactionalCache) { + entity = JacksonUtil.clone(entity); // cloning the entity to avoid updating the cached one + } entity.setImage(inlineImage(entity.getTenantId(), "image", entity.getImage(), true)); + return entity; } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index aff7af8165..1321fa5081 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -43,7 +43,10 @@ public abstract class DataValidator> { Pattern.compile("^[A-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$", Pattern.CASE_INSENSITIVE); private static final Pattern QUEUE_PATTERN = Pattern.compile("^[a-zA-Z0-9_.\\-]+$"); - + private static final String DOMAIN_REGEX = "^(((?!-))(xn--|_)?[a-z0-9-]{0,61}[a-z0-9]{1,1}\\.)*(xn--)?([a-z0-9][a-z0-9\\-]{0,60}|[a-z0-9-]{1,30}\\.[a-z]{2,})$"; + private static final Pattern DOMAIN_PATTERN = Pattern.compile(DOMAIN_REGEX); + private static final String LOCALHOST_REGEX = "^localhost(:\\d{1,5})?$"; + private static final Pattern LOCALHOST_PATTERN = Pattern.compile(LOCALHOST_REGEX); private static final String NAME = "name"; private static final String TOPIC = "topic"; @@ -171,4 +174,14 @@ public abstract class DataValidator> { } } + public static boolean isValidDomain(String domainName) { + if (domainName == null) { + return false; + } + if (LOCALHOST_PATTERN.matcher(domainName).matches()) { + return true; + } + return DOMAIN_PATTERN.matcher(domainName).matches(); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java new file mode 100644 index 0000000000..9a9cf99d0e --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.domain.Domain; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.exception.IncorrectParameterException; + +@Component +public class DomainDataValidator extends AbstractHasOtaPackageValidator { + + @Override + protected void validateDataImpl(TenantId tenantId, Domain domain) { + if (!isValidDomain(domain.getName())) { + throw new IncorrectParameterException("Domain name " + domain.getName() + " is invalid"); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java index 3d2d80e15e..6e5ae702dc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java @@ -73,8 +73,8 @@ public class JpaNotificationRequestDao extends JpaAbstractDao findByRuleIdAndOriginatorEntityId(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId) { - return DaoUtil.convertDataList(notificationRequestRepository.findAllByRuleIdAndOriginatorEntityIdAndOriginatorEntityType(ruleId.getId(), originatorEntityId.getId(), originatorEntityId.getEntityType())); + public List findByRuleIdAndOriginatorEntityIdAndStatus(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId, NotificationRequestStatus status) { + return DaoUtil.convertDataList(notificationRequestRepository.findAllByRuleIdAndOriginatorEntityIdAndOriginatorEntityTypeAndStatus(ruleId.getId(), originatorEntityId.getId(), originatorEntityId.getEntityType(), status)); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java index d6b346e3df..558bab94ff 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java @@ -54,7 +54,7 @@ public interface NotificationRequestRepository extends JpaRepository findAllIdsByStatusAndRuleId(@Param("status") NotificationRequestStatus status, @Param("ruleId") UUID ruleId); - List findAllByRuleIdAndOriginatorEntityIdAndOriginatorEntityType(UUID ruleId, UUID originatorEntityId, EntityType originatorEntityType); + List findAllByRuleIdAndOriginatorEntityIdAndOriginatorEntityTypeAndStatus(UUID ruleId, UUID originatorEntityId, EntityType originatorEntityType, NotificationRequestStatus status); Page findAllByStatus(NotificationRequestStatus status, Pageable pageable); diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java index f9c4218d64..756b73d88b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java @@ -254,11 +254,11 @@ public class BaseTimeseriesService implements TimeseriesService { } @Override - public ListenableFuture> removeAllLatest(TenantId tenantId, EntityId entityId) { + public ListenableFuture> removeAllLatest(TenantId tenantId, EntityId entityId) { validate(entityId); return Futures.transformAsync(this.findAllLatest(tenantId, entityId), latest -> { if (latest != null && !latest.isEmpty()) { - Collection keys = latest.stream().map(TsKvEntry::getKey).collect(Collectors.toList()); + List keys = latest.stream().map(TsKvEntry::getKey).collect(Collectors.toList()); return Futures.transform(this.removeLatest(tenantId, entityId, keys), res -> keys, MoreExecutors.directExecutor()); } else { return Futures.immediateFuture(Collections.emptyList()); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index f43ea4a5f6..916a5487f8 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -896,7 +896,7 @@ CREATE TABLE IF NOT EXISTS queue_stats ( ); CREATE TABLE IF NOT EXISTS qr_code_settings ( - id uuid NOT NULL CONSTRAINT mobile_app_settings_pkey PRIMARY KEY, + id uuid NOT NULL CONSTRAINT qr_code_settings_pkey PRIMARY KEY, created_time bigint NOT NULL, tenant_id uuid NOT NULL, use_default_app boolean, diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DomainServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DomainServiceTest.java index 734e30137a..c50991ea87 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DomainServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DomainServiceTest.java @@ -90,7 +90,7 @@ public class DomainServiceTest extends AbstractServiceTest { public void testGetTenantDomains() { List domains = new ArrayList<>(); for (int i = 0; i < 5; i++) { - Domain oAuth2Client = constructDomain(TenantId.SYS_TENANT_ID, StringUtils.randomAlphabetic(5), true, false); + Domain oAuth2Client = constructDomain(TenantId.SYS_TENANT_ID, StringUtils.randomAlphabetic(5).toLowerCase(), true, false); Domain savedOauth2Client = domainService.saveDomain(SYSTEM_TENANT_ID, oAuth2Client); domains.add(savedOauth2Client); } diff --git a/docker/docker-upgrade-tb.sh b/docker/docker-upgrade-tb.sh index 41f50c7019..07aa7ef05f 100755 --- a/docker/docker-upgrade-tb.sh +++ b/docker/docker-upgrade-tb.sh @@ -28,13 +28,7 @@ case $i in esac done -if [[ -z "${FROM_VERSION// }" ]]; then - echo "--fromVersion parameter is invalid or unspecified!" - echo "Usage: docker-upgrade-tb.sh --fromVersion={VERSION}" - exit 1 -else - fromVersion="${FROM_VERSION// }" -fi +fromVersion="${FROM_VERSION// }" set -e diff --git a/msa/tb-node/docker/start-tb-node.sh b/msa/tb-node/docker/start-tb-node.sh index c164954a82..b90553e772 100755 --- a/msa/tb-node/docker/start-tb-node.sh +++ b/msa/tb-node/docker/start-tb-node.sh @@ -47,12 +47,8 @@ elif [ "$UPGRADE_TB" == "true" ]; then echo "Starting ThingsBoard upgrade ..." - if [[ -z "${FROM_VERSION// }" ]]; then - echo "FROM_VERSION variable is invalid or unspecified!" - exit 1 - else - fromVersion="${FROM_VERSION// }" - fi + + fromVersion="${FROM_VERSION// }" exec java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \ -Dspring.jpa.hibernate.ddl-auto=none \ diff --git a/msa/tb/docker/upgrade-tb.sh b/msa/tb/docker/upgrade-tb.sh index 3c8e4f194d..3e8f7d804a 100644 --- a/msa/tb/docker/upgrade-tb.sh +++ b/msa/tb/docker/upgrade-tb.sh @@ -20,15 +20,23 @@ start-db.sh CONF_FOLDER="${pkg.installFolder}/conf" jarfile=${pkg.installFolder}/bin/${pkg.name}.jar configfile=${pkg.name}.conf +upgradeversion=${DATA_FOLDER}/.upgradeversion source "${CONF_FOLDER}/${configfile}" +FROM_VERSION=`cat ${upgradeversion}` + echo "Starting ThingsBoard upgrade ..." +fromVersion="${FROM_VERSION// }" + java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \ -Dspring.jpa.hibernate.ddl-auto=none \ -Dinstall.upgrade=true \ + -Dinstall.upgrade.from_version=${fromVersion} \ -Dlogging.config=/usr/share/thingsboard/bin/install/logback.xml \ org.springframework.boot.loader.launch.PropertiesLauncher +echo "${pkg.upgradeVersion}" > ${upgradeversion} + stop-db.sh \ No newline at end of file diff --git a/packaging/java/scripts/install/upgrade.sh b/packaging/java/scripts/install/upgrade.sh index 2b87b26aa6..07164ef2c8 100755 --- a/packaging/java/scripts/install/upgrade.sh +++ b/packaging/java/scripts/install/upgrade.sh @@ -28,13 +28,7 @@ case $i in esac done -if [[ -z "${FROM_VERSION// }" ]]; then - echo "--fromVersion parameter is invalid or unspecified!" - echo "Usage: upgrade.sh --fromVersion={VERSION}" - exit 1 -else - fromVersion="${FROM_VERSION// }" -fi +fromVersion="${FROM_VERSION// }" CONF_FOLDER=${pkg.installFolder}/conf configfile=${pkg.name}.conf diff --git a/packaging/java/scripts/install/upgrade_dev_db.sh b/packaging/java/scripts/install/upgrade_dev_db.sh index d0c42eaaa6..010f6d3655 100755 --- a/packaging/java/scripts/install/upgrade_dev_db.sh +++ b/packaging/java/scripts/install/upgrade_dev_db.sh @@ -28,13 +28,7 @@ case $i in esac done -if [[ -z "${FROM_VERSION// }" ]]; then - echo "--fromVersion parameter is invalid or unspecified!" - echo "Usage: upgrade_dev_db.sh --fromVersion={VERSION}" - exit 1 -else - fromVersion="${FROM_VERSION// }" -fi +fromVersion="${FROM_VERSION// }" BASE=${project.basedir}/target CONF_FOLDER=${BASE}/conf diff --git a/packaging/java/scripts/windows/upgrade.bat b/packaging/java/scripts/windows/upgrade.bat index b86121b8f1..115eea5244 100644 --- a/packaging/java/scripts/windows/upgrade.bat +++ b/packaging/java/scripts/windows/upgrade.bat @@ -15,12 +15,6 @@ IF NOT "%1"=="" ( GOTO :loop ) -if not defined fromVersion ( - echo "--fromVersion parameter is invalid or unspecified!" - echo "Usage: upgrade.bat --fromVersion {VERSION}" - exit /b 1 -) - SET LOADER_PATH=%BASE%\conf,%BASE%\extensions SET SQL_DATA_FOLDER=%BASE%\data\sql SET jarfile=%BASE%\lib\${pkg.name}.jar diff --git a/pom.xml b/pom.xml index f18740b658..dec169a9b6 100755 --- a/pom.xml +++ b/pom.xml @@ -869,6 +869,29 @@ + + io.github.git-commit-id + git-commit-id-maven-plugin + 5.0.0 + + + get-the-git-infos + + revision + + initialize + + + + true + ${project.build.outputDirectory}/git.properties + + + + + full + + diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesDeleteRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesDeleteRequest.java new file mode 100644 index 0000000000..118c62e78c --- /dev/null +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesDeleteRequest.java @@ -0,0 +1,117 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.api; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.SettableFuture; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; +import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.List; + +@Getter +@ToString +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class AttributesDeleteRequest { + + private final TenantId tenantId; + private final EntityId entityId; + private final AttributeScope scope; + private final List keys; + private final boolean notifyDevice; + private final FutureCallback callback; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private TenantId tenantId; + private EntityId entityId; + private AttributeScope scope; + private List keys; + private boolean notifyDevice; + private FutureCallback callback; + + Builder() {} + + public Builder tenantId(TenantId tenantId) { + this.tenantId = tenantId; + return this; + } + + public Builder entityId(EntityId entityId) { + this.entityId = entityId; + return this; + } + + public Builder scope(AttributeScope scope) { + this.scope = scope; + return this; + } + + @Deprecated + public Builder scope(String scope) { + try { + this.scope = AttributeScope.valueOf(scope); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid attribute scope '" + scope + "'"); + } + return this; + } + + public Builder keys(List keys) { + this.keys = keys; + return this; + } + + public Builder notifyDevice(boolean notifyDevice) { + this.notifyDevice = notifyDevice; + return this; + } + + public Builder callback(FutureCallback callback) { + this.callback = callback; + return this; + } + + public Builder future(SettableFuture future) { + return callback(new FutureCallback<>() { + @Override + public void onSuccess(Void result) { + future.set(result); + } + + @Override + public void onFailure(Throwable t) { + future.setException(t); + } + }); + } + + public AttributesDeleteRequest build() { + return new AttributesDeleteRequest(tenantId, entityId, scope, keys, notifyDevice, callback); + } + + } + +} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesSaveRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesSaveRequest.java new file mode 100644 index 0000000000..22fa8de6de --- /dev/null +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesSaveRequest.java @@ -0,0 +1,128 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.api; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.SettableFuture; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; +import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.KvEntry; + +import java.util.List; + +@Getter +@ToString +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class AttributesSaveRequest { + + private final TenantId tenantId; + private final EntityId entityId; + private final AttributeScope scope; + private final List entries; + private final boolean notifyDevice; + private final FutureCallback callback; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private TenantId tenantId; + private EntityId entityId; + private AttributeScope scope; + private List entries; + private boolean notifyDevice = true; + private FutureCallback callback; + + Builder() {} + + public Builder tenantId(TenantId tenantId) { + this.tenantId = tenantId; + return this; + } + + public Builder entityId(EntityId entityId) { + this.entityId = entityId; + return this; + } + + public Builder scope(AttributeScope scope) { + this.scope = scope; + return this; + } + + @Deprecated + public Builder scope(String scope) { + try { + this.scope = AttributeScope.valueOf(scope); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid attribute scope '" + scope + "'"); + } + return this; + } + + public Builder entries(List entries) { + this.entries = entries; + return this; + } + + public Builder entry(AttributeKvEntry entry) { + return entries(List.of(entry)); + } + + public Builder entry(KvEntry kvEntry) { + return entry(new BaseAttributeKvEntry(kvEntry, System.currentTimeMillis())); + } + + public Builder notifyDevice(boolean notifyDevice) { + this.notifyDevice = notifyDevice; + return this; + } + + public Builder callback(FutureCallback callback) { + this.callback = callback; + return this; + } + + public Builder future(SettableFuture future) { + return callback(new FutureCallback<>() { + @Override + public void onSuccess(Void result) { + future.set(result); + } + + @Override + public void onFailure(Throwable t) { + future.setException(t); + } + }); + } + + public AttributesSaveRequest build() { + return new AttributesSaveRequest(tenantId, entityId, scope, entries, notifyDevice, callback); + } + + } + +} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java index 0c2de91b2f..17696b4bb1 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java @@ -15,97 +15,17 @@ */ package org.thingsboard.rule.engine.api; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.server.common.data.AttributeScope; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; -import org.thingsboard.server.common.data.kv.TsKvEntry; - -import java.util.Collection; -import java.util.List; - /** * Created by ashvayka on 02.04.18. */ public interface RuleEngineTelemetryService { - ListenableFuture saveAndNotify(TenantId tenantId, EntityId entityId, TsKvEntry ts); - - void saveAndNotify(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback); - - void saveAndNotify(TenantId tenantId, CustomerId id, EntityId entityId, List ts, long ttl, FutureCallback callback); - - void saveWithoutLatestAndNotify(TenantId tenantId, CustomerId id, EntityId entityId, List ts, long ttl, FutureCallback callback); - - @Deprecated(since = "3.7.0") - void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List attributes, FutureCallback callback); - - void saveAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes, FutureCallback callback); - - @Deprecated(since = "3.7.0") - void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List attributes, boolean notifyDevice, FutureCallback callback); - - void saveAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes, boolean notifyDevice, FutureCallback callback); - - void saveLatestAndNotify(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback); - - @Deprecated(since = "3.7.0") - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, long value); - - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, long value); - - @Deprecated(since = "3.7.0") - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, String value); - - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, String value); - - @Deprecated(since = "3.7.0") - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, double value); - - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, double value); - - @Deprecated(since = "3.7.0") - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, boolean value); - - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, boolean value); - - @Deprecated(since = "3.7.0") - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, long value, FutureCallback callback); - - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, long value, FutureCallback callback); - - @Deprecated(since = "3.7.0") - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, String value, FutureCallback callback); - - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, String value, FutureCallback callback); - - @Deprecated(since = "3.7.0") - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, double value, FutureCallback callback); - - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, double value, FutureCallback callback); - - @Deprecated(since = "3.7.0") - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, boolean value, FutureCallback callback); - - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, boolean value, FutureCallback callback); - - @Deprecated(since = "3.7.0") - void deleteAndNotify(TenantId tenantId, EntityId entityId, String scope, List keys, FutureCallback callback); - - void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, FutureCallback callback); - - @Deprecated(since = "3.7.0") - void deleteAndNotify(TenantId tenantId, EntityId entityId, String scope, List keys, boolean notifyDevice, FutureCallback callback); + void saveTimeseries(TimeseriesSaveRequest request); - void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, boolean notifyDevice, FutureCallback callback); + void saveAttributes(AttributesSaveRequest request); - void deleteLatest(TenantId tenantId, EntityId entityId, List keys, FutureCallback callback); + void deleteTimeseries(TimeseriesDeleteRequest request); - void deleteAllLatest(TenantId tenantId, EntityId entityId, FutureCallback> callback); + void deleteAttributes(AttributesDeleteRequest request); - void deleteTimeseriesAndNotify(TenantId tenantId, EntityId entityId, List keys, List deleteTsKvQueries, FutureCallback callback); } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index b517bb0051..fa8e51b51f 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -192,9 +192,6 @@ public interface TbContext { void ack(TbMsg tbMsg); - @Deprecated(since = "3.6.0", forRemoval = true) - TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data); - /** * Creates a new TbMsg instance with the specified parameters. * diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesDeleteRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesDeleteRequest.java new file mode 100644 index 0000000000..b124806fff --- /dev/null +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesDeleteRequest.java @@ -0,0 +1,85 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.api; + +import com.google.common.util.concurrent.FutureCallback; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; + +import java.util.List; + +@Getter +@ToString +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class TimeseriesDeleteRequest { + + private final TenantId tenantId; + private final EntityId entityId; + private final List keys; + private final List deleteHistoryQueries; + private final FutureCallback> callback; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private TenantId tenantId; + private EntityId entityId; + private List keys; + private List deleteHistoryQueries; + private FutureCallback> callback; + + Builder() {} + + public Builder tenantId(TenantId tenantId) { + this.tenantId = tenantId; + return this; + } + + public Builder entityId(EntityId entityId) { + this.entityId = entityId; + return this; + } + + public Builder keys(List keys) { + this.keys = keys; + return this; + } + + public Builder deleteHistoryQueries(List deleteHistoryQueries) { + this.deleteHistoryQueries = deleteHistoryQueries; + return this; + } + + public Builder callback(FutureCallback> callback) { + this.callback = callback; + return this; + } + + public TimeseriesDeleteRequest build() { + return new TimeseriesDeleteRequest(tenantId, entityId, keys, deleteHistoryQueries, callback); + } + + } + +} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java new file mode 100644 index 0000000000..2b5881212d --- /dev/null +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java @@ -0,0 +1,131 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.api; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.SettableFuture; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; + +import java.util.List; + +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class TimeseriesSaveRequest { + + private final TenantId tenantId; + private final CustomerId customerId; + private final EntityId entityId; + private final List entries; + private final long ttl; + private final boolean saveLatest; + private final boolean onlyLatest; + private final FutureCallback callback; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private TenantId tenantId; + private CustomerId customerId; + private EntityId entityId; + private List entries; + private long ttl; + private FutureCallback callback; + private boolean saveLatest = true; + private boolean onlyLatest; + + Builder() {} + + public Builder tenantId(TenantId tenantId) { + this.tenantId = tenantId; + return this; + } + + public Builder customerId(CustomerId customerId) { + this.customerId = customerId; + return this; + } + + public Builder entityId(EntityId entityId) { + this.entityId = entityId; + return this; + } + + public Builder entries(List entries) { + this.entries = entries; + return this; + } + + public Builder entry(TsKvEntry entry) { + return entries(List.of(entry)); + } + + public Builder entry(KvEntry kvEntry) { + return entry(new BasicTsKvEntry(System.currentTimeMillis(), kvEntry)); + } + + public Builder ttl(long ttl) { + this.ttl = ttl; + return this; + } + + public Builder saveLatest(boolean saveLatest) { + this.saveLatest = saveLatest; + return this; + } + + public Builder onlyLatest(boolean onlyLatest) { + this.onlyLatest = onlyLatest; + this.saveLatest = true; + return this; + } + + public Builder callback(FutureCallback callback) { + this.callback = callback; + return this; + } + + public Builder future(SettableFuture future) { + return callback(new FutureCallback<>() { + @Override + public void onSuccess(Void result) { + future.set(result); + } + + @Override + public void onFailure(Throwable t) { + future.setException(t); + } + }); + } + + public TimeseriesSaveRequest build() { + return new TimeseriesSaveRequest(tenantId, customerId, entityId, entries, ttl, saveLatest, onlyLatest, callback); + } + + } + +} diff --git a/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java b/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java index 7da2efdf3f..0aa3077add 100644 --- a/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java +++ b/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java @@ -44,7 +44,12 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.put("data_key", "data_value"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .copyMetaData(md) + .data(JacksonUtil.toString(node)) + .build(); String result = TbNodeUtils.processPattern(pattern, msg); Assertions.assertEquals("ABC metadata_value data_value", result); } @@ -58,7 +63,12 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.put("key", "data_value"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .copyMetaData(md) + .data(JacksonUtil.toString(node)) + .build(); String result = TbNodeUtils.processPattern(pattern, msg); Assertions.assertEquals(pattern, result); } @@ -72,7 +82,12 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.put("key", "data_value"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .copyMetaData(md) + .data(JacksonUtil.toString(node)) + .build(); String result = TbNodeUtils.processPattern(pattern, msg); Assertions.assertEquals("ABC metadata_value data_value", result); } @@ -93,7 +108,12 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.set("key1", key1Node); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .copyMetaData(md) + .data(JacksonUtil.toString(node)) + .build(); String result = TbNodeUtils.processPattern(pattern, msg); Assertions.assertEquals("ABC metadata_value value3", result); } @@ -114,7 +134,12 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.set("key1", key1Node); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .copyMetaData(md) + .data(JacksonUtil.toString(node)) + .build(); String result = TbNodeUtils.processPattern(pattern, msg); Assertions.assertEquals("ABC metadata_value $[key1.key2[0].key3]", result); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java index 855727494e..5431913894 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java @@ -79,7 +79,9 @@ public abstract class TbAbstractAlarmNode filteredAttributes = attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr, entityView)).collect(Collectors.toList()); if (!filteredAttributes.isEmpty()) { - ctx.getTelemetryService().deleteAndNotify(ctx.getTenantId(), entityView.getId(), scope, filteredAttributes, - getFutureCallback(ctx, msg, entityView)); + ctx.getTelemetryService().deleteAttributes(AttributesDeleteRequest.builder() + .tenantId(ctx.getTenantId()) + .entityId(entityView.getId()) + .scope(scope) + .keys(filteredAttributes) + .callback(getFutureCallback(ctx, msg, entityView)) + .build()); } } else { Set attributes = JsonConverter.convertToAttributes(JsonParser.parseString(msg.getData())); List filteredAttributes = attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr.getKey(), entityView)).collect(Collectors.toList()); - ctx.getTelemetryService().saveAndNotify(ctx.getTenantId(), entityView.getId(), scope, filteredAttributes, - getFutureCallback(ctx, msg, entityView)); + ctx.getTelemetryService().saveAttributes(AttributesSaveRequest.builder() + .tenantId(ctx.getTenantId()) + .entityId(entityView.getId()) + .scope(scope) + .entries(filteredAttributes) + .callback(getFutureCallback(ctx, msg, entityView)) + .build()); } } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java index 333aaaf7df..edbd9c7ca4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java @@ -74,7 +74,14 @@ public class TbMsgCountNode implements TbNode { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("delta", Long.toString(System.currentTimeMillis() - lastScheduledTs + delay)); - TbMsg tbMsg = TbMsg.newMsg(msg.getQueueName(), TbMsgType.POST_TELEMETRY_REQUEST, ctx.getTenantId(), msg.getCustomerId(), metaData, gson.toJson(telemetryJson)); + TbMsg tbMsg = TbMsg.newMsg() + .queueName(msg.getQueueName()) + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(ctx.getTenantId()) + .customerId(msg.getCustomerId()) + .copyMetaData(metaData) + .data(gson.toJson(telemetryJson)) + .build(); ctx.enqueueForTellNext(tbMsg, TbNodeConnectionType.SUCCESS); scheduleTickMsg(ctx, tbMsg); } else { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNode.java index 1dbc81bfa1..bb9f5416db 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNode.java @@ -132,14 +132,19 @@ public class TbAwsLambdaNode extends TbAbstractExternalNode { TbMsgMetaData metaData = originalMsg.getMetaData().copy(); metaData.putValue("requestId", invokeResult.getSdkResponseMetadata().getRequestId()); String data = getPayload(invokeResult); - return TbMsg.transformMsg(originalMsg, metaData, data); + return originalMsg.transform() + .metaData(metaData) + .data(data) + .build(); } private TbMsg processException(TbMsg origMsg, InvokeResult invokeResult, Throwable t) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue("error", t.getClass() + ": " + t.getMessage()); metaData.putValue("requestId", invokeResult.getSdkResponseMetadata().getRequestId()); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java index 6516a5e4b3..2c732150e1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java @@ -103,13 +103,17 @@ public class TbSnsNode extends TbAbstractExternalNode { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(MESSAGE_ID, result.getMessageId()); metaData.putValue(REQUEST_ID, result.getSdkResponseMetadata().getRequestId()); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } private TbMsg processException(TbMsg origMsg, Throwable t) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(ERROR, t.getClass() + ": " + t.getMessage()); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java index 4dbc188cdd..e61cd537c4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java @@ -134,13 +134,17 @@ public class TbSqsNode extends TbAbstractExternalNode { if (!StringUtils.isEmpty(result.getSequenceNumber())) { metaData.putValue(SEQUENCE_NUMBER, result.getSequenceNumber()); } - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } private TbMsg processException(TbMsg origMsg, Throwable t) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(ERROR, t.getClass() + ": " + t.getMessage()); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java index 1afba3581f..cf4b5b4ce1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java @@ -154,12 +154,13 @@ public class TbMsgDeduplicationNode implements TbNode { iterator.remove(); } } - deduplicationResults.add(TbMsg.newMsg( - queueName, - config.getOutMsgType(), - deduplicationId, - getMetadata(), - getMergedData(pack))); + deduplicationResults.add(TbMsg.newMsg() + .queueName(queueName) + .type(config.getOutMsgType()) + .originator(deduplicationId) + .copyMetaData(getMetadata()) + .data(getMergedData(pack)) + .build()); } else { TbMsg resultMsg = null; boolean searchMin = DeduplicationStrategy.FIRST.equals(config.getStrategy()); @@ -176,13 +177,15 @@ public class TbMsgDeduplicationNode implements TbNode { } } if (resultMsg != null) { - deduplicationResults.add(TbMsg.newMsg( - queueName != null ? queueName : resultMsg.getQueueName(), - resultMsg.getType(), - resultMsg.getOriginator(), - resultMsg.getCustomerId(), - resultMsg.getMetaData(), - resultMsg.getData())); + String queueName1 = queueName != null ? queueName : resultMsg.getQueueName(); + deduplicationResults.add(TbMsg.newMsg() + .queueName(queueName1) + .type(resultMsg.getType()) + .originator(resultMsg.getOriginator()) + .customerId(resultMsg.getCustomerId()) + .copyMetaData(resultMsg.getMetaData()) + .data(resultMsg.getData()) + .build()); } } packBoundsOpt = findValidPack(msgList, deduplicationTimeoutMs); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java index b60a46ee0a..a8a2c4e9b5 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java @@ -65,14 +65,14 @@ public class TbMsgDelayNode implements TbNode { TbMsg pendingMsg = pendingMsgs.remove(UUID.fromString(msg.getData())); if (pendingMsg != null) { ctx.enqueueForTellNext( - TbMsg.newMsg( - pendingMsg.getQueueName(), - pendingMsg.getType(), - pendingMsg.getOriginator(), - pendingMsg.getCustomerId(), - pendingMsg.getMetaData(), - pendingMsg.getData() - ), + TbMsg.newMsg() + .queueName(pendingMsg.getQueueName()) + .type(pendingMsg.getType()) + .originator(pendingMsg.getOriginator()) + .customerId(pendingMsg.getCustomerId()) + .copyMetaData(pendingMsg.getMetaData()) + .data(pendingMsg.getData()) + .build(), TbNodeConnectionType.SUCCESS ); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java index 537318fa75..d6d3bb3c11 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java @@ -120,13 +120,17 @@ public class TbPubSubNode extends TbAbstractExternalNode { private TbMsg processPublishResult(TbMsg origMsg, String messageId) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(MESSAGE_ID, messageId); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } private TbMsg processException(TbMsg origMsg, Throwable t) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(ERROR, t.getClass() + ": " + t.getMessage()); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } Publisher initPubSubClient(TbContext ctx) throws IOException { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java index 89b6e1c1d9..865eac0aac 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java @@ -188,13 +188,17 @@ public class TbKafkaNode extends TbAbstractExternalNode { metaData.putValue(OFFSET, String.valueOf(recordMetadata.offset())); metaData.putValue(PARTITION, String.valueOf(recordMetadata.partition())); metaData.putValue(TOPIC, recordMetadata.topic()); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } private TbMsg processException(TbMsg origMsg, Exception e) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(ERROR, e.getClass() + ": " + e.getMessage()); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java index 4e64ad854e..d72a23708e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java @@ -19,16 +19,19 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.SettableFuture; import lombok.extern.slf4j.Slf4j; import net.objecthunter.exp4j.Expression; import net.objecthunter.exp4j.ExpressionBuilder; import org.springframework.util.ConcurrentReferenceHashMap; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.util.SemaphoreWithTbMsgQueue; import org.thingsboard.server.common.data.AttributeScope; @@ -37,6 +40,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -139,22 +143,36 @@ public class TbMathNode implements TbNode { } private ListenableFuture saveTimeSeries(TbContext ctx, TbMsg msg, double result, TbMathResult mathResultDef) { - - return ctx.getTelemetryService().saveAndNotify(ctx.getTenantId(), msg.getOriginator(), - new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry(mathResultDef.getKey(), result))); + final BasicTsKvEntry basicTsKvEntry = new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry(mathResultDef.getKey(), result)); + SettableFuture future = SettableFuture.create(); + ctx.getTelemetryService().saveTimeseries(TimeseriesSaveRequest.builder() + .tenantId(ctx.getTenantId()) + .entityId(msg.getOriginator()) + .entry(basicTsKvEntry) + .future(future) + .build()); + return future; } private ListenableFuture saveAttribute(TbContext ctx, TbMsg msg, double result, TbMathResult mathResultDef) { AttributeScope attributeScope = getAttributeScope(mathResultDef.getAttributeScope()); + KvEntry kvEntry; if (isIntegerResult(mathResultDef, config.getOperation())) { var value = toIntValue(result); - return ctx.getTelemetryService().saveAttrAndNotify( - ctx.getTenantId(), msg.getOriginator(), attributeScope, mathResultDef.getKey(), value); + kvEntry = new LongDataEntry(mathResultDef.getKey(), value); } else { var value = toDoubleValue(mathResultDef, result); - return ctx.getTelemetryService().saveAttrAndNotify( - ctx.getTenantId(), msg.getOriginator(), attributeScope, mathResultDef.getKey(), value); + kvEntry = new DoubleDataEntry(mathResultDef.getKey(), value); } + SettableFuture future = SettableFuture.create(); + ctx.getTelemetryService().saveAttributes(AttributesSaveRequest.builder() + .tenantId(ctx.getTenantId()) + .entityId(msg.getOriginator()) + .scope(attributeScope) + .entry(kvEntry) + .future(future) + .build()); + return future; } private boolean isIntegerResult(TbMathResult mathResultDef, TbRuleNodeMathFunctionType function) { @@ -202,7 +220,9 @@ public class TbMathNode implements TbNode { } else { body.put(mathResultKey, toDoubleValue(mathResultDef, result)); } - return TbMsg.transformMsgData(msg, JacksonUtil.toString(body)); + return msg.transform() + .data(JacksonUtil.toString(body)) + .build(); } private TbMsg addToMeta(TbMsg msg, TbMathResult mathResultDef, String mathResultKey, double result) { @@ -212,7 +232,9 @@ public class TbMathNode implements TbNode { } else { md.putValue(mathResultKey, Double.toString(toDoubleValue(mathResultDef, result))); } - return TbMsg.transformMsgMetadata(msg, md); + return msg.transform() + .metaData(md) + .build(); } private double calculateResult(List args) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java index 329fa46c3b..75d0e774a2 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java @@ -175,7 +175,9 @@ public class CalculateDeltaNode implements TbNode { long period = previousData != null ? msg.getMetaDataTs() - previousData.ts : 0; json.put(config.getPeriodValueKey(), period); } - return TbMsg.transformMsgData(msg, JacksonUtil.toString(json)); + return msg.transform() + .data(JacksonUtil.toString(json)) + .build(); }, MoreExecutors.directExecutor()); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractNodeWithFetchTo.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractNodeWithFetchTo.java index 951f1635d8..0e4fc27c02 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractNodeWithFetchTo.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractNodeWithFetchTo.java @@ -82,9 +82,13 @@ public abstract class TbAbstractNodeWithFetchTo> list = ctx.getTimeseriesService().findAll(ctx.getTenantId(), msg.getOriginator(), buildQueries(interval, keys)); DonAsynchron.withCallback(list, data -> { var metaData = updateMetadata(data, msg, keys); - ctx.tellSuccess(TbMsg.transformMsgMetadata(msg, metaData)); + ctx.tellSuccess(msg.transform() + .metaData(metaData) + .build()); }, error -> ctx.tellFailure(msg, error), ctx.getDbCallbackExecutor()); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java index 912496f39e..56bfe038a8 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java @@ -103,7 +103,9 @@ public class TbMqttNode extends TbAbstractExternalNode { private TbMsg processException(TbMsg origMsg, Throwable e) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(ERROR, e.getClass() + ": " + e.getMessage()); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java index 34c5d6de28..8ae3da7bad 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java @@ -82,7 +82,9 @@ public class TbNotificationNode extends TbAbstractExternalNode { public void onSuccess(NotificationRequestStats stats) { TbMsgMetaData metaData = tbMsg.getMetaData().copy(); metaData.putValue("notificationRequestResult", JacksonUtil.toString(stats)); - tellSuccess(ctx, TbMsg.transformMsgMetadata(tbMsg, metaData)); + tellSuccess(ctx, tbMsg.transform() + .metaData(metaData) + .build()); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java index c7b2d010a8..c57a9a1433 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java @@ -29,6 +29,7 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.msg.TbMsgType; @@ -176,7 +177,14 @@ public class TbDeviceProfileNode implements TbNode { } protected void scheduleAlarmHarvesting(TbContext ctx, TbMsg msg) { - TbMsg periodicCheck = TbMsg.newMsg(TbMsgType.DEVICE_PROFILE_PERIODIC_SELF_MSG, ctx.getTenantId(), msg != null ? msg.getCustomerId() : null, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + CustomerId customerId = msg != null ? msg.getCustomerId() : null; + TbMsg periodicCheck = TbMsg.newMsg() + .type(TbMsgType.DEVICE_PROFILE_PERIODIC_SELF_MSG) + .originator(ctx.getTenantId()) + .customerId(customerId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); ctx.tellSelf(periodicCheck, TimeUnit.MINUTES.toMillis(1)); } @@ -201,7 +209,12 @@ public class TbDeviceProfileNode implements TbNode { } protected void onProfileUpdate(DeviceProfile profile) { - ctx.tellSelf(TbMsg.newMsg(TbMsgType.DEVICE_PROFILE_UPDATE_SELF_MSG, ctx.getTenantId(), TbMsgMetaData.EMPTY, profile.getId().getId().toString()), 0L); + ctx.tellSelf(TbMsg.newMsg() + .type(TbMsgType.DEVICE_PROFILE_UPDATE_SELF_MSG) + .originator(ctx.getTenantId()) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(profile.getId().getId().toString()) + .build(), 0L); } private void onDeviceUpdate(DeviceId deviceId, DeviceProfile deviceProfile) { @@ -210,7 +223,12 @@ public class TbDeviceProfileNode implements TbNode { if (deviceProfile != null) { msgData.put("deviceProfileId", deviceProfile.getId().getId().toString()); } - ctx.tellSelf(TbMsg.newMsg(TbMsgType.DEVICE_UPDATE_SELF_MSG, ctx.getTenantId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(msgData)), 0L); + ctx.tellSelf(TbMsg.newMsg() + .type(TbMsgType.DEVICE_UPDATE_SELF_MSG) + .originator(ctx.getTenantId()) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.toString(msgData)) + .build(), 0L); } protected void invalidateDeviceProfileCache(DeviceId deviceId, String deviceJson) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java index b15e0e8e3d..8b724ae86e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java @@ -127,7 +127,9 @@ public class TbRabbitMqNode extends TbAbstractExternalNode { private TbMsg processException(TbMsg origMsg, Throwable t) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(ERROR, t.getClass() + ": " + t.getMessage()); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java index 566430bd8d..ed1ed4fa83 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java @@ -323,7 +323,9 @@ public class TbHttpClient { metaData.putValue(STATUS_REASON, httpStatus.getReasonPhrase()); metaData.putValue(ERROR_BODY, response.getBody()); headersToMetaData(response.getHeaders(), metaData::putValue); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } private TbMsg processException(TbMsg origMsg, Throwable e) { @@ -334,7 +336,9 @@ public class TbHttpClient { metaData.putValue(STATUS_CODE, restClientResponseException.getStatusCode().value() + ""); metaData.putValue(ERROR_BODY, restClientResponseException.getResponseBodyAsString()); } - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } private void prepareHeaders(HttpHeaders headers, TbMsg msg) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java index 40b82ba150..e83a125265 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java @@ -17,11 +17,13 @@ package org.thingsboard.rule.engine.telemetry; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import com.google.gson.JsonParser; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.DonAsynchron; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; @@ -56,10 +58,10 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_R version = 2, nodeDescription = "Saves attributes data", nodeDetails = "Saves entity attributes based on configurable scope parameter. Expects messages with 'POST_ATTRIBUTES_REQUEST' message type. " + - "If upsert(update/insert) operation is completed successfully rule node will send the incoming message via Success chain, otherwise, Failure chain is used. " + - "Additionally if checkbox Send attributes updated notification is set to true, rule node will put the \"Attributes Updated\" " + - "event for SHARED_SCOPE and SERVER_SCOPE attributes updates to the corresponding rule engine queue." + - "Performance checkbox 'Save attributes only if the value changes' will skip attributes overwrites for values with no changes (avoid concurrent writes because this check is not transactional; will not update 'Last updated time' for skipped attributes).", + "If upsert(update/insert) operation is completed successfully rule node will send the incoming message via Success chain, otherwise, Failure chain is used. " + + "Additionally if checkbox Send attributes updated notification is set to true, rule node will put the \"Attributes Updated\" " + + "event for SHARED_SCOPE and SERVER_SCOPE attributes updates to the corresponding rule engine queue." + + "Performance checkbox 'Save attributes only if the value changes' will skip attributes overwrites for values with no changes (avoid concurrent writes because this check is not transactional; will not update 'Last updated time' for skipped attributes).", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeAttributesConfig", icon = "file_upload" @@ -114,16 +116,17 @@ public class TbMsgAttributesNode implements TbNode { ctx.tellSuccess(msg); return; } - ctx.getTelemetryService().saveAndNotify( - ctx.getTenantId(), - msg.getOriginator(), - scope, - attributes, - config.isNotifyDevice() || checkNotifyDeviceMdValue(msg.getMetaData().getValue(NOTIFY_DEVICE_METADATA_KEY)), - sendAttributesUpdateNotification ? - new AttributesUpdateNodeCallback(ctx, msg, scope.name(), attributes) : - new TelemetryNodeCallback(ctx, msg) - ); + FutureCallback callback = sendAttributesUpdateNotification ? + new AttributesUpdateNodeCallback(ctx, msg, scope.name(), attributes) : + new TelemetryNodeCallback(ctx, msg); + ctx.getTelemetryService().saveAttributes(AttributesSaveRequest.builder() + .tenantId(ctx.getTenantId()) + .entityId(msg.getOriginator()) + .scope(scope) + .entries(attributes) + .notifyDevice(config.isNotifyDevice() || checkNotifyDeviceMdValue(msg.getMetaData().getValue(NOTIFY_DEVICE_METADATA_KEY))) + .callback(callback) + .build()); } List filterChangedAttr(List currentAttributes, List newAttributes) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java index 66faa80f9f..f1f1a29f3c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java @@ -16,6 +16,7 @@ package org.thingsboard.rule.engine.telemetry; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; @@ -70,16 +71,16 @@ public class TbMsgDeleteAttributesNode implements TbNode { ctx.tellSuccess(msg); } else { AttributeScope scope = getScope(msg.getMetaData().getValue(SCOPE)); - ctx.getTelemetryService().deleteAndNotify( - ctx.getTenantId(), - msg.getOriginator(), - scope, - keysToDelete, - checkNotifyDevice(msg.getMetaData().getValue(NOTIFY_DEVICE_METADATA_KEY), scope), - config.isSendAttributesDeletedNotification() ? + ctx.getTelemetryService().deleteAttributes(AttributesDeleteRequest.builder() + .tenantId(ctx.getTenantId()) + .entityId(msg.getOriginator()) + .scope(scope) + .keys(keysToDelete) + .notifyDevice(checkNotifyDevice(msg.getMetaData().getValue(NOTIFY_DEVICE_METADATA_KEY), scope)) + .callback(config.isSendAttributesDeletedNotification() ? new AttributesDeleteNodeCallback(ctx, msg, scope.name(), keysToDelete) : - new TelemetryNodeCallback(ctx, msg) - ); + new TelemetryNodeCallback(ctx, msg)) + .build()); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index c90e8e2375..27f45feb47 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -22,6 +22,7 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.StringUtils; @@ -104,11 +105,15 @@ public class TbMsgTimeseriesNode implements TbNode { if (ttl == 0L) { ttl = tenantProfileDefaultStorageTtl; } - if (config.isSkipLatestPersistence()) { - ctx.getTelemetryService().saveWithoutLatestAndNotify(ctx.getTenantId(), msg.getCustomerId(), msg.getOriginator(), tsKvEntryList, ttl, new TelemetryNodeCallback(ctx, msg)); - } else { - ctx.getTelemetryService().saveAndNotify(ctx.getTenantId(), msg.getCustomerId(), msg.getOriginator(), tsKvEntryList, ttl, new TelemetryNodeCallback(ctx, msg)); - } + ctx.getTelemetryService().saveTimeseries(TimeseriesSaveRequest.builder() + .tenantId(ctx.getTenantId()) + .customerId(msg.getCustomerId()) + .entityId(msg.getOriginator()) + .entries(tsKvEntryList) + .ttl(ttl) + .saveLatest(!config.isSkipLatestPersistence()) + .callback(new TelemetryNodeCallback(ctx, msg)) + .build()); } public static long computeTs(TbMsg msg, boolean ignoreMetadataTs) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java index aedf8eac1e..1417618a54 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java @@ -105,7 +105,10 @@ public class TbCopyKeysNode extends TbAbstractTransformNodeWithTbMsgSource { log.debug("Unexpected CopyFrom value: {}. Allowed values: {}", copyFrom, TbMsgSource.values()); } } - ctx.tellSuccess(msgChanged ? TbMsg.transformMsg(msg, metaDataCopy, msgData) : msg); + ctx.tellSuccess(msgChanged ? msg.transform() + .metaData(metaDataCopy) + .data(msgData) + .build() : msg); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java index 3d4c9b9684..cf31381af2 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java @@ -100,7 +100,10 @@ public class TbDeleteKeysNode extends TbAbstractTransformNodeWithTbMsgSource { default: log.debug("Unexpected DeleteFrom value: {}. Allowed values: {}", deleteFrom, TbMsgSource.values()); } - ctx.tellSuccess(hasNoChanges ? msg : TbMsg.transformMsg(msg, metaDataCopy, msgDataStr)); + ctx.tellSuccess(hasNoChanges ? msg : msg.transform() + .metaData(metaDataCopy) + .data(msgDataStr) + .build()); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java index b0a7bc059f..043e914c2c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java @@ -68,7 +68,9 @@ public class TbJsonPathNode implements TbNode { if (!TbJsonPathNodeConfiguration.DEFAULT_JSON_PATH.equals(this.jsonPathValue)) { try { Object jsonPathData = jsonPath.read(msg.getData(), this.configurationJsonPath); - ctx.tellSuccess(TbMsg.transformMsgData(msg, JacksonUtil.toString(jsonPathData))); + ctx.tellSuccess(msg.transform() + .data(JacksonUtil.toString(jsonPathData)) + .build()); } catch (PathNotFoundException e) { ctx.tellFailure(msg, e); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java index f01f09ae49..490504c42b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java @@ -106,7 +106,10 @@ public class TbRenameKeysNode extends TbAbstractTransformNodeWithTbMsgSource { default: log.debug("Unexpected RenameIn value: {}. Allowed values: {}", renameIn, TbMsgSource.values()); } - ctx.tellSuccess(msgChanged ? TbMsg.transformMsg(msg, metaDataCopy, data) : msg); + ctx.tellSuccess(msgChanged ? msg.transform() + .metaData(metaDataCopy) + .data(data) + .build() : msg); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java index fb12352dc0..92a3dd8d09 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java @@ -64,7 +64,9 @@ public class TbSplitArrayMsgNode implements TbNode { if (data.isEmpty()) { ctx.ack(msg); } else if (data.size() == 1) { - ctx.tellSuccess(TbMsg.transformMsgData(msg, JacksonUtil.toString(data.get(0)))); + ctx.tellSuccess(msg.transform() + .data(JacksonUtil.toString(data.get(0))) + .build()); } else { TbMsgCallbackWrapper wrapper = new MultipleTbMsgsCallbackWrapper(data.size(), new TbMsgCallback() { @Override @@ -78,7 +80,9 @@ public class TbSplitArrayMsgNode implements TbNode { } }); data.forEach(msgNode -> { - TbMsg outMsg = TbMsg.transformMsgData(msg, JacksonUtil.toString(msgNode)); + TbMsg outMsg = msg.transform() + .data(JacksonUtil.toString(msgNode)) + .build(); ctx.enqueueForTellNext(outMsg, TbNodeConnectionType.SUCCESS, wrapper::onSuccess, wrapper::onFailure); }); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNodeTest.java index dde4808deb..6a136a473b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNodeTest.java @@ -306,7 +306,12 @@ class TbAssignToCustomerNodeTest extends AbstractRuleNodeUpgradeTest { } private TbMsg getTbMsg(EntityId originator) { - return TbMsg.newMsg(TbMsgType.NA, originator, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.newMsg() + .type(TbMsgType.NA) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } private EntityId toOriginator(EntityType type) { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java index afab0a0a99..a50c2ff5aa 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java @@ -91,7 +91,12 @@ class TbClearAlarmNodeTest { void alarmCanBeCleared() { initWithClearAlarmScript(); metadata.putValue("key", "value"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, msgOriginator, metadata, "{\"temperature\": 50}"); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msgOriginator) + .copyMetaData(metadata) + .data("{\"temperature\": 50}") + .build(); long oldEndDate = System.currentTimeMillis(); Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(msgOriginator).severity(AlarmSeverity.WARNING).endTs(oldEndDate).build(); @@ -143,7 +148,12 @@ class TbClearAlarmNodeTest { void alarmCanBeClearedWithAlarmOriginator() { initWithClearAlarmScript(); metadata.putValue("key", "value"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, alarmOriginator, metadata, "{\"temperature\": 50}"); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(alarmOriginator) + .copyMetaData(metadata) + .data("{\"temperature\": 50}") + .build(); long oldEndDate = System.currentTimeMillis(); AlarmId id = new AlarmId(alarmOriginator.getId()); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java index 4166c05a7a..91bdb8d6ff 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java @@ -15,7 +15,6 @@ */ package org.thingsboard.rule.engine.action; -import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -26,6 +25,8 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TbContext; @@ -37,7 +38,6 @@ import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.objects.AttributesEntityView; @@ -55,7 +55,7 @@ import java.util.UUID; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.verify; @@ -106,17 +106,20 @@ public class TbCopyAttributesToEntityViewNodeTest { public void givenExistingClientAttributes_whenOnMsg_thenCopyAttributesToView() { EntityView entityView = getEntityView(CLIENT_TELEMETRY_ENTITY_VIEW); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, - new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())), - "{\"clientAttribute1\": 100, \"clientAttribute2\": \"value2\"}"); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name()))) + .data("{\"clientAttribute1\": 100, \"clientAttribute2\": \"value2\"}") + .build(); mockEntityViewLookup(entityView); when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); doAnswer(invocation -> { - FutureCallback callback = invocation.getArgument(4); - callback.onSuccess(null); + AttributesSaveRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).saveAndNotify(any(), any(), any(AttributeScope.class), anyList(), any(FutureCallback.class)); + }).when(telemetryServiceMock).saveAttributes(any(AttributesSaveRequest.class)); TbMsg newMsg = TbMsg.newMsg(msg, msg.getQueueName(), msg.getRuleChainId(), msg.getRuleNodeId()); // TODO: use newMsg() with any(TbMsgType.class), replace in other tests as well. doAnswer(invocation -> newMsg).when(ctxMock).newMsg(any(), any(String.class), any(), any(), any(), any()); @@ -124,13 +127,15 @@ public class TbCopyAttributesToEntityViewNodeTest { node.onMsg(ctxMock, msg); verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID)); - ArgumentCaptor> filteredAttributesCaptor = ArgumentCaptor.forClass(List.class); - verify(telemetryServiceMock).saveAndNotify(eq(TENANT_ID), eq(ENTITY_VIEW_ID), eq(AttributeScope.CLIENT_SCOPE), - filteredAttributesCaptor.capture(), any(FutureCallback.class)); - List filteredAttributesCaptorValue = filteredAttributesCaptor.getValue(); - assertThat(filteredAttributesCaptorValue.size()).isEqualTo(1); - assertThat(filteredAttributesCaptorValue.get(0).getKey()).isEqualTo("clientAttribute1"); - assertThat(filteredAttributesCaptorValue.get(0).getValue()).isEqualTo(100L); + verify(telemetryServiceMock).saveAttributes(assertArg(request -> { + assertThat(request.getTenantId()).isEqualTo(TENANT_ID); + assertThat(request.getEntityId()).isEqualTo(ENTITY_VIEW_ID); + assertThat(request.getScope()).isEqualTo(AttributeScope.CLIENT_SCOPE); + + assertThat(request.getEntries().size()).isEqualTo(1); + assertThat(request.getEntries().get(0).getKey()).isEqualTo("clientAttribute1"); + assertThat(request.getEntries().get(0).getValue()).isEqualTo(100L); + })); verify(ctxMock).ack(eq(msg)); verify(ctxMock).enqueueForTellNext(eq(newMsg), eq(TbNodeConnectionType.SUCCESS)); verifyNoMoreInteractions(ctxMock, entityViewServiceMock, telemetryServiceMock); @@ -140,28 +145,33 @@ public class TbCopyAttributesToEntityViewNodeTest { public void givenExistingServerAttributesAndMsgTypeAttributesDeleted_whenOnMsg_thenDeleteAttributesFromView() { EntityView entityView = getEntityView(SERVER_TELEMETRY_ENTITY_VIEW); - TbMsg msg = TbMsg.newMsg( - ATTRIBUTES_DELETED, DEVICE_ID, new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())), - "{\"attributes\": [\"serverAttribute1\"]}"); + TbMsg msg = TbMsg.newMsg() + .type(ATTRIBUTES_DELETED) + .originator(DEVICE_ID) + .copyMetaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name()))) + .data("{\"attributes\": [\"serverAttribute1\"]}") + .build(); mockEntityViewLookup(entityView); when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); doAnswer(invocation -> { - FutureCallback callback = invocation.getArgument(4); - callback.onSuccess(null); + AttributesDeleteRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).deleteAndNotify(any(), any(), any(AttributeScope.class), anyList(), any(FutureCallback.class)); + }).when(telemetryServiceMock).deleteAttributes(any()); TbMsg newMsg = TbMsg.newMsg(msg, msg.getQueueName(), msg.getRuleChainId(), msg.getRuleNodeId()); doAnswer(invocation -> newMsg).when(ctxMock).newMsg(any(), any(String.class), any(), any(), any(), any()); node.onMsg(ctxMock, msg); verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID)); - ArgumentCaptor> filteredAttributesCaptor = ArgumentCaptor.forClass(List.class); - verify(telemetryServiceMock).deleteAndNotify(eq(TENANT_ID), eq(ENTITY_VIEW_ID), eq(AttributeScope.SERVER_SCOPE), filteredAttributesCaptor.capture(), any(FutureCallback.class)); - List filteredAttributesCaptorValue = filteredAttributesCaptor.getValue(); - assertThat(filteredAttributesCaptorValue.size()).isEqualTo(1); - assertThat(filteredAttributesCaptorValue.get(0)).isEqualTo("serverAttribute1"); + verify(telemetryServiceMock).deleteAttributes(assertArg(request -> { + assertThat(request.getTenantId()).isEqualTo(TENANT_ID); + assertThat(request.getEntityId()).isEqualTo(ENTITY_VIEW_ID); + assertThat(request.getScope()).isEqualTo(AttributeScope.SERVER_SCOPE); + assertThat(request.getKeys().size()).isEqualTo(1); + assertThat(request.getKeys().get(0)).isEqualTo("serverAttribute1"); + })); verify(ctxMock).ack(eq(msg)); verify(ctxMock).enqueueForTellNext(eq(newMsg), eq(TbNodeConnectionType.SUCCESS)); verifyNoMoreInteractions(ctxMock, entityViewServiceMock, telemetryServiceMock); @@ -171,9 +181,12 @@ public class TbCopyAttributesToEntityViewNodeTest { public void givenNonMatchedSharedAttributesAndMsgTypeIsAttributesDeleted_whenOnMsg_thenNoAttributesDeleteFromView() { EntityView entityView = getEntityView(SHARED_TELEMETRY_ENTITY_VIEW); - TbMsg msg = TbMsg.newMsg( - TbMsgType.ATTRIBUTES_DELETED, DEVICE_ID, new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SHARED_SCOPE.name())), - "{\"attributes\": [\"anotherAttribute\"]}"); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.ATTRIBUTES_DELETED) + .originator(DEVICE_ID) + .copyMetaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SHARED_SCOPE.name()))) + .data("{\"attributes\": [\"anotherAttribute\"]}") + .build(); mockEntityViewLookup(entityView); @@ -188,24 +201,32 @@ public class TbCopyAttributesToEntityViewNodeTest { public void givenNonMatchedAttributesAndMsgTypeIsPostAttributesRequest_whenOnMsg_thenCopyNoAttributesToView() { EntityView entityView = getEntityView(CLIENT_TELEMETRY_ENTITY_VIEW); - TbMsg msg = TbMsg.newMsg( - TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())), - "{\"clientAttribute2\": \"value2\"}"); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name()))) + .data("{\"clientAttribute2\": \"value2\"}") + .build(); mockEntityViewLookup(entityView); when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); doAnswer(invocation -> { - FutureCallback callback = invocation.getArgument(4); - callback.onSuccess(null); + AttributesSaveRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).saveAndNotify(any(), any(), any(AttributeScope.class), anyList(), any(FutureCallback.class)); + }).when(telemetryServiceMock).saveAttributes(any(AttributesSaveRequest.class)); TbMsg newMsg = TbMsg.newMsg(msg, msg.getQueueName(), msg.getRuleChainId(), msg.getRuleNodeId()); doAnswer(invocation -> newMsg).when(ctxMock).newMsg(any(), any(String.class), any(), any(), any(), any()); node.onMsg(ctxMock, msg); verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID)); - verify(telemetryServiceMock).saveAndNotify(eq(TENANT_ID), eq(ENTITY_VIEW_ID), eq(AttributeScope.CLIENT_SCOPE), eq(Collections.emptyList()), any(FutureCallback.class)); + verify(telemetryServiceMock).saveAttributes(assertArg(request -> { + assertThat(request.getTenantId()).isEqualTo(TENANT_ID); + assertThat(request.getEntityId()).isEqualTo(ENTITY_VIEW_ID); + assertThat(request.getScope()).isEqualTo(AttributeScope.CLIENT_SCOPE); + assertThat(request.getEntries().isEmpty()).isTrue(); + })); verify(ctxMock).ack(eq(msg)); verify(ctxMock).enqueueForTellNext(eq(newMsg), eq(TbNodeConnectionType.SUCCESS)); verifyNoMoreInteractions(ctxMock, entityViewServiceMock, telemetryServiceMock); @@ -220,9 +241,12 @@ public class TbCopyAttributesToEntityViewNodeTest { ); mockEntityViewLookup(entityView); - TbMsg msg = TbMsg.newMsg( - ATTRIBUTES_DELETED, DEVICE_ID, new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())), - "{\"attributes\": [\"serverAttribute1\"]}"); + TbMsg msg = TbMsg.newMsg() + .type(ATTRIBUTES_DELETED) + .originator(DEVICE_ID) + .copyMetaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name()))) + .data("{\"attributes\": [\"serverAttribute1\"]}") + .build(); node.onMsg(ctxMock, msg); verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID)); @@ -233,7 +257,12 @@ public class TbCopyAttributesToEntityViewNodeTest { @ParameterizedTest @EnumSource(TbMsgType.class) public void givenMsgTypeAndEmptyMetadata_whenOnMsg_thenVerifyFailureMsg(TbMsgType msgType) { - TbMsg msg = TbMsg.newMsg(msgType, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(msgType) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java index c0e27231c4..370c9f1347 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java @@ -163,7 +163,12 @@ class TbCreateAlarmNodeTest { var ruleNodeSelfId = new RuleNodeId(Uuids.timeBased()); - var incomingMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, msgOriginator, metadata, "{\"temperature\": 50}"); + var incomingMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msgOriginator) + .copyMetaData(metadata) + .data("{\"temperature\": 50}") + .build(); Alarm existingAlarm = null; @@ -224,12 +229,12 @@ class TbCreateAlarmNodeTest { given(alarmServiceMock.createAlarm(expectedCreateAlarmRequest)).willReturn(apiCallResult); given(ctxMock.alarmActionMsg(expectedCreatedAlarmInfo, ruleNodeSelfId, TbMsgType.ENTITY_CREATED)).willReturn(alarmActionMsgMock); given(ctxMock.transformMsg(any(TbMsg.class), any(TbMsgType.class), any(EntityId.class), any(TbMsgMetaData.class), anyString())) - .willAnswer(answer -> TbMsg.transformMsg( - answer.getArgument(0, TbMsg.class), - answer.getArgument(1, TbMsgType.class), - answer.getArgument(2, EntityId.class), - answer.getArgument(3, TbMsgMetaData.class), - answer.getArgument(4, String.class)) + .willAnswer(answer -> answer.getArgument(0, TbMsg.class).transform() + .type(answer.getArgument(1, TbMsgType.class)) + .originator(answer.getArgument(2, EntityId.class)) + .metaData(answer.getArgument(3, TbMsgMetaData.class)) + .data(answer.getArgument(4, String.class)) + .build() ); given(ctxMock.createScriptEngine(ScriptLanguage.TBEL, TbAbstractAlarmNodeConfiguration.ALARM_DETAILS_BUILD_TBEL_TEMPLATE)).willReturn(alarmDetailsScriptMock); @@ -317,7 +322,12 @@ class TbCreateAlarmNodeTest { var ruleNodeSelfId = new RuleNodeId(Uuids.timeBased()); - var incomingMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, msgOriginator, metadata, "{\"temperature\": 50, \"alarmType\": \"" + alarmType + "\"}"); + var incomingMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msgOriginator) + .copyMetaData(metadata) + .data("{\"temperature\": 50, \"alarmType\": \"" + alarmType + "\"}") + .build(); var existingClearedAlarm = Alarm.builder() .tenantId(tenantId) @@ -393,12 +403,12 @@ class TbCreateAlarmNodeTest { given(alarmServiceMock.createAlarm(expectedCreateAlarmRequest)).willReturn(apiCallResult); given(ctxMock.alarmActionMsg(expectedCreatedAlarmInfo, ruleNodeSelfId, TbMsgType.ENTITY_CREATED)).willReturn(alarmActionMsgMock); given(ctxMock.transformMsg(any(TbMsg.class), any(TbMsgType.class), any(EntityId.class), any(TbMsgMetaData.class), anyString())) - .willAnswer(answer -> TbMsg.transformMsg( - answer.getArgument(0, TbMsg.class), - answer.getArgument(1, TbMsgType.class), - answer.getArgument(2, EntityId.class), - answer.getArgument(3, TbMsgMetaData.class), - answer.getArgument(4, String.class)) + .willAnswer(answer -> answer.getArgument(0, TbMsg.class).transform() + .type(answer.getArgument(1, TbMsgType.class)) + .originator(answer.getArgument(2, EntityId.class)) + .metaData(answer.getArgument(3, TbMsgMetaData.class)) + .data(answer.getArgument(4, String.class)) + .build() ); given(ctxMock.createScriptEngine(ScriptLanguage.JS, config.getAlarmDetailsBuildJs())).willReturn(alarmDetailsScriptMock); @@ -508,7 +518,12 @@ class TbCreateAlarmNodeTest { var ruleNodeSelfId = new RuleNodeId(Uuids.timeBased()); - var incomingMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, msgOriginator, metadata, "{\"temperature\": 50, \"alarmSeverity\": \"" + newAlarmSeverity.name() + "\"}"); + var incomingMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msgOriginator) + .copyMetaData(metadata) + .data("{\"temperature\": 50, \"alarmSeverity\": \"" + newAlarmSeverity.name() + "\"}") + .build(); var existingAlarmId = new AlarmId(Uuids.timeBased()); var existingActiveAlarm = Alarm.builder() @@ -583,12 +598,12 @@ class TbCreateAlarmNodeTest { given(alarmServiceMock.updateAlarm(expectedUpdateAlarmRequest)).willReturn(apiCallResult); given(ctxMock.alarmActionMsg(expectedUpdatedAlarmInfo, ruleNodeSelfId, TbMsgType.ENTITY_UPDATED)).willReturn(alarmActionMsgMock); given(ctxMock.transformMsg(any(TbMsg.class), any(TbMsgType.class), any(EntityId.class), any(TbMsgMetaData.class), anyString())) - .willAnswer(answer -> TbMsg.transformMsg( - answer.getArgument(0, TbMsg.class), - answer.getArgument(1, TbMsgType.class), - answer.getArgument(2, EntityId.class), - answer.getArgument(3, TbMsgMetaData.class), - answer.getArgument(4, String.class)) + .willAnswer(answer -> answer.getArgument(0, TbMsg.class).transform() + .type(answer.getArgument(1, TbMsgType.class)) + .originator(answer.getArgument(2, EntityId.class)) + .metaData(answer.getArgument(3, TbMsgMetaData.class)) + .data(answer.getArgument(4, String.class)) + .build() ); given(ctxMock.createScriptEngine(ScriptLanguage.TBEL, config.getAlarmDetailsBuildTbel())).willReturn(alarmDetailsScriptMock); @@ -680,7 +695,12 @@ class TbCreateAlarmNodeTest { var ruleNodeSelfId = new RuleNodeId(Uuids.timeBased()); - var incomingMsg = TbMsg.newMsg(TbMsgType.ALARM, msgOriginator, metadata, JacksonUtil.toString(alarmFromIncomingMessage)); + var incomingMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(msgOriginator) + .copyMetaData(metadata) + .data(JacksonUtil.toString(alarmFromIncomingMessage)) + .build(); var existingClearedAlarm = Alarm.builder() .tenantId(tenantId) @@ -755,12 +775,12 @@ class TbCreateAlarmNodeTest { given(alarmServiceMock.createAlarm(expectedCreateAlarmRequest)).willReturn(apiCallResult); given(ctxMock.alarmActionMsg(expectedCreatedAlarmInfo, ruleNodeSelfId, TbMsgType.ENTITY_CREATED)).willReturn(alarmActionMsgMock); given(ctxMock.transformMsg(any(TbMsg.class), any(TbMsgType.class), any(EntityId.class), any(TbMsgMetaData.class), anyString())) - .willAnswer(answer -> TbMsg.transformMsg( - answer.getArgument(0, TbMsg.class), - answer.getArgument(1, TbMsgType.class), - answer.getArgument(2, EntityId.class), - answer.getArgument(3, TbMsgMetaData.class), - answer.getArgument(4, String.class)) + .willAnswer(answer -> answer.getArgument(0, TbMsg.class).transform() + .type(answer.getArgument(1, TbMsgType.class)) + .originator(answer.getArgument(2, EntityId.class)) + .metaData(answer.getArgument(3, TbMsgMetaData.class)) + .data(answer.getArgument(4, String.class)) + .build() ); given(ctxMock.createScriptEngine(ScriptLanguage.TBEL, config.getAlarmDetailsBuildTbel())).willReturn(alarmDetailsScriptMock); @@ -867,7 +887,12 @@ class TbCreateAlarmNodeTest { .details(newAlarmDetails) .build(); - var incomingMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, msgOriginator, metadata, JacksonUtil.toString(alarmFromIncomingMessage)); + var incomingMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msgOriginator) + .copyMetaData(metadata) + .data(JacksonUtil.toString(alarmFromIncomingMessage)) + .build(); var existingAlarmId = new AlarmId(Uuids.timeBased()); var existingActiveAlarm = Alarm.builder() @@ -942,12 +967,12 @@ class TbCreateAlarmNodeTest { given(alarmServiceMock.updateAlarm(expectedUpdateAlarmRequest)).willReturn(apiCallResult); given(ctxMock.alarmActionMsg(expectedUpdatedAlarmInfo, ruleNodeSelfId, TbMsgType.ENTITY_UPDATED)).willReturn(alarmActionMsgMock); given(ctxMock.transformMsg(any(TbMsg.class), any(TbMsgType.class), any(EntityId.class), any(TbMsgMetaData.class), anyString())) - .willAnswer(answer -> TbMsg.transformMsg( - answer.getArgument(0, TbMsg.class), - answer.getArgument(1, TbMsgType.class), - answer.getArgument(2, EntityId.class), - answer.getArgument(3, TbMsgMetaData.class), - answer.getArgument(4, String.class)) + .willAnswer(answer -> answer.getArgument(0, TbMsg.class).transform() + .type(answer.getArgument(1, TbMsgType.class)) + .originator(answer.getArgument(2, EntityId.class)) + .metaData(answer.getArgument(3, TbMsgMetaData.class)) + .data(answer.getArgument(4, String.class)) + .build() ); given(ctxMock.createScriptEngine(ScriptLanguage.TBEL, config.getAlarmDetailsBuildTbel())).willReturn(alarmDetailsScriptMock); @@ -1048,7 +1073,12 @@ class TbCreateAlarmNodeTest { .details(alarmDetails) .build(); - var incomingMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, msgOriginator, metadata, JacksonUtil.toString(alarmFromIncomingMessage)); + var incomingMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msgOriginator) + .copyMetaData(metadata) + .data(JacksonUtil.toString(alarmFromIncomingMessage)) + .build(); var existingAlarmId = new AlarmId(Uuids.timeBased()); var existingActiveAlarm = Alarm.builder() @@ -1123,12 +1153,12 @@ class TbCreateAlarmNodeTest { given(alarmServiceMock.updateAlarm(expectedUpdateAlarmRequest)).willReturn(apiCallResult); given(ctxMock.alarmActionMsg(expectedUpdatedAlarmInfo, ruleNodeSelfId, TbMsgType.ENTITY_UPDATED)).willReturn(alarmActionMsgMock); given(ctxMock.transformMsg(any(TbMsg.class), any(TbMsgType.class), any(EntityId.class), any(TbMsgMetaData.class), anyString())) - .willAnswer(answer -> TbMsg.transformMsg( - answer.getArgument(0, TbMsg.class), - answer.getArgument(1, TbMsgType.class), - answer.getArgument(2, EntityId.class), - answer.getArgument(3, TbMsgMetaData.class), - answer.getArgument(4, String.class)) + .willAnswer(answer -> answer.getArgument(0, TbMsg.class).transform() + .type(answer.getArgument(1, TbMsgType.class)) + .originator(answer.getArgument(2, EntityId.class)) + .metaData(answer.getArgument(3, TbMsgMetaData.class)) + .data(answer.getArgument(4, String.class)) + .build() ); given(ctxMock.createScriptEngine(ScriptLanguage.TBEL, config.getAlarmDetailsBuildTbel())).willReturn(alarmDetailsScriptMock); @@ -1189,7 +1219,12 @@ class TbCreateAlarmNodeTest { // GIVEN config = config.defaultConfiguration(); - var incomingMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, msgOriginator, metadata, "{\"temperature\": 50}"); + var incomingMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msgOriginator) + .copyMetaData(metadata) + .data("{\"temperature\": 50}") + .build(); given(ctxMock.getTenantId()).willReturn(tenantId); given(ctxMock.getAlarmService()).willReturn(alarmServiceMock); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java index 86381634ac..cf4eec3072 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java @@ -439,7 +439,9 @@ public class TbCreateRelationNodeTest extends AbstractRuleNodeUpgradeTest { var md = getMetadataWithNameTemplate(); var msg = getTbMsg(originatorId, md); - var msgAfterOriginatorChanged = TbMsg.transformMsgOriginator(msg, originatorId); + var msgAfterOriginatorChanged = msg.transform() + .originator(originatorId) + .build(); when(ctxMock.transformMsgOriginator(any(), any())).thenReturn(msgAfterOriginatorChanged); // WHEN @@ -486,7 +488,12 @@ public class TbCreateRelationNodeTest extends AbstractRuleNodeUpgradeTest { when(ctxMock.getRelationService()).thenReturn(relationServiceMock); var mockMethodCallsMap = mockEntityServiceCallsCreateEntityIfNotExistsEnabled(); - var entityCreatedMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + var entityCreatedMsg = TbMsg.newMsg() + .type(TbMsgType.ENTITY_CREATED) + .originator(entityId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); mockMethodCallsMap.get(entityType).accept(entity, entityCreatedMsg); when(relationServiceMock.checkRelationAsync(any(), any(), any(), any(), any())).thenReturn(Futures.immediateFuture(false)); @@ -676,7 +683,12 @@ public class TbCreateRelationNodeTest extends AbstractRuleNodeUpgradeTest { } private TbMsg getTbMsg(EntityId originator, TbMsgMetaData metaData) { - return TbMsg.newMsg(TbMsgType.NA, originator, metaData, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.newMsg() + .type(TbMsgType.NA) + .originator(originator) + .copyMetaData(metaData) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } private TbMsgMetaData getMetadataWithNameTemplate() { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeTest.java index 55461bdd7b..ac530bd93b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeTest.java @@ -560,7 +560,12 @@ public class TbDeleteRelationNodeTest extends AbstractRuleNodeUpgradeTest { } private TbMsg getTbMsg(EntityId originator, TbMsgMetaData metaData) { - return TbMsg.newMsg(TbMsgType.NA, originator, metaData, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.newMsg() + .type(TbMsgType.NA) + .originator(originator) + .copyMetaData(metaData) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } private TbMsgMetaData getMetadataWithNameTemplate() { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeviceStateNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeviceStateNodeTest.java index 9b3d2dc29b..6dff2b90b0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeviceStateNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeviceStateNodeTest.java @@ -85,7 +85,12 @@ public class TbDeviceStateNodeTest { metaData.putValue("ts", String.valueOf(METADATA_TS)); var data = JacksonUtil.newObjectNode(); data.put("humidity", 58.3); - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, JacksonUtil.toString(data)); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data(JacksonUtil.toString(data)) + .build(); } @BeforeEach @@ -207,7 +212,12 @@ public class TbDeviceStateNodeTest { return unsupportedType; } }; - var msg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, nonDeviceOriginator, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + var msg = TbMsg.newMsg() + .type(TbMsgType.ENTITY_CREATED) + .originator(nonDeviceOriginator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -236,7 +246,13 @@ public class TbDeviceStateNodeTest { given(ctxMock.getDeviceStateManager()).willReturn(deviceStateManagerMock); long msgTs = METADATA_TS + 1; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, msgTs); + msg = TbMsg.newMsg() + .ts(msgTs) + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); // WHEN node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java index 7d915f1cc9..70ebc8dbef 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java @@ -50,7 +50,12 @@ public class TbLogNodeTest { TbLogNode node = new TbLogNode(); String data = "{\"key\": \"value\"}"; TbMsgMetaData metaData = new TbMsgMetaData(Map.of("mdKey1", "mdValue1", "mdKey2", "23")); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, metaData, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .copyMetaData(metaData) + .data(data) + .build(); String logMessage = node.toLogMessage(msg); log.info(logMessage); @@ -66,7 +71,12 @@ public class TbLogNodeTest { void givenEmptyDataMsg_whenToLog_thenReturnString() { TbLogNode node = new TbLogNode(); TbMsgMetaData metaData = new TbMsgMetaData(Collections.emptyMap()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, metaData, ""); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .copyMetaData(metaData) + .data("") + .build(); String logMessage = node.toLogMessage(msg); log.info(logMessage); @@ -82,7 +92,12 @@ public class TbLogNodeTest { void givenNullDataMsg_whenToLog_thenReturnString() { TbLogNode node = new TbLogNode(); TbMsgMetaData metaData = new TbMsgMetaData(Collections.emptyMap()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, metaData, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .copyMetaData(metaData) + .data(null) + .build(); String logMessage = node.toLogMessage(msg); log.info(logMessage); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbMsgCountNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbMsgCountNodeTest.java index e1f020f5ff..9e5f744b53 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbMsgCountNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbMsgCountNodeTest.java @@ -63,7 +63,12 @@ public class TbMsgCountNodeTest { private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("1b21c7cc-0c9e-4ab1-b867-99451599e146")); private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("04dfbd38-10e5-47b7-925f-11e795db89e1")); - private final TbMsg tickMsg = TbMsg.newMsg(TbMsgType.MSG_COUNT_SELF_MSG, RULE_NODE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + private final TbMsg tickMsg = TbMsg.newMsg() + .type(TbMsgType.MSG_COUNT_SELF_MSG) + .originator(RULE_NODE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); private ScheduledExecutorService executorService; private TbMsgCountNode node; @@ -120,7 +125,12 @@ public class TbMsgCountNodeTest { var expectedProcessedMsgs = new ArrayList(); for (int i = 0; i < msgCount; i++) { - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); if (msgWithCounterSent.get()) { break; } @@ -142,7 +152,12 @@ public class TbMsgCountNodeTest { then(ctxMock).should().enqueueForTellNext(msgWithCounterCaptor.capture(), eq(TbNodeConnectionType.SUCCESS)); TbMsg resultedMsg = msgWithCounterCaptor.getValue(); String expectedData = "{\"messageCount_tb-rule-engine\":" + currentMsgNumber + "}"; - TbMsg expectedMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TENANT_ID, TbMsgMetaData.EMPTY, expectedData); + TbMsg expectedMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TENANT_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(expectedData) + .build(); assertThat(resultedMsg).usingRecursiveComparison() .ignoringFields("id", "ts", "ctx", "metaData") .isEqualTo(expectedMsg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeTest.java index 7df2a982de..eae95e7025 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeTest.java @@ -185,7 +185,12 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); assertThatThrownBy(() -> node.onMsg(ctxMock, msg)) .isInstanceOf(IllegalStateException.class) .hasMessage("Invalid message structure, it is not a JSON Object: " + null); @@ -206,7 +211,12 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad "humidity": 77 } """; - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(data) + .build(); assertThatThrownBy(() -> node.onMsg(ctxMock, msg)) .isInstanceOf(RuntimeException.class) .hasMessage("Message data doesn't contain key: 'temp'!"); @@ -227,7 +237,12 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad "temp": [value] } """; - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(data) + .build(); assertThatThrownBy(() -> node.onMsg(ctxMock, msg)) .isInstanceOf(RuntimeException.class) .hasMessage("Message data key: 'temp' with value: '[\"value\"]' is not a JSON Object or JSON Primitive!"); @@ -249,7 +264,12 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad mockSubmittingCassandraTask(); node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(sessionMock).should().prepare(expectedQuery); @@ -299,7 +319,12 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad } } """; - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(data) + .build(); node.onMsg(ctxMock, msg); verifySettingStatementBuilder(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java index 8abef77289..d7858e5ceb 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java @@ -294,7 +294,12 @@ class TbUnassignFromCustomerNodeTest extends AbstractRuleNodeUpgradeTest { } private TbMsg getTbMsg(EntityId originator) { - return TbMsg.newMsg(TbMsgType.NA, originator, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.newMsg() + .type(TbMsgType.NA) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } private static EntityId toOriginator(EntityType type) { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java index 14ae9bef20..0afa3804b9 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java @@ -145,7 +145,12 @@ public class TbAwsLambdaNodeTest { config.setFunctionName(functionName); config.setQualifier(qualifier); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metadata, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metadata) + .data(data) + .build(); InvokeRequest request = createInvokeRequest(msg); String requestIdStr = "a124af57-e7c3-4ebb-83bf-b09ff86eaa23"; @@ -171,7 +176,10 @@ public class TbAwsLambdaNodeTest { assertThat(invokeRequestCaptor.getValue().getQualifier()).isEqualTo(expectedQualifier); TbMsgMetaData resultMsgMetadata = metadata.copy(); resultMsgMetadata.putValue("requestId", requestIdStr); - TbMsg resultedMsg = TbMsg.transformMsg(msg, resultMsgMetadata, funcResponsePayload); + TbMsg resultedMsg = msg.transform() + .metaData(resultMsgMetadata) + .data(funcResponsePayload) + .build(); assertThat(msgCaptor.getValue()).usingRecursiveComparison() .ignoringFields("ctx") .isEqualTo(resultedMsg); @@ -197,7 +205,12 @@ public class TbAwsLambdaNodeTest { init(); config.setTellFailureIfFuncThrowsExc(true); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); InvokeRequest request = createInvokeRequest(msg); String requestIdStr = "a124af57-e7c3-4ebb-83bf-b09ff86eaa23"; String errorMsg = "Unhandled exception from function"; @@ -221,7 +234,9 @@ public class TbAwsLambdaNodeTest { verify(ctx).tellFailure(msgCaptor.capture(), throwableCaptor.capture()); var metadata = Map.of("error", RuntimeException.class + ": " + errorMsg, "requestId", requestIdStr); - TbMsg resultedMsg = TbMsg.transformMsgMetadata(msg, new TbMsgMetaData(metadata)); + TbMsg resultedMsg = msg.transform() + .metaData(new TbMsgMetaData(metadata)) + .build(); assertThat(msgCaptor.getValue()).usingRecursiveComparison() .ignoringFields("ctx") @@ -233,7 +248,12 @@ public class TbAwsLambdaNodeTest { public void givenExceptionWasThrownInsideFunctionAndTellFailureIfFuncThrowsExcIsFalse_whenOnMsg_thenTellSuccess() { init(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); InvokeRequest request = createInvokeRequest(msg); String requestIdStr = "e83dfbc4-68d5-441c-8ee9-289959a30d3b"; String payload = "{\"errorMessage\":\"Something went wrong\",\"errorType\":\"Exception\",\"requestId\":\"" + requestIdStr + "\"}"; @@ -255,7 +275,10 @@ public class TbAwsLambdaNodeTest { verify(ctx).tellSuccess(msgCaptor.capture()); Map metadata = Map.of("requestId", requestIdStr); - TbMsg resultedMsg = TbMsg.transformMsg(msg, new TbMsgMetaData(metadata), payload); + TbMsg resultedMsg = msg.transform() + .metaData(new TbMsgMetaData(metadata)) + .data(payload) + .build(); assertThat(msgCaptor.getValue()).usingRecursiveComparison() .ignoringFields("ctx") @@ -266,7 +289,12 @@ public class TbAwsLambdaNodeTest { public void givenPayloadFromResultIsNull_whenOnMsg_thenTellFailure() { init(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); InvokeRequest request = createInvokeRequest(msg); String requestIdStr = "12bbb074-e2fc-4381-8f28-d4bd235103d5"; String errorMsg = "Payload from result of AWS Lambda function execution is null."; @@ -289,7 +317,9 @@ public class TbAwsLambdaNodeTest { verify(ctx).tellFailure(msgCaptor.capture(), throwableCaptor.capture()); var metadata = Map.of("error", RuntimeException.class + ": " + errorMsg, "requestId", requestIdStr); - TbMsg resultedMsg = TbMsg.transformMsgMetadata(msg, new TbMsgMetaData(metadata)); + TbMsg resultedMsg = msg.transform() + .metaData(new TbMsgMetaData(metadata)) + .build(); assertThat(msgCaptor.getValue()).usingRecursiveComparison() .ignoringFields("ctx") @@ -300,7 +330,12 @@ public class TbAwsLambdaNodeTest { @Test public void givenExceptionWasThrownOnAWS_whenOnMsg_thenTellFailure() { init(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); InvokeRequest request = createInvokeRequest(msg); String errorMsg = "Simulated error"; diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java index eaa20e7b59..e085857f3d 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java @@ -105,7 +105,12 @@ class TbSnsNodeTest { given(publishResultMock.getSdkResponseMetadata()).willReturn(responseMetadataMock); given(responseMetadataMock.getRequestId()).willReturn(requestId); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data(data) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().ack(msg); @@ -143,7 +148,12 @@ class TbSnsNodeTest { ListenableFuture failedFuture = Futures.immediateFailedFuture(new RuntimeException(errorMsg)); given(listeningExecutor.executeAsync(any(Callable.class))).willReturn(failedFuture); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should(never()).enqueueForTellNext(any(), any(String.class)); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java index e8238ce5af..3da7f3d190 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java @@ -107,7 +107,12 @@ class TbSqsNodeTest { mockSendingMsgRequest(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data(data) + .build(); node.onMsg(ctxMock, msg); SendMessageRequest sendMsgRequest = new SendMessageRequest() @@ -143,7 +148,12 @@ class TbSqsNodeTest { mockSendingMsgRequest(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data(data) + .build(); node.onMsg(ctxMock, msg); Map messageAttributes = new HashMap<>(); @@ -186,7 +196,12 @@ class TbSqsNodeTest { given(sendMessageResultMock.getMD5OfMessageAttributes()).willReturn(messageAttributesMd5); given(sendMessageResultMock.getSequenceNumber()).willReturn(sequenceNumber); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().ack(msg); @@ -221,7 +236,12 @@ class TbSqsNodeTest { ListenableFuture failedFuture = Futures.immediateFailedFuture(new RuntimeException(errorMsg)); given(listeningExecutor.executeAsync(any(Callable.class))).willReturn(failedFuture); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should(never()).enqueueForTellNext(any(), any(String.class)); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java index 53b8e5f814..cd5498c5d8 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java @@ -190,7 +190,12 @@ public class TbMsgGeneratorNodeTest extends AbstractRuleNodeUpgradeTest { given(ctxMock.createScriptEngine(any(), any(), any(), any(), any())).willReturn(scriptEngineMock); // creation of tickMsg - TbMsg tickMsg = TbMsg.newMsg(TbMsgType.GENERATOR_NODE_SELF_MSG, RULE_NODE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg tickMsg = TbMsg.newMsg() + .type(TbMsgType.GENERATOR_NODE_SELF_MSG) + .originator(RULE_NODE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); given(ctxMock.newMsg(null, TbMsgType.GENERATOR_NODE_SELF_MSG, RULE_NODE_ID, null, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING)).willReturn(tickMsg); // invocation of tellSelf() method @@ -203,16 +208,31 @@ public class TbMsgGeneratorNodeTest extends AbstractRuleNodeUpgradeTest { }).given(ctxMock).tellSelf(any(), any(Long.class)); // creation of first message - TbMsg firstMsg = TbMsg.newMsg(TbMsg.EMPTY_STRING, RULE_NODE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg firstMsg = TbMsg.newMsg() + .type(TbMsg.EMPTY_STRING) + .originator(RULE_NODE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); given(ctxMock.newMsg(null, TbMsg.EMPTY_STRING, RULE_NODE_ID, null, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT)).willReturn(firstMsg); // creation of generated message TbMsgMetaData metaData = new TbMsgMetaData(Map.of("data", "40")); - TbMsg generatedMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, RULE_NODE_ID, metaData, "{ \"temp\": 42, \"humidity\": 77 }"); + TbMsg generatedMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(RULE_NODE_ID) + .copyMetaData(metaData) + .data("{ \"temp\": 42, \"humidity\": 77 }") + .build(); given(scriptEngineMock.executeGenerateAsync(any())).willReturn(Futures.immediateFuture(generatedMsg)); // creation of prev message - TbMsg prevMsg = TbMsg.newMsg(generatedMsg.getType(), RULE_NODE_ID, generatedMsg.getMetaData(), generatedMsg.getData()); + TbMsg prevMsg = TbMsg.newMsg() + .type(generatedMsg.getType()) + .originator(RULE_NODE_ID) + .copyMetaData(generatedMsg.getMetaData()) + .data(generatedMsg.getData()) + .build(); given(ctxMock.newMsg(null, generatedMsg.getType(), RULE_NODE_ID, null, generatedMsg.getMetaData(), generatedMsg.getData())).willReturn(prevMsg); // WHEN diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java index 621a1a4ba2..602983398f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java @@ -85,8 +85,13 @@ public class TbMsgPushToEdgeNodeTest { Mockito.when(ctx.getEdgeService()).thenReturn(edgeService); Mockito.when(edgeService.findRelatedEdgeIdsByEntityId(tenantId, deviceId, new PageLink(RELATED_EDGES_CACHE_ITEMS))).thenReturn(new PageData<>()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctx, msg); @@ -106,8 +111,13 @@ public class TbMsgPushToEdgeNodeTest { PageData edgePageData = new PageData<>(List.of(edgeId), 1, 1, false); Mockito.when(edgeService.findRelatedEdgeIdsByEntityId(tenantId, userId, new PageLink(RELATED_EDGES_CACHE_ITEMS))).thenReturn(edgePageData); - TbMsg msg = TbMsg.newMsg(TbMsgType.ATTRIBUTES_UPDATED, userId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.ATTRIBUTES_UPDATED) + .originator(userId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctx, msg); @@ -137,8 +147,13 @@ public class TbMsgPushToEdgeNodeTest { Mockito.when(ctx.getDbCallbackExecutor()).thenReturn(dbCallbackExecutor); Mockito.when(edgeEventService.saveAsync(any())).thenReturn(SettableFuture.create()); - TbMsg msg = TbMsg.newMsg(event, new EdgeId(UUID.randomUUID()), metaData, - TbMsgDataType.JSON, "{\"lastConnectTs\":1}", null, null); + TbMsg msg = TbMsg.newMsg() + .type(event) + .originator(new EdgeId(UUID.randomUUID())) + .copyMetaData(metaData) + .dataType(TbMsgDataType.JSON) + .data("{\"lastConnectTs\":1}") + .build(); node.onMsg(ctx, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java index f072eb1723..15519e18ee 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java @@ -118,7 +118,13 @@ class TbAssetTypeSwitchNodeTest { } private TbMsg getTbMsg(EntityId entityId) { - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, callback); + return TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .callback(callback) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java index 5b71db5dfb..0068289080 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java @@ -174,7 +174,12 @@ class TbCheckAlarmStatusNodeTest { } private TbMsg getTbMsg(String msgData) { - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, msgData); + return TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(msgData) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java index ce8afbbe99..9ecac1a271 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java @@ -44,7 +44,12 @@ import static org.mockito.Mockito.verify; class TbCheckMessageNodeTest { private static final DeviceId DEVICE_ID = new DeviceId(UUID.randomUUID()); - private static final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + private static final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); private TbCheckMessageNode node; @@ -196,7 +201,12 @@ class TbCheckMessageNodeTest { metadata.putValue(DataConstants.DEVICE_NAME, "Test Device"); metadata.putValue(DataConstants.DEVICE_TYPE, DataConstants.DEFAULT_DEVICE_TYPE); metadata.putValue("ts", String.valueOf(System.currentTimeMillis())); - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, metadata, data); + return TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metadata) + .data(data) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java index e445b93455..72a78b73a0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java @@ -66,7 +66,12 @@ class TbCheckRelationNodeTest extends AbstractRuleNodeUpgradeTest { private final TenantId TENANT_ID = new TenantId(UUID.randomUUID()); private final DeviceId ORIGINATOR_ID = new DeviceId(UUID.randomUUID()); private final TestDbCallbackExecutor DB_EXECUTOR = new TestDbCallbackExecutor(); - private final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, ORIGINATOR_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + private final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(ORIGINATOR_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); private TbCheckRelationNode node; diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java index 707df26d0f..31bddccb21 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java @@ -118,6 +118,12 @@ class TbDeviceTypeSwitchNodeTest { } private TbMsg getTbMsg(EntityId entityId) { - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, callback); + return TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .callback(callback) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java index b991b7b220..79bd3fa710 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java @@ -59,7 +59,15 @@ public class TbJsFilterNodeTest { @Test public void falseEvaluationDoNotSendMsg() throws TbNodeException { initWithScript(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(null) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(TbMsg.EMPTY_JSON_OBJECT) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); when(scriptEngine.executeFilterAsync(msg)).thenReturn(Futures.immediateFuture(false)); node.onMsg(ctx, msg); @@ -71,7 +79,15 @@ public class TbJsFilterNodeTest { public void exceptionInJsThrowsException() throws TbNodeException { initWithScript(); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(null) + .copyMetaData(metaData) + .dataType(TbMsgDataType.JSON) + .data(TbMsg.EMPTY_JSON_OBJECT) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); when(scriptEngine.executeFilterAsync(msg)).thenReturn(Futures.immediateFailedFuture(new ScriptException("error"))); @@ -83,7 +99,15 @@ public class TbJsFilterNodeTest { public void metadataConditionCanBeTrue() throws TbNodeException { initWithScript(); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(null) + .copyMetaData(metaData) + .dataType(TbMsgDataType.JSON) + .data(TbMsg.EMPTY_JSON_OBJECT) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); when(scriptEngine.executeFilterAsync(msg)).thenReturn(Futures.immediateFuture(true)); node.onMsg(ctx, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java index ff7a40644b..74cd7e4f4f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java @@ -59,7 +59,15 @@ public class TbJsSwitchNodeTest { metaData.putValue("humidity", "99"); String rawJson = "{\"name\": \"Vit\", \"passed\": 5}"; - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(null) + .copyMetaData(metaData) + .dataType(TbMsgDataType.JSON) + .data(rawJson) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); when(scriptEngine.executeSwitchAsync(msg)).thenReturn(Futures.immediateFuture(Sets.newHashSet("one", "three"))); node.onMsg(ctx, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java index c760a295e2..2d9cf75e22 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java @@ -97,7 +97,12 @@ class TbMsgTypeFilterNodeTest { } private TbMsg getTbMsg(EntityId entityId, TbMsgType msgType) { - return TbMsg.newMsg(msgType, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.newMsg() + .type(msgType) + .originator(entityId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java index cc771de1d1..1d8bb285af 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java @@ -89,7 +89,12 @@ class TbMsgTypeSwitchNodeTest { } private TbMsg getTbMsg(TbMsgType msgType) { - return TbMsg.newMsg(msgType, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.newMsg() + .type(msgType) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java index b1f6c59407..378e5d53bf 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java @@ -96,7 +96,12 @@ class TbOriginatorTypeFilterNodeTest { } private TbMsg getTbMsg(EntityId entityId) { - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java index fd67fa16b8..4782c1bdee 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java @@ -90,7 +90,12 @@ class TbOriginatorTypeSwitchNodeTest { } private TbMsg getTbMsg(EntityId entityId) { - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java index 49e92f2d74..f712a607d0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java @@ -67,7 +67,12 @@ public class TbAckNodeTest { public void givenMsg_whenOnMsg_thenAckAndTellSuccess() throws TbNodeException { node.init(ctxMock, nodeConfiguration); DeviceId deviceId = new DeviceId(UUID.fromString("5770153d-6ca2-4447-8a54-5d8a4538e052")); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().ack(msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java index e596c71d1e..8c3c72fb2d 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java @@ -87,7 +87,12 @@ public class TbCheckpointNodeTest extends AbstractRuleNodeUpgradeTest { given(ctxMock.getQueueName()).willReturn(queueName); node.init(ctxMock, nodeConfiguration); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor onSuccess = ArgumentCaptor.forClass(Runnable.class); @@ -101,7 +106,12 @@ public class TbCheckpointNodeTest extends AbstractRuleNodeUpgradeTest { given(ctxMock.getQueueName()).willReturn(DataConstants.HP_QUEUE_NAME); node.init(ctxMock, nodeConfiguration); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor> onFailure = ArgumentCaptor.forClass(Consumer.class); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java index 680d106d1e..4457b3c671 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java @@ -300,6 +300,11 @@ public class TbRuleChainInputNodeTest extends AbstractRuleNodeUpgradeTest { } private TbMsg getMsg(EntityId entityId) { - return TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + return TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(entityId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java index 5c678e5c50..49d24f579f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java @@ -74,7 +74,12 @@ public class TbRuleChainOutputNodeTest { node.init(ctxMock, nodeConfiguration); DeviceId deviceId = new DeviceId(UUID.fromString("f514da88-79b3-46da-9f02-1747c5e84f44")); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().output(msg, "test"); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java index 309023b3c6..49fc03f784 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java @@ -118,7 +118,12 @@ class TbPubSubNodeTest { given(ctxMock.getExternalCallExecutor()).willReturn(executor); node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data(data) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().ack(msg); @@ -133,7 +138,9 @@ class TbPubSubNodeTest { ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); then(ctxMock).should().enqueueForTellNext(actualMsg.capture(), eq(TbNodeConnectionType.SUCCESS)); metaData.putValue("messageId", messageId); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metaData); + TbMsg expectedMsg = msg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg.getValue()) .usingRecursiveComparison() .ignoringFields("ctx") @@ -164,7 +171,12 @@ class TbPubSubNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); TbMsgMetaData metadata = new TbMsgMetaData(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metadata, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metadata) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should(never()).ack(msg); @@ -174,7 +186,9 @@ class TbPubSubNodeTest { ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); then(ctxMock).should().tellSuccess(actualMsg.capture()); metadata.putValue("messageId", messageId); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metadata); + TbMsg expectedMsg = msg.transform() + .metaData(metadata) + .build(); assertThat(actualMsg.getValue()) .usingRecursiveComparison() .ignoringFields("ctx") @@ -193,7 +207,12 @@ class TbPubSubNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should(never()).ack(any()); @@ -201,7 +220,9 @@ class TbPubSubNodeTest { ArgumentCaptor actualError = ArgumentCaptor.forClass(Throwable.class); then(ctxMock).should().tellFailure(actualMsg.capture(), actualError.capture()); metaData.putValue("error", RuntimeException.class + ": " + errorMsg); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metaData); + TbMsg expectedMsg = msg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg.getValue()) .usingRecursiveComparison() .ignoringFields("ctx") @@ -221,7 +242,12 @@ class TbPubSubNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().ack(msg); @@ -229,7 +255,9 @@ class TbPubSubNodeTest { ArgumentCaptor actualError = ArgumentCaptor.forClass(Throwable.class); then(ctxMock).should().enqueueForTellFailure(actualMsg.capture(), actualError.capture()); metaData.putValue("error", RuntimeException.class + ": " + errorMsg); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metaData); + TbMsg expectedMsg = msg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg.getValue()) .usingRecursiveComparison() .ignoringFields("ctx") diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java index a0b57ba948..872225243b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java @@ -164,7 +164,12 @@ class TbGpsGeofencingActionNodeTest extends AbstractRuleNodeUpgradeTest { private TbMsg getTbMsg(EntityId entityId, TbMsgMetaData metadata, double latitude, double longitude) { String data = "{\"latitude\": " + latitude + ", \"longitude\": " + longitude + "}"; - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, metadata, data); + return TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .copyMetaData(metadata) + .data(data) + .build(); } private TbMsgMetaData getMetadataForNewVersionPolygonPerimeter() { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java index 10968516e6..9a7a79b9ad 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java @@ -448,11 +448,21 @@ class TbGpsGeofencingFilterNodeTest { private TbMsg getTbMsg(EntityId entityId, TbMsgMetaData metadata, double latitude, double longitude) { String data = "{\"latitude\": " + latitude + ", \"longitude\": " + longitude + "}"; - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, metadata, data); + return TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .copyMetaData(metadata) + .data(data) + .build(); } private TbMsg getEmptyArrayTbMsg(EntityId entityId) { - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + return TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java index add12dcec2..1d7635476e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java @@ -188,7 +188,12 @@ public class TbKafkaNodeTest { ReflectionTestUtils.setField(node, "initError", new ThingsboardKafkaClientError(errorMsg)); // WHEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -212,7 +217,12 @@ public class TbKafkaNodeTest { // WHEN node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -232,7 +242,12 @@ public class TbKafkaNodeTest { // GIVEN config.setTopicPattern(topicPattern); config.setKeyPattern(keyPattern); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data(data) + .build(); String topic = TbNodeUtils.processPattern(topicPattern, msg); String key = TbNodeUtils.processPattern(keyPattern, msg); @@ -278,7 +293,12 @@ public class TbKafkaNodeTest { // WHEN node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -303,7 +323,12 @@ public class TbKafkaNodeTest { // WHEN node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -331,7 +356,12 @@ public class TbKafkaNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("key", "value"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -406,7 +436,9 @@ public class TbKafkaNodeTest { metaData.putValue("offset", String.valueOf(OFFSET)); metaData.putValue("partition", String.valueOf(PARTITION)); metaData.putValue("topic", expectedTopic); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(originalMsg, metaData); + TbMsg expectedMsg = originalMsg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg) .usingRecursiveComparison() .ignoringFields("ctx") @@ -416,7 +448,9 @@ public class TbKafkaNodeTest { private void verifyOutgoingFailureMsg(String errorMsg, TbMsg actualMsg, TbMsg originalMsg) { TbMsgMetaData metaData = originalMsg.getMetaData(); metaData.putValue("error", RuntimeException.class + ": " + errorMsg); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(originalMsg, metaData); + TbMsg expectedMsg = originalMsg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java index 5ea816a1eb..86d90ec9c0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java @@ -103,7 +103,12 @@ public class TbMsgToEmailNodeTest { } var msgDataStr = "{\"temperature\": " + EXPECTED_TEMPERATURE + "}"; - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, md, msgDataStr); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(md) + .data(msgDataStr) + .build(); // WHEN node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java index ba81fbad20..360fbce5b0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java @@ -34,10 +34,12 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.verification.Timeout; import org.thingsboard.common.util.AbstractListeningExecutor; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.DeviceId; @@ -46,8 +48,8 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; -import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -70,13 +72,13 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyDouble; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.willAnswer; import static org.mockito.BDDMockito.willReturn; import static org.mockito.BDDMockito.willThrow; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -173,7 +175,12 @@ public class TbMathNodeTest { metaData.putValue("key2", "argumentA"); ObjectNode msgNode = JacksonUtil.newObjectNode() .put("key3", "argumentB").put("argumentA", 2).put("argumentB", 2); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, msgNode.toString()); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(metaData) + .data(msgNode.toString()) + .build(); node.onMsg(ctx, msg); @@ -181,7 +188,12 @@ public class TbMathNodeTest { metaData.putValue("key2", "argumentC"); msgNode = JacksonUtil.newObjectNode() .put("key3", "argumentD").put("argumentC", 4).put("argumentD", 3); - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, msgNode.toString()); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(metaData) + .data(msgNode.toString()) + .build(); node.onMsg(ctx, msg); @@ -228,7 +240,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "b") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", arg1).put("b", arg2).toString()); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.newObjectNode().put("a", arg1).put("b", arg2).toString()) + .build(); node.onMsg(ctx, msg); @@ -291,7 +308,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", arg1).toString()); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.newObjectNode().put("a", arg1).toString()) + .build(); node.onMsg(ctx, msg); @@ -314,7 +336,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "b") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()) + .build(); node.onMsg(ctx, msg); @@ -337,7 +364,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "b") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()) + .build(); node.onMsg(ctx, msg); @@ -361,7 +393,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.TIME_SERIES, "b") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().toString()); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.newObjectNode().toString()) + .build(); when(attributesService.find(tenantId, originator, AttributeScope.SERVER_SCOPE, "a")) .thenReturn(Futures.immediateFuture(Optional.of(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("a", 2.0))))); @@ -389,7 +426,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.newObjectNode().put("a", 5).toString()) + .build(); node.onMsg(ctx, msg); @@ -411,7 +453,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.newObjectNode().put("a", 5).toString()) + .build(); node.onMsg(ctx, msg); @@ -433,16 +480,25 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); - - when(telemetryService.saveAttrAndNotify(any(), any(), any(AttributeScope.class), anyString(), anyDouble())) - .thenReturn(Futures.immediateFuture(null)); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.newObjectNode().put("a", 5).toString()) + .build(); + doAnswer(invocation -> { + AttributesSaveRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); + return null; + }).when(telemetryService).saveAttributes(any(AttributesSaveRequest.class)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); - verify(telemetryService, times(1)).saveAttrAndNotify(any(), any(), any(AttributeScope.class), anyString(), anyDouble()); + verify(telemetryService, times(1)).saveAttributes(assertArg(request -> { + assertThat(request.getEntries()).singleElement().extracting(KvEntry::getValue).isInstanceOf(Double.class); + })); TbMsg resultMsg = msgCaptor.getValue(); assertNotNull(resultMsg); @@ -459,15 +515,26 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); - when(telemetryService.saveAndNotify(any(), any(), any(TsKvEntry.class))) - .thenReturn(Futures.immediateFuture(null)); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.newObjectNode().put("a", 5).toString()) + .build(); + doAnswer(invocation -> { + TimeseriesSaveRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); + return null; + }).when(telemetryService).saveTimeseries(any(TimeseriesSaveRequest.class)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); - verify(telemetryService, times(1)).saveAndNotify(any(), any(), any(TsKvEntry.class)); + verify(telemetryService, times(1)).saveTimeseries(assertArg(request -> { + assertThat(request.getEntries()).size().isOne(); + assertThat(request.isSaveLatest()).isTrue(); + })); TbMsg resultMsg = msgCaptor.getValue(); assertNotNull(resultMsg); @@ -484,15 +551,26 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); - when(telemetryService.saveAndNotify(any(), any(), any(TsKvEntry.class))) - .thenReturn(Futures.immediateFuture(null)); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.newObjectNode().put("a", 5).toString()) + .build(); + doAnswer(invocation -> { + TimeseriesSaveRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); + return null; + }).when(telemetryService).saveTimeseries(any(TimeseriesSaveRequest.class)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); - verify(telemetryService, times(1)).saveAndNotify(any(), any(), any(TsKvEntry.class)); + verify(telemetryService, times(1)).saveTimeseries(assertArg(request -> { + assertThat(request.getEntries()).size().isOne(); + assertThat(request.isSaveLatest()).isTrue(); + })); TbMsg resultMsg = msgCaptor.getValue(); assertNotNull(resultMsg); @@ -515,7 +593,12 @@ public class TbMathNodeTest { new TbMathResult(TbMathArgumentType.MESSAGE_METADATA, "result", 3, false, false, null), tbMathArgument ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 10).toString()); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.newObjectNode().put("a", 10).toString()) + .build(); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); @@ -535,7 +618,12 @@ public class TbMathNodeTest { new TbMathResult(TbMathArgumentType.TIME_SERIES, "result", 3, true, false, DataConstants.SERVER_SCOPE), new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "TestKey") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 10).toString()); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.newObjectNode().put("a", 10).toString()) + .build(); node.onMsg(ctx, msg); ArgumentCaptor tCaptor = ArgumentCaptor.forClass(Throwable.class); @@ -550,7 +638,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); node.onMsg(ctx, msg); ArgumentCaptor tCaptor = ArgumentCaptor.forClass(Throwable.class); @@ -570,10 +663,20 @@ public class TbMathNodeTest { CountDownLatch slowProcessingLatch = new CountDownLatch(1); List slowMsgList = IntStream.range(0, 5) - .mapToObj(x -> TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originatorSlow, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString())) + .mapToObj(x -> TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originatorSlow) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()) + .build()) .toList(); List fastMsgList = IntStream.range(0, 2) - .mapToObj(x -> TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originatorFast, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString())) + .mapToObj(x -> TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originatorFast) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()) + .build()) .toList(); assertThat(slowMsgList.size()).as("slow msgs >= rule-dispatcher pool size").isGreaterThanOrEqualTo(RULE_DISPATCHER_POOL_SIZE); @@ -640,7 +743,12 @@ public class TbMathNodeTest { CountDownLatch slowProcessingLatch = new CountDownLatch(1); List slowMsgList = IntStream.range(0, 5) - .mapToObj(x -> TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originatorSlow, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString())) + .mapToObj(x -> TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originatorSlow) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()) + .build()) .collect(Collectors.toList()); assertThat(slowMsgList.size()).as("slow msgs >= rule-dispatcher pool size").isGreaterThanOrEqualTo(RULE_DISPATCHER_POOL_SIZE); @@ -713,7 +821,12 @@ public class TbMathNodeTest { }) .toList(); ctxNodes.forEach(ctxNode -> ruleEngineDispatcherExecutor.executeAsync(() -> ctxNode.getRight() - .onMsg(ctxNode.getLeft(), TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, "{\"a\":2,\"b\":2}")))); + .onMsg(ctxNode.getLeft(), TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(TbMsgMetaData.EMPTY) + .data("{\"a\":2,\"b\":2}") + .build()))); ctxNodes.forEach(ctxNode -> verify(ctxNode.getRight(), timeout(TIMEOUT)).onMsg(eq(ctxNode.getLeft()), any())); processingLatch.countDown(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java index 1b2ff4f2bf..823b02fe59 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java @@ -169,7 +169,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { // GIVEN node.init(ctxMock, nodeConfiguration); var msgData = "{\"pulseCounter\": 42}"; - var msg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -184,7 +189,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { public void givenInvalidMsgDataType_whenOnMsg_thenShouldTellNextOther() throws TbNodeException { // GIVEN node.init(ctxMock, nodeConfiguration); - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -200,7 +210,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { public void givenInputKeyIsNotPresent_whenOnMsg_thenShouldTellNextOther() throws TbNodeException { // GIVEN node.init(ctxMock, nodeConfiguration); - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -224,7 +239,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry("temperature", 40.5))); var msgData = "{\"temperature\": 42,\"airPressure\":123}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -254,7 +274,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry("temperature", 40L))); var msgData = "{\"temperature\": 42,\"airPressure\":123}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -284,7 +309,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry("temperature", "40.0"))); var msgData = "{\"temperature\": 42,\"airPressure\":123}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -318,7 +348,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msgData = "{\"temperature\": 42,\"airPressure\":123}"; var firstMsgMetaData = new TbMsgMetaData(); firstMsgMetaData.putValue("ts", String.valueOf(3L)); - var firstMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, firstMsgMetaData, msgData); + var firstMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(firstMsgMetaData) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, firstMsg); @@ -344,7 +379,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var secondMsgMetaData = new TbMsgMetaData(); secondMsgMetaData.putValue("ts", String.valueOf(6L)); - var secondMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, secondMsgMetaData, msgData); + var secondMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(secondMsgMetaData) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, secondMsg); @@ -375,7 +415,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry("temperature", null))); var msgData = "{\"temperature\": 42,\"airPressure\":123}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -403,7 +448,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry("pulseCounter", 200L))); var msgData = "{\"pulseCounter\":\"123\"}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -435,7 +485,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry("pulseCounter", 200L))); var msgData = "{\"pulseCounter\":\"123\"}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -459,7 +514,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry("pulseCounter", "high"))); var msgData = "{\"pulseCounter\":\"123\"}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -484,7 +544,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry("pulseCounter", false))); var msgData = "{\"pulseCounter\":true}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -509,7 +574,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new JsonDataEntry("pulseCounter", "{\"isActive\":false}"))); var msgData = "{\"pulseCounter\":{\"isActive\":true}}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -552,7 +622,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { List tbMsgList = IntStream.range(0, RULE_DISPATCHER_POOL_SIZE * 2).mapToObj(x -> { var msgData = "{\"pulseCounter\":" + 2 + "}"; - return TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + return TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(msgData) + .build(); }).toList(); CountDownLatch processingLatch = new CountDownLatch(tbMsgList.size()); @@ -597,7 +672,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(1L, new DoubleDataEntry("temperature", testConfig.prevValue()))); var msgData = "{\"temperature\":" + testConfig.currentValue() + ",\"airPressure\":123}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java index 5cbe20fc5e..ecb9ab80ee 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java @@ -173,7 +173,13 @@ public class TbFetchDeviceCredentialsNodeTest { final var metaData = new TbMsgMetaData(mdMap); final String data = "{\"TestAttribute_1\": \"humidity\", \"TestAttribute_2\": \"voltage\"}"; - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, metaData, data, callbackMock); + return TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .copyMetaData(metaData) + .data(data) + .callback(callbackMock) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java index fb08dff8e2..22144ff32e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java @@ -17,7 +17,6 @@ package org.thingsboard.rule.engine.metadata; import com.google.common.util.concurrent.Futures; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -247,7 +246,12 @@ public class TbGetAttributesNodeTest extends AbstractRuleNodeUpgradeTest { public void givenFetchLatestTimeseriesToDataAndDataIsNotJsonObject_whenOnMsg_thenException() throws Exception { // GIVEN node = initNode(TbMsgSource.DATA, true, true); - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ORIGINATOR_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(ORIGINATOR_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); @@ -343,7 +347,12 @@ public class TbGetAttributesNodeTest extends AbstractRuleNodeUpgradeTest { msgMetaData.putValue("client_attr_metadata", "client_attr_3"); msgMetaData.putValue("server_attr_metadata", "server_attr_3"); - return TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, msgMetaData, JacksonUtil.toString(msgData)); + return TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(entityId) + .copyMetaData(msgMetaData) + .data(JacksonUtil.toString(msgData)) + .build(); } private List getAttributeNames(String prefix) { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java index 894a538c80..e8e0b42a76 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java @@ -210,7 +210,12 @@ public class TbGetCustomerAttributeNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); @@ -225,7 +230,12 @@ public class TbGetCustomerAttributeNodeTest { // GIVEN var userId = new UserId(UUID.randomUUID()); - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, userId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(userId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); when(ctxMock.getTenantId()).thenReturn(TENANT_ID); @@ -469,7 +479,12 @@ public class TbGetCustomerAttributeNodeTest { var msgData = "{\"temp\":42,\"humidity\":77,\"messageBodyPattern1\":\"targetKey2\",\"messageBodyPattern2\":\"sourceKey3\"}"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, msgMetaData, msgData); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(msgMetaData) + .data(msgData) + .build(); } @RequiredArgsConstructor diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java index fa4df10609..47c78bc106 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java @@ -159,7 +159,12 @@ public class TbGetCustomerDetailsNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); @@ -458,7 +463,12 @@ public class TbGetCustomerDetailsNodeTest { var msgData = "{\"dataKey1\":123,\"dataKey2\":\"dataValue2\"}"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, msgMetaData, msgData); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(msgMetaData) + .data(msgData) + .build(); } private void mockFindCustomer() { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java index 8cacf33f79..212fa7993c 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java @@ -115,7 +115,12 @@ public class TbGetDeviceAttrNodeTest extends AbstractRuleNodeUpgradeTest { given(deviceServiceMock.findDevicesByQuery(any(TenantId.class), any(DeviceSearchQuery.class))).willReturn(Futures.immediateFuture(Collections.emptyList())); given(ctxMock.getDbCallbackExecutor()).willReturn(executor); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor actualException = ArgumentCaptor.forClass(Throwable.class); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java index fdb73279c0..54da7e6e78 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java @@ -135,7 +135,12 @@ public class TbGetOriginatorFieldsNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); @@ -164,7 +169,12 @@ public class TbGetOriginatorFieldsNodeTest { node.fetchTo = TbMsgSource.DATA; var msgMetaData = new TbMsgMetaData(); var msgData = "{\"temp\":42,\"humidity\":77}"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, msgMetaData, msgData); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(msgMetaData) + .data(msgData) + .build(); when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); when(ctxMock.getTenantId()).thenReturn(DUMMY_TENANT_ID); @@ -206,7 +216,12 @@ public class TbGetOriginatorFieldsNodeTest { node.fetchTo = TbMsgSource.DATA; var msgMetaData = new TbMsgMetaData(); var msgData = "{\"temp\":42,\"humidity\":77}"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, msgMetaData, msgData); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(msgMetaData) + .data(msgData) + .build(); when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); when(ctxMock.getTenantId()).thenReturn(DUMMY_TENANT_ID); @@ -249,7 +264,12 @@ public class TbGetOriginatorFieldsNodeTest { "testKey1", "testValue1", "testKey2", "123")); var msgData = "[\"value1\",\"value2\"]"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, msgMetaData, msgData); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(msgMetaData) + .data(msgData) + .build(); when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); when(ctxMock.getTenantId()).thenReturn(DUMMY_TENANT_ID); @@ -297,7 +317,12 @@ public class TbGetOriginatorFieldsNodeTest { "testKey1", "testValue1", "testKey2", "123")); var msgData = "[\"value1\",\"value2\"]"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, msgMetaData, msgData); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(msgMetaData) + .data(msgData) + .build(); when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); when(ctxMock.getTenantId()).thenReturn(DUMMY_TENANT_ID); @@ -355,7 +380,12 @@ public class TbGetOriginatorFieldsNodeTest { "testKey1", "testValue1", "testKey2", "123")); var msgData = "[\"value1\",\"value2\"]"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, new DashboardId(UUID.randomUUID()), msgMetaData, msgData); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(new DashboardId(UUID.randomUUID())) + .copyMetaData(msgMetaData) + .data(msgData) + .build(); when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java index 3b6b1eb3c6..9ca0d64307 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java @@ -223,7 +223,12 @@ public class TbGetRelatedAttributeNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); @@ -592,7 +597,12 @@ public class TbGetRelatedAttributeNodeTest { msgData = "{\"temp\":42,\"humidity\":77,\"messageBodyPattern1\":\"targetKey2\",\"messageBodyPattern2\":\"sourceKey3\"}"; } - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, msgMetaData, msgData); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(msgMetaData) + .data(msgData) + .build(); } @RequiredArgsConstructor diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java index 87f273df93..f2f4f572dc 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java @@ -182,7 +182,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { // WHEN-THEN node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); assertThatThrownBy(() -> node.onMsg(ctxMock, msg)) .isInstanceOf(RuntimeException.class) .hasMessage("Interval start should be less than Interval end"); @@ -205,7 +210,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { long endTs = 1719220353000L; TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("mdStartInterval", String.valueOf(startTs)); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, "{\"msgEndInterval\":\"" + endTs + "\"}"); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data("{\"msgEndInterval\":\"" + endTs + "\"}") + .build(); node.onMsg(ctxMock, msg); // THEN @@ -227,7 +237,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(Collections.emptyList())); // WHEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -251,7 +266,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { // WHEN TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("mdTsKey", "humidity"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, "{\"msgTsKey\":\"pressure\"}"); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data("{\"msgTsKey\":\"pressure\"}") + .build(); node.onMsg(ctxMock, msg); // THEN @@ -276,7 +296,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(Collections.emptyList())); // WHEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -306,7 +331,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(Collections.emptyList())); // WHEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -346,7 +376,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(Collections.emptyList())); // WHEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -382,7 +417,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); // WHEN-THEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, "{\"msgStartInterval\":\"start\"}"); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data("{\"msgStartInterval\":\"start\"}") + .build(); assertThatThrownBy(() -> node.onMsg(ctxMock, msg)).isInstanceOf(IllegalArgumentException.class).hasMessage(errorMsg); } @@ -411,7 +451,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(tsKvEntries)); // WHEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -420,7 +465,9 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("temperature", "[{\"ts\":" + (ts - 5) + ",\"value\":23.1},{\"ts\":" + (ts - 4) + ",\"value\":22.4}]"); metaData.putValue("humidity", "[{\"ts\":" + (ts - 4) + ",\"value\":55.5}]"); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metaData); + TbMsg expectedMsg = msg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); } @@ -442,7 +489,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(tsKvEntries)); // WHEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -451,7 +503,9 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("temperature", "\"22.4\""); metaData.putValue("humidity", "\"55.5\""); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metaData); + TbMsg expectedMsg = msg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java index 9f20301b46..7dd69e051e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java @@ -190,7 +190,12 @@ public class TbGetTenantAttributeNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); @@ -398,7 +403,12 @@ public class TbGetTenantAttributeNodeTest { var msgData = "{\"temp\":42,\"humidity\":77,\"messageBodyPattern1\":\"targetKey2\",\"messageBodyPattern2\":\"sourceKey3\"}"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, msgMetaData, msgData); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(msgMetaData) + .data(msgData) + .build(); } @RequiredArgsConstructor diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java index 6649cf49de..4dc37c824e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java @@ -129,7 +129,12 @@ public class TbGetTenantDetailsNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); @@ -289,7 +294,12 @@ public class TbGetTenantDetailsNodeTest { var msgData = "{\"dataKey1\":123,\"dataKey2\":\"dataValue2\"}"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, msgMetaData, msgData); + msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .copyMetaData(msgMetaData) + .data(msgData) + .build(); } private void mockFindTenant() { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java index 8fa8c387fd..0cd8308ded 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java @@ -283,7 +283,12 @@ public class TbMqttNodeTest extends AbstractRuleNodeUpgradeTest { return null; }).given(future).addListener(any()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data(data) + .build(); mqttNode.onMsg(ctxMock, msg); then(ctxMock).should().ack(msg); @@ -322,7 +327,12 @@ public class TbMqttNodeTest extends AbstractRuleNodeUpgradeTest { return null; }).given(future).addListener(any()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, "\"string\""); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data("\"string\"") + .build(); mqttNode.onMsg(ctxMock, msg); then(ctxMock).should(never()).ack(msg); @@ -330,7 +340,9 @@ public class TbMqttNodeTest extends AbstractRuleNodeUpgradeTest { then(mqttClientMock).should().publish(mqttNodeConfig.getTopicPattern(), Unpooled.wrappedBuffer(expectedData.getBytes(StandardCharsets.UTF_8)), MqttQoS.AT_LEAST_ONCE, false); TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("error", RuntimeException.class + ": " + errorMsg); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metaData); + TbMsg expectedMsg = msg.transform() + .metaData(metaData) + .build(); ArgumentCaptor actualMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(ctxMock).should().tellFailure(actualMsgCaptor.capture(), eq(exception)); TbMsg actualMsg = actualMsgCaptor.getValue(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java index 5cee1aa5b3..99cec83cfc 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java @@ -95,7 +95,11 @@ public class DeviceStateTest { when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), any())).thenAnswer(invocationOnMock -> { TbMsgType type = invocationOnMock.getArgument(1); String data = invocationOnMock.getArgument(invocationOnMock.getArguments().length - 1); - return TbMsg.newMsg(type, null, TbMsgMetaData.EMPTY, data); + return TbMsg.newMsg() + .type(type) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(data) + .build(); }); } @@ -107,8 +111,12 @@ public class DeviceStateTest { DeviceId deviceId = new DeviceId(UUID.randomUUID()); DeviceState deviceState = createDeviceState(deviceId, alarmConfig); - TbMsg attributeUpdateMsg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, - deviceId, TbMsgMetaData.EMPTY, "{ \"enabled\": false }"); + TbMsg attributeUpdateMsg = TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data("{ \"enabled\": false }") + .build(); deviceState.process(ctx, attributeUpdateMsg); @@ -116,11 +124,21 @@ public class DeviceStateTest { verify(ctx).enqueueForTellNext(resultMsgCaptor.capture(), eq("Alarm Created")); Alarm alarm = JacksonUtil.fromString(resultMsgCaptor.getValue().getData(), Alarm.class); - deviceState.process(ctx, TbMsg.newMsg(TbMsgType.ALARM_CLEAR, deviceId, TbMsgMetaData.EMPTY, JacksonUtil.toString(alarm))); + deviceState.process(ctx, TbMsg.newMsg() + .type(TbMsgType.ALARM_CLEAR) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.toString(alarm)) + .build()); reset(ctx); String deletedAttributes = "{ \"attributes\": [ \"other\" ] }"; - deviceState.process(ctx, TbMsg.newMsg(TbMsgType.ATTRIBUTES_DELETED, deviceId, TbMsgMetaData.EMPTY, deletedAttributes)); + deviceState.process(ctx, TbMsg.newMsg() + .type(TbMsgType.ATTRIBUTES_DELETED) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(deletedAttributes) + .build()); verify(ctx, never()).enqueueForTellNext(any(), anyString()); } @@ -130,17 +148,31 @@ public class DeviceStateTest { DeviceId deviceId = new DeviceId(UUID.randomUUID()); DeviceState deviceState = createDeviceState(deviceId, alarmConfig); - TbMsg attributeUpdateMsg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, - deviceId, TbMsgMetaData.EMPTY, "{ \"enabled\": false }"); + TbMsg attributeUpdateMsg = TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data("{ \"enabled\": false }") + .build(); deviceState.process(ctx, attributeUpdateMsg); ArgumentCaptor resultMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); verify(ctx).enqueueForTellNext(resultMsgCaptor.capture(), eq("Alarm Created")); Alarm alarm = JacksonUtil.fromString(resultMsgCaptor.getValue().getData(), Alarm.class); - deviceState.process(ctx, TbMsg.newMsg(TbMsgType.ALARM_CLEAR, deviceId, TbMsgMetaData.EMPTY, JacksonUtil.toString(alarm))); - - TbMsg alarmDeleteNotification = TbMsg.newMsg(TbMsgType.ALARM_DELETE, deviceId, TbMsgMetaData.EMPTY, JacksonUtil.toString(alarm)); + deviceState.process(ctx, TbMsg.newMsg() + .type(TbMsgType.ALARM_CLEAR) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.toString(alarm)) + .build()); + + TbMsg alarmDeleteNotification = TbMsg.newMsg() + .type(TbMsgType.ALARM_DELETE) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(JacksonUtil.toString(alarm)) + .build(); assertDoesNotThrow(() -> { deviceState.process(ctx, alarmDeleteNotification); }); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java index 04371c23a0..876b75ecc0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java @@ -127,8 +127,13 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 42); - TbMsg msg = TbMsg.newMsg("123456789", deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data)); + TbMsg msg = TbMsg.newMsg() + .type("123456789") + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); @@ -146,8 +151,13 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 42); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); @@ -198,26 +208,46 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")).thenReturn(null); registerCreateAlarmMock(alarmService.createAlarm(any()), true); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 42); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); verify(ctx).enqueueForTellNext(theMsg, "Alarm Created"); verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); - TbMsg theMsg2 = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, "2"); + TbMsg theMsg2 = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data("2") + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg2); registerCreateAlarmMock(alarmService.updateAlarm(any()), false); Thread.sleep(1); - TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg2 = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); verify(ctx).enqueueForTellNext(theMsg2, "Alarm Updated"); @@ -274,19 +304,34 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm1")).thenReturn(null); registerCreateAlarmMock(alarmService.createAlarm(any()), true); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 42); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); verify(ctx).enqueueForTellNext(theMsg, "Alarm Created"); verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); - TbMsg theMsg2 = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg2 = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg2); AlarmInfo alarm = new AlarmInfo(new Alarm(new AlarmId(UUID.randomUUID()))); @@ -305,8 +350,13 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { when(alarmService.updateAlarm(any())).thenReturn(result); data.put("temperature", 52); - TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg2 = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); verify(ctx).enqueueForTellNext(theMsg2, "Alarm Severity Updated"); @@ -380,14 +430,24 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(attrListListenableFuture); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); Mockito.when(ctx.newMsg(Mockito.any(), Mockito.any(TbMsgType.class), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 21); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -468,14 +528,24 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.any(AttributeScope.class), Mockito.anyString())) .thenReturn(attrListListenableFuture); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 21); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -538,14 +608,24 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFutureWithLess); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -634,14 +714,24 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -655,8 +745,13 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Thread.sleep(halfOfAlarmDelay + 1); - TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg2 = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); @@ -760,14 +855,24 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listNoDurationAttribute); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -781,8 +886,13 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Thread.sleep(halfOfAlarmDelay + 1); - TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg2 = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); @@ -871,14 +981,24 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -886,8 +1006,13 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { verify(ctx, Mockito.never()).tellNext(theMsg, "Alarm Created"); data.put("temperature", 151); - TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg2 = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); @@ -989,14 +1114,24 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listNoDurationAttribute); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -1004,8 +1139,13 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { verify(ctx, Mockito.never()).tellNext(theMsg, "Alarm Created"); data.put("temperature", 151); - TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg2 = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); @@ -1086,14 +1226,24 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -1107,8 +1257,13 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Thread.sleep(halfOfAlarmDelay + 1); - TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg2 = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); @@ -1185,14 +1340,24 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -1268,14 +1433,25 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFutureActiveSchedule); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + + .build(); // Mockito.reset(ctx); @@ -1365,12 +1541,22 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFutureInactiveSchedule); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -1444,14 +1630,24 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(customerId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 25); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -1518,14 +1714,24 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 40); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -1597,19 +1803,29 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { .thenReturn(device); Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFutureWithLess); - Mockito.when(attributesService.find(eq(tenantId), eq(customerId), Mockito.any(AttributeScope.class), Mockito.anyString())) + Mockito.when(attributesService.find(eq(tenantId), eq(customerId), Mockito.any(AttributeScope.class), Mockito.anyString())) .thenReturn(emptyOptionalFuture); Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150L); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -1688,14 +1904,24 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150L); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java index 8ce71f684c..cd7de994b5 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java @@ -147,7 +147,12 @@ public class TbRabbitMqNodeTest { given(ctxMock.getExternalCallExecutor()).willReturn(executor); node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data(data) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().ack(msg); @@ -178,7 +183,12 @@ public class TbRabbitMqNodeTest { given(ctxMock.getExternalCallExecutor()).willReturn(executor); node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should(never()).ack(any(TbMsg.class)); @@ -201,7 +211,12 @@ public class TbRabbitMqNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should(forceAck ? times(1) : never()).ack(any(TbMsg.class)); @@ -212,7 +227,9 @@ public class TbRabbitMqNodeTest { () -> then(ctxMock).should().tellFailure(actualMsg.capture(), throwable.capture()); verifyTellFailure.run(); metaData.putValue("error", RuntimeException.class + ": " + errorMsg); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metaData); + TbMsg expectedMsg = msg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); assertThat(throwable.getValue()).isInstanceOf(RuntimeException.class).hasMessage(errorMsg); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java index 2eb65c1787..b42fa8662f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java @@ -140,11 +140,18 @@ public class TbHttpClientTest { var httpClient = new TbHttpClient(config, eventLoop); - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, new DeviceId(EntityId.NULL_UUID), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); - var successMsg = TbMsg.newMsg( - TbMsgType.POST_TELEMETRY_REQUEST, msg.getOriginator(), - msg.getMetaData(), msg.getData() - ); + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(new DeviceId(EntityId.NULL_UUID)) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); + var successMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msg.getOriginator()) + .copyMetaData(msg.getMetaData()) + .data(msg.getData()) + .build(); var ctx = mock(TbContext.class); when(ctx.transformMsg( diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java index e7d541d587..9f5834cd46 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java @@ -142,7 +142,15 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest { config.setRestEndpointUrlPattern(String.format("http://localhost:%d%s", server.getLocalPort(), path)); initWithConfig(config); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(metaData) + .dataType(TbMsgDataType.JSON) + .data(TbMsg.EMPTY_JSON_OBJECT) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); restNode.onMsg(ctx, msg); assertTrue(latch.await(10, TimeUnit.SECONDS), "Server handled request"); @@ -203,7 +211,15 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest { config.setRestEndpointUrlPattern(String.format("http://localhost:%d%s", server.getLocalPort(), path)); initWithConfig(config); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .copyMetaData(metaData) + .dataType(TbMsgDataType.JSON) + .data(TbMsg.EMPTY_JSON_OBJECT) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); restNode.onMsg(ctx, msg); assertTrue(latch.await(10, TimeUnit.SECONDS), "Server handled request"); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java index c603e111e2..6bf34de3b1 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java @@ -89,7 +89,12 @@ public class TbSendRestApiCallReplyNodeTest { Map metadata = Map.of( requestIdAttribute, requestUUIDStr, serviceIdAttribute, serviceIdStr); - TbMsg msg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, DEVICE_ID, new TbMsgMetaData(metadata), data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.REST_API_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(new TbMsgMetaData(metadata)) + .data(data) + .build(); node.onMsg(ctxMock, msg); @@ -109,7 +114,12 @@ public class TbSendRestApiCallReplyNodeTest { @ParameterizedTest @MethodSource public void givenInvalidRequest_whenOnMsg_thenTellFailure(TbMsgMetaData metaData, String data, String errorMsg) { - TbMsg msg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.REST_API_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data(data) + .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java index 9cab33fcdd..11a29c67ba 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java @@ -92,8 +92,13 @@ public class TbSendRPCReplyNodeTest { public void sendReplyToTransport() { when(ctx.getRpcService()).thenReturn(rpcService); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, getDefaultMetadata(), - TbMsgDataType.JSON, DUMMY_DATA, null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(getDefaultMetadata()) + .dataType(TbMsgDataType.JSON) + .data(DUMMY_DATA) + .build(); node.onMsg(ctx, msg); @@ -111,8 +116,13 @@ public class TbSendRPCReplyNodeTest { TbMsgMetaData defaultMetadata = getDefaultMetadata(); defaultMetadata.putValue(DataConstants.EDGE_ID, UUID.randomUUID().toString()); defaultMetadata.putValue(DataConstants.DEVICE_ID, UUID.randomUUID().toString()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, defaultMetadata, - TbMsgDataType.JSON, DUMMY_DATA, null, null); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(defaultMetadata) + .dataType(TbMsgDataType.JSON) + .data(DUMMY_DATA) + .build(); node.onMsg(ctx, msg); @@ -124,7 +134,12 @@ public class TbSendRPCReplyNodeTest { @EnumSource(EntityType.class) public void testOriginatorEntityTypes(EntityType entityType) { EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, "0f386739-210f-4e23-8739-23f84a172adc"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(entityId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctx, msg); @@ -138,7 +153,12 @@ public class TbSendRPCReplyNodeTest { @ParameterizedTest @MethodSource public void testForAvailabilityOfMetadataAndDataValues(TbMsgMetaData metaData, String errorMsg) { - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, metaData, TbMsg.EMPTY_STRING); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(metaData) + .data(TbMsg.EMPTY_STRING) + .build(); node.onMsg(ctx, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java index e9d25e0d5e..4aa38b230a 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java @@ -107,7 +107,12 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData msgMetadata = new TbMsgMetaData(); msgMetadata.putValue("oneway", mdKeyValue); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, msgMetadata, MSG_DATA); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .copyMetaData(msgMetadata) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); var ruleEngineDeviceRpcRequestCaptor = captureRequest(); @@ -128,7 +133,12 @@ public class TbSendRPCRequestNodeTest { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(RuleEngineDeviceRpcRequest.class); @@ -149,7 +159,12 @@ public class TbSendRPCRequestNodeTest { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - TbMsg msg = TbMsg.newMsg(TbMsgType.TO_SERVER_RPC_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.TO_SERVER_RPC_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -170,7 +185,12 @@ public class TbSendRPCRequestNodeTest { "requestId": 12345 } """; - TbMsg msg = TbMsg.newMsg(TbMsgType.TO_SERVER_RPC_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.TO_SERVER_RPC_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(data) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -185,7 +205,12 @@ public class TbSendRPCRequestNodeTest { String requestUUID = "b795a241-5a30-48fb-92d5-46b864d47130"; TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue("requestUUID", requestUUID); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .copyMetaData(metadata) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -200,7 +225,12 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue("requestUUID", requestUUID); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .copyMetaData(metadata) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -215,7 +245,12 @@ public class TbSendRPCRequestNodeTest { String originServiceId = "service-id-123"; TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue("originServiceId", originServiceId); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .copyMetaData(metadata) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -230,7 +265,12 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue("originServiceId", originServiceId); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .copyMetaData(metadata) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -245,7 +285,12 @@ public class TbSendRPCRequestNodeTest { String expirationTime = "2000000000000"; TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue(DataConstants.EXPIRATION_TIME, expirationTime); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .copyMetaData(metadata) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -260,7 +305,12 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue(DataConstants.EXPIRATION_TIME, expirationTime); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .copyMetaData(metadata) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -275,7 +325,12 @@ public class TbSendRPCRequestNodeTest { Integer retries = 3; TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue(DataConstants.RETRIES, String.valueOf(retries)); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .copyMetaData(metadata) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -290,7 +345,12 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue(DataConstants.RETRIES, retries); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .copyMetaData(metadata) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -303,7 +363,12 @@ public class TbSendRPCRequestNodeTest { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - TbMsg msg = TbMsg.newMsg(msgType, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); + TbMsg msg = TbMsg.newMsg() + .type(msgType) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -322,7 +387,12 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue(DataConstants.PERSISTENT, isPersisted); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .copyMetaData(metadata) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -346,7 +416,12 @@ public class TbSendRPCRequestNodeTest { @Test public void givenRpcResponseWithoutError_whenOnMsg_thenSendsRpcRequest() { - TbMsg outMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg outMsg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); @@ -361,7 +436,12 @@ public class TbSendRPCRequestNodeTest { return null; }).given(rpcServiceMock).sendRpcRequestToDevice(any(RuleEngineDeviceRpcRequest.class), any(Consumer.class)); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().enqueueForTellNext(outMsg, TbNodeConnectionType.SUCCESS); @@ -370,7 +450,12 @@ public class TbSendRPCRequestNodeTest { @Test public void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { - TbMsg outMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg outMsg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); @@ -384,7 +469,12 @@ public class TbSendRPCRequestNodeTest { return null; }).given(rpcServiceMock).sendRpcRequestToDevice(any(RuleEngineDeviceRpcRequest.class), any(Consumer.class)); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().enqueueForTellFailure(outMsg, RpcError.NO_ACTIVE_CONNECTION.name()); @@ -396,7 +486,12 @@ public class TbSendRPCRequestNodeTest { public void givenOriginatorIsNotDevice_whenOnMsg_thenThrowsException(EntityType entityType) { EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, "ac21a1bb-eabf-4463-8313-24bea1f498d9"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(entityId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor throwableCaptor = ArgumentCaptor.forClass(Throwable.class); @@ -409,7 +504,12 @@ public class TbSendRPCRequestNodeTest { @ParameterizedTest @ValueSource(strings = {"method", "params"}) public void givenMethodOrParamsAreNotPresent_whenOnMsg_thenThrowsException(String key) { - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, "{\"" + key + "\": \"value\"}"); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data("{\"" + key + "\": \"value\"}") + .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java index b5cf5533b8..12c59f3873 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java @@ -22,7 +22,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.ArgumentCaptor; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.AbstractRuleNodeUpgradeTest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; @@ -53,6 +52,7 @@ import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.willCallRealMethod; import static org.mockito.Mockito.mock; @@ -164,22 +164,25 @@ class TbMsgAttributesNodeTest extends AbstractRuleNodeUpgradeTest { md.putValue(NOTIFY_DEVICE_METADATA_KEY, mdValue); } // dummy list with one ts kv to pass the empty list check. - var testTbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, md, TbMsg.EMPTY_STRING); + var testTbMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(md) + .data(TbMsg.EMPTY_STRING) + .build(); List testAttrList = List.of(new BaseAttributeKvEntry(0L, new StringDataEntry("testKey", "testValue"))); node.saveAttr(testAttrList, ctxMock, testTbMsg, AttributeScope.SHARED_SCOPE, false); - ArgumentCaptor notifyDeviceCaptor = ArgumentCaptor.forClass(Boolean.class); - - verify(telemetryServiceMock, times(1)).saveAndNotify( - eq(tenantId), eq(deviceId), eq(AttributeScope.SHARED_SCOPE), - eq(testAttrList), notifyDeviceCaptor.capture(), any() - ); - boolean notifyDevice = notifyDeviceCaptor.getValue(); - assertThat(notifyDevice).isEqualTo(expectedArgumentValue); + verify(telemetryServiceMock, times(1)).saveAttributes(assertArg(request -> { + assertThat(request.getTenantId()).isEqualTo(tenantId); + assertThat(request.getEntityId()).isEqualTo(deviceId); + assertThat(request.getScope()).isEqualTo(AttributeScope.SHARED_SCOPE); + assertThat(request.getEntries()).isEqualTo(testAttrList); + assertThat(request.isNotifyDevice()).isEqualTo(expectedArgumentValue); + })); } - // Rule nodes upgrade private static Stream givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() { return Stream.of( diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java index 3d546c80f7..198e12ecb6 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java @@ -21,11 +21,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.msg.TbMsgType; @@ -41,10 +41,9 @@ import java.util.function.Consumer; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.BDDMockito.willAnswer; import static org.mockito.BDDMockito.willReturn; import static org.mockito.Mockito.mock; @@ -78,11 +77,10 @@ public class TbMsgDeleteAttributesNodeTest { willReturn(telemetryService).given(ctx).getTelemetryService(); willAnswer(invocation -> { - TelemetryNodeCallback callBack = invocation.getArgument(5); - callBack.onSuccess(null); + AttributesDeleteRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); return null; - }).given(telemetryService).deleteAndNotify( - any(), any(), any(AttributeScope.class), anyList(), anyBoolean(), any()); + }).given(telemetryService).deleteAttributes(any()); } @AfterEach @@ -139,7 +137,13 @@ public class TbMsgDeleteAttributesNodeTest { } final String data = "{\"TestAttribute_2\": \"humidity\", \"TestAttribute_3\": \"voltage\"}"; - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, deviceId, metaData, data, callback); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(deviceId) + .copyMetaData(metaData) + .data(data) + .callback(callback) + .build(); node.onMsg(ctx, msg); ArgumentCaptor successCaptor = ArgumentCaptor.forClass(Runnable.class); @@ -153,6 +157,8 @@ public class TbMsgDeleteAttributesNodeTest { } verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture()); verify(ctx, never()).tellFailure(any(), any()); - verify(telemetryService, times(1)).deleteAndNotify(any(), any(), any(AttributeScope.class), anyList(), eq(notifyDevice || notifyDeviceMetadata), any()); + verify(telemetryService, times(1)).deleteAttributes(assertArg(request -> { + assertThat(request.isNotifyDevice()).isEqualTo(notifyDevice || notifyDeviceMetadata); + })); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java index 1fb4e9eefd..2cba4b8fb3 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java @@ -25,12 +25,14 @@ import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.ThrowingConsumer; import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.DeviceId; @@ -54,10 +56,8 @@ import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -96,7 +96,12 @@ public class TbMsgTimeseriesNodeTest { @EnumSource(TbMsgType.class) public void givenMsgTypeAndEmptyMsgData_whenOnMsg_thenVerifyFailureMsg(TbMsgType msgType) throws TbNodeException { init(); - TbMsg msg = TbMsg.newMsg(msgType, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + TbMsg msg = TbMsg.newMsg() + .type(msgType) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor throwableCaptor = ArgumentCaptor.forClass(Throwable.class); @@ -122,24 +127,33 @@ public class TbMsgTimeseriesNodeTest { "humidity": 77 } """; - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(data) + .build(); when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); when(ctxMock.getTenantId()).thenReturn(TENANT_ID); doAnswer(invocation -> { - TelemetryNodeCallback callback = invocation.getArgument(5); - callback.onSuccess(null); + TimeseriesSaveRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).saveAndNotify(any(), any(), any(), anyList(), anyLong(), any()); + }).when(telemetryServiceMock).saveTimeseries(any(TimeseriesSaveRequest.class)); node.onMsg(ctxMock, msg); List expectedList = getTsKvEntriesListWithTs(data, System.currentTimeMillis()); - ArgumentCaptor> entryListCaptor = ArgumentCaptor.forClass(List.class); - verify(telemetryServiceMock).saveAndNotify(eq(TENANT_ID), isNull(), eq(DEVICE_ID), entryListCaptor.capture(), - eq(tenantProfileDefaultStorageTtl), any(TelemetryNodeCallback.class)); - assertThat(entryListCaptor.getValue()).usingRecursiveFieldByFieldElementComparatorIgnoringFields("ts") - .containsExactlyElementsOf(expectedList); + verify(telemetryServiceMock).saveTimeseries(assertArg(request -> { + assertThat(request.getTenantId()).isEqualTo(TENANT_ID); + assertThat(request.getCustomerId()).isNull(); + assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); + assertThat(request.getEntries()).usingRecursiveFieldByFieldElementComparatorIgnoringFields("ts").containsExactlyElementsOf(expectedList); + assertThat(request.getTtl()).isEqualTo(tenantProfileDefaultStorageTtl); + assertThat(request.isSaveLatest()).isTrue(); + assertThat(request.getCallback()).isInstanceOf(TelemetryNodeCallback.class); + })); verify(ctxMock).tellSuccess(msg); verifyNoMoreInteractions(ctxMock, telemetryServiceMock); } @@ -159,23 +173,33 @@ public class TbMsgTimeseriesNodeTest { """; long ts = System.currentTimeMillis(); var metadata = Map.of("ts", String.valueOf(ts)); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, new TbMsgMetaData(metadata), data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(new TbMsgMetaData(metadata)) + .data(data) + .build(); when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); when(ctxMock.getTenantId()).thenReturn(TENANT_ID); doAnswer(invocation -> { - TelemetryNodeCallback callback = invocation.getArgument(5); - callback.onSuccess(null); + TimeseriesSaveRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).saveWithoutLatestAndNotify(any(), any(), any(), anyList(), anyLong(), any()); + }).when(telemetryServiceMock).saveTimeseries(any(TimeseriesSaveRequest.class)); node.onMsg(ctxMock, msg); List expectedList = getTsKvEntriesListWithTs(data, ts); - ArgumentCaptor> entryListCaptor = ArgumentCaptor.forClass(List.class); - verify(telemetryServiceMock).saveWithoutLatestAndNotify( - eq(TENANT_ID), isNull(), eq(DEVICE_ID), entryListCaptor.capture(), eq(ttlFromConfig), any(TelemetryNodeCallback.class)); - assertThat(entryListCaptor.getValue()).containsExactlyElementsOf(expectedList); + verify(telemetryServiceMock).saveTimeseries(assertArg(request -> { + assertThat(request.getTenantId()).isEqualTo(TENANT_ID); + assertThat(request.getCustomerId()).isNull(); + assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); + assertThat(request.getEntries()).containsExactlyElementsOf(expectedList); + assertThat(request.getTtl()).isEqualTo(ttlFromConfig); + assertThat(request.isSaveLatest()).isFalse(); + assertThat(request.getCallback()).isInstanceOf(TelemetryNodeCallback.class); + })); verify(ctxMock).tellSuccess(msg); verifyNoMoreInteractions(ctxMock, telemetryServiceMock); } @@ -197,10 +221,22 @@ public class TbMsgTimeseriesNodeTest { """; var metadata = new TbMsgMetaData(); metadata.putValue("TTL", ttlFromMd); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metadata, data); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metadata) + .data(data) + .build(); node.onMsg(ctxMock, msg); - verify(telemetryServiceMock).saveAndNotify(eq(TENANT_ID), isNull(), eq(DEVICE_ID), anyList(), eq(expectedTtl), any(TelemetryNodeCallback.class)); + verify(telemetryServiceMock).saveTimeseries(assertArg(request -> { + assertThat(request.getTenantId()).isEqualTo(TENANT_ID); + assertThat(request.getCustomerId()).isNull(); + assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); + assertThat(request.getTtl()).isEqualTo(expectedTtl); + assertThat(request.isSaveLatest()).isTrue(); + assertThat(request.getCallback()).isInstanceOf(TelemetryNodeCallback.class); + })); } private static Stream givenTtlFromConfigAndTtlFromMd_whenOnMsg_thenVerifyTtl() { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java index 098fe3efda..2a0e0b73f7 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java @@ -176,8 +176,15 @@ public class TbChangeOriginatorNodeTest { Device device = new Device(DEVICE_ID); device.setCustomerId(CUSTOMER_ID); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); - TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, CUSTOMER_ID); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); + TbMsg expectedMsg = msg.transform() + .originator(CUSTOMER_ID) + .build(); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); given(ctxMock.getDeviceService()).willReturn(deviceServiceMock); @@ -199,8 +206,15 @@ public class TbChangeOriginatorNodeTest { public void givenOriginatorSourceIsTenant_whenOnMsg_thenTellSuccess() throws TbNodeException { config.setOriginatorSource(TENANT); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ASSET_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); - TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, TENANT_ID); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(ASSET_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); + TbMsg expectedMsg = msg.transform() + .originator(TENANT_ID) + .build(); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); given(ctxMock.getTenantId()).willReturn(TENANT_ID); @@ -219,7 +233,12 @@ public class TbChangeOriginatorNodeTest { public void givenOriginatorSourceIsRelatedAndNewOriginatorIsNull_whenOnMsg_thenTellFailure() throws TbNodeException { config.setOriginatorSource(RELATED); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ASSET_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(ASSET_ID) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); given(ctxMock.getRelationService()).willReturn(relationServiceMock); @@ -253,8 +272,15 @@ public class TbChangeOriginatorNodeTest { Alarm alarm = new Alarm(alarmId); alarm.setOriginator(DEVICE_ID); - TbMsg msg = TbMsg.newMsg(TbMsgType.ALARM, alarmId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); - TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, DEVICE_ID); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(alarmId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); + TbMsg expectedMsg = msg.transform() + .originator(DEVICE_ID) + .build(); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); given(ctxMock.getAlarmService()).willReturn(alarmServiceMock); @@ -279,8 +305,15 @@ public class TbChangeOriginatorNodeTest { config.setEntityType(EntityType.ASSET.name()); config.setEntityNamePattern(entityNamePattern); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); - TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, ASSET_ID); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data(data) + .build(); + TbMsg expectedMsg = msg.transform() + .originator(ASSET_ID) + .build(); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); given(ctxMock.getAssetService()).willReturn(assetServiceMock); @@ -315,7 +348,12 @@ public class TbChangeOriginatorNodeTest { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("md-name-pattern", "test-asset"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .copyMetaData(metaData) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); given(ctxMock.getAssetService()).willReturn(assetServiceMock); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java index b680981e34..3e214d534d 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java @@ -196,7 +196,13 @@ public class TbCopyKeysNodeTest { "voltageDataValue", "220", "city", "NY" ); - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, new TbMsgMetaData(mdMap), data, callback); + return TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .copyMetaData(new TbMsgMetaData(mdMap)) + .data(data) + .callback(callback) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java index 670b117a2c..036dc5440b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java @@ -173,7 +173,13 @@ public class TbDeleteKeysNodeTest { "voltageDataValue", "220", "city", "NY" ); - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, new TbMsgMetaData(mdMap), data, callback); + return TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .copyMetaData(new TbMsgMetaData(mdMap)) + .data(data) + .callback(callback) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java index 2c09bf555c..ab8c9a44ba 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java @@ -171,6 +171,12 @@ public class TbJsonPathNodeTest { Map mdMap = Map.of("country", "US", "city", "NY" ); - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, new TbMsgMetaData(mdMap), data, callback); + return TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .copyMetaData(new TbMsgMetaData(mdMap)) + .data(data) + .callback(callback) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java index 26fd9081a9..cbff064ec3 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java @@ -103,7 +103,12 @@ public class TbMsgDeduplicationNodeTest extends AbstractRuleNodeUpgradeTest { EntityId originator = (EntityId) (invocationOnMock.getArguments())[2]; TbMsgMetaData metaData = (TbMsgMetaData) (invocationOnMock.getArguments())[3]; String data = (String) (invocationOnMock.getArguments())[4]; - return TbMsg.newMsg(type, originator, metaData.copy(), data); + return TbMsg.newMsg() + .type(type) + .originator(originator) + .copyMetaData(metaData) + .data(data) + .build(); }).when(ctx).newMsg(isNull(), eq(TbMsgType.DEDUPLICATION_TIMEOUT_SELF_MSG), nullable(EntityId.class), any(TbMsgMetaData.class), any(String.class)); node = spy(new TbMsgDeduplicationNode()); config = new TbMsgDeduplicationNodeConfiguration().defaultConfiguration(); @@ -452,12 +457,13 @@ public class TbMsgDeduplicationNodeTest extends AbstractRuleNodeUpgradeTest { dataNode.put("deviceId", deviceId.getId().toString()); TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("ts", String.valueOf(ts)); - return TbMsg.newMsg( - DataConstants.MAIN_QUEUE_NAME, - TbMsgType.POST_TELEMETRY_REQUEST, - deviceId, - metaData, - JacksonUtil.toString(dataNode)); + return TbMsg.newMsg() + .queueName(DataConstants.MAIN_QUEUE_NAME) + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .copyMetaData(metaData) + .data(JacksonUtil.toString(dataNode)) + .build(); } private String getMergedData(List msgs) { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java index 03580920ce..0c7f291db1 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java @@ -188,6 +188,12 @@ public class TbRenameKeysNodeTest { "country", "US", "city", "NY" ); - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, new TbMsgMetaData(mdMap), data, callback); + return TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .copyMetaData(new TbMsgMetaData(mdMap)) + .data(data) + .callback(callback) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java index 8abf83fa48..895ae96db8 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java @@ -132,6 +132,12 @@ public class TbSplitArrayMsgNodeTest { "country", "US", "city", "NY" ); - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, new TbMsgMetaData(mdMap), data, callback); + return TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .copyMetaData(new TbMsgMetaData(mdMap)) + .data(data) + .callback(callback) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java index 98ecc237b6..72f893aa86 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java @@ -61,8 +61,24 @@ public class TbTransformMsgNodeTest { RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON,rawJson, ruleChainId, ruleNodeId); - TbMsg transformedMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON, "{new}", ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(null) + .copyMetaData(metaData) + .dataType(TbMsgDataType.JSON) + .data(rawJson) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); + TbMsg transformedMsg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(null) + .copyMetaData(metaData) + .dataType(TbMsgDataType.JSON) + .data("{new}") + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); when(scriptEngine.executeUpdateAsync(msg)).thenReturn(Futures.immediateFuture(Collections.singletonList(transformedMsg))); node.onMsg(ctx, msg); @@ -81,7 +97,15 @@ public class TbTransformMsgNodeTest { RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(null) + .copyMetaData(metaData) + .dataType(TbMsgDataType.JSON) + .data(rawJson) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); when(scriptEngine.executeUpdateAsync(msg)).thenReturn(Futures.immediateFailedFuture(new IllegalStateException("error"))); node.onMsg(ctx, msg); diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index 2c1c0550e1..0c7ff4e43e 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -198,6 +198,30 @@ coap: # - A value between 0 and <= 4: SingleNodeConnectionIdGenerator is used # - A value that are > 4: MultiNodeConnectionIdGenerator is used connection_id_length: "${COAP_DTLS_CONNECTION_ID_LENGTH:}" + # Specify the MTU (Maximum Transmission Unit). + # Should be used if LAN MTU is not used, e.g. if IP tunnels are used or if the client uses a smaller value than the LAN MTU. + # Default = 1024 + # Minimum value = 64 + # If set to 0 - LAN MTU is used. + max_transmission_unit: "${COAP_DTLS_MAX_TRANSMISSION_UNIT:1024}" + # DTLS maximum fragment length (RFC 6066, Section 4). + # Default = 1024 + # Possible values: 512, 1024, 2048, 4096. + # If set to 0, the default maximum fragment size of 2^14 bytes (16,384 bytes) is used. + # Without this extension, TLS specifies a fixed maximum plaintext fragment length of 2^14 bytes. + # It may be desirable for constrained clients to negotiate a smaller maximum fragment length due to memory limitations or bandwidth limitations. + # In order to negotiate smaller maximum fragment lengths, + # clients MAY include an extension of type "max_fragment_length" in the (extended) client hello. + # The "extension_data" field of this extension SHALL contain: + # enum { + # 2^9(1) == 512, + # 2^10(2) == 1024, + # 2^11(3) == 2048, + # 2^12(4) == 4096, + # (255) + # } MaxFragmentLength; + # TLS already requires clients and servers to support fragmentation of handshake messages. + max_fragment_length: "${COAP_DTLS_MAX_FRAGMENT_LENGTH:1024}" # Server DTLS credentials credentials: # Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore) diff --git a/ui-ngx/src/app/core/services/dashboard-utils.service.ts b/ui-ngx/src/app/core/services/dashboard-utils.service.ts index 235924f98f..7e5009f199 100644 --- a/ui-ngx/src/app/core/services/dashboard-utils.service.ts +++ b/ui-ngx/src/app/core/services/dashboard-utils.service.ts @@ -60,6 +60,7 @@ import { BackgroundType, colorBackground, isBackgroundSettings } from '@shared/m import { MediaBreakpoints } from '@shared/models/constants'; import { TranslateService } from '@ngx-translate/core'; import { DashboardPageLayout } from '@home/components/dashboard-page/dashboard-page.models'; +import { maxGridsterCol, maxGridsterRow } from '@home/models/dashboard-component.models'; @Injectable({ providedIn: 'root' @@ -682,6 +683,10 @@ export class DashboardUtilsService { if (row > -1 && column > - 1) { widgetLayout.row = row; widgetLayout.col = column; + if (this.hasWidgetCollision(widgetLayout.row, widgetLayout.col, + widgetLayout.sizeX, widgetLayout.sizeY, Object.values(layout.widgets))) { + this.widgetPossiblePosition(widgetLayout, layout); + } } else { row = 0; for (const w of Object.keys(layout.widgets)) { @@ -703,6 +708,60 @@ export class DashboardUtilsService { layout.widgets[widget.id] = widgetLayout; } + private widgetPossiblePosition(widgetLayout: WidgetLayout, layout: DashboardLayout) { + let bestRow = 0; + let bestCol = 0; + + let maxCol = layout.gridSettings.minColumns || layout.gridSettings.columns || 0; + let maxRow = 0; + + const widgetLayouts = Object.values(layout.widgets); + + widgetLayouts.forEach(widget => { + maxCol = Math.max(maxCol, widget.col + widget.sizeX); + maxRow = Math.max(maxRow, widget.row + widget.sizeY); + }) + + for (; bestRow < maxRow; bestRow++) { + for (bestCol = 0; bestCol < maxCol; bestCol++) { + if (!this.hasWidgetCollision(bestRow, bestCol, widgetLayout.sizeX, widgetLayout.sizeY, widgetLayouts)) { + widgetLayout.row = bestRow; + widgetLayout.col = bestCol; + return; + } + } + } + const canAddToRows = maxGridsterRow >= maxRow + bestRow; + const canAddToColumns = maxGridsterCol >= maxCol + bestCol; + const addToRows = bestRow <= bestCol && canAddToRows; + if (!addToRows && canAddToColumns) { + widgetLayout.col = maxCol; + widgetLayout.row = 0; + } else if (canAddToRows) { + widgetLayout.row = maxRow; + widgetLayout.col = 0; + } + } + + private hasWidgetCollision(row: number, col: number, sizeX: number, sizeY: number, widgetLayouts: WidgetLayout[]) { + const left = col; + const right = col + sizeX; + const top = row; + const bottom = row + sizeY; + + for (const widget of widgetLayouts) { + const left2 = widget.col; + const right2 = widget.col + widget.sizeX; + const top2 = widget.row; + const bottom2 = widget.row + widget.sizeY; + + if (left < right2 && right > left2 && top < bottom2 && bottom > top2) { + return true; + } + } + return false; + } + public removeWidgetFromLayout(dashboard: Dashboard, targetState: string, targetLayout: DashboardLayoutId, diff --git a/ui-ngx/src/app/core/services/item-buffer.service.ts b/ui-ngx/src/app/core/services/item-buffer.service.ts index c77784c4f5..c43d1ef35b 100644 --- a/ui-ngx/src/app/core/services/item-buffer.service.ts +++ b/ui-ngx/src/app/core/services/item-buffer.service.ts @@ -305,7 +305,11 @@ export class ItemBufferService { connectors: [], additionalInfo: origNode.additionalInfo, configuration: origNode.configuration, - debugMode: origNode.debugMode, + debugSettings: { + failuresEnabled: origNode.debugSettings?.failuresEnabled, + allEnabled: origNode.debugSettings?.allEnabled || origNode.debugSettings?.allEnabledUntil > new Date().getTime(), + allEnabledUntil: 0 + }, x: origNode.x, y: origNode.y, name: origNode.name, diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts index 609dbb8e7a..887a2e2e0d 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts @@ -16,6 +16,7 @@ import { Component, + DestroyRef, ElementRef, forwardRef, Inject, @@ -46,6 +47,7 @@ import { EntityType } from '@shared/models/entity-type.models'; import { fromEvent, Subscription } from 'rxjs'; import { POSITION_MAP } from '@shared/models/overlay.models'; import { UtilsService } from '@core/services/utils.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export const ALARM_FILTER_CONFIG_DATA = new InjectionToken('AlarmFilterConfigData'); @@ -129,7 +131,8 @@ export class AlarmFilterConfigComponent implements OnInit, OnDestroy, ControlVal private overlay: Overlay, private nativeElement: ElementRef, private viewContainerRef: ViewContainerRef, - private utils: UtilsService) { + private utils: UtilsService, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -149,7 +152,9 @@ export class AlarmFilterConfigComponent implements OnInit, OnDestroy, ControlVal searchPropagatedAlarms: [false, []], assigneeId: [AlarmAssigneeOption.noAssignee, []] }); - this.alarmFilterConfigForm.valueChanges.subscribe( + this.alarmFilterConfigForm.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => { if (!this.buttonMode) { this.alarmConfigUpdated(this.alarmFilterConfigForm.value); diff --git a/ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.ts b/ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.ts index 15791f9588..5a311a3392 100644 --- a/ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; +import { Component, DestroyRef, Inject, OnInit, SkipSelf } from '@angular/core'; import { ErrorStateMatcher } from '@angular/material/core'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; @@ -40,6 +40,7 @@ import { DialogService } from '@core/services/dialog.service'; import { deepClone, isUndefined } from '@core/utils'; import { EntityAliasDialogComponent, EntityAliasDialogData } from './entity-alias-dialog.component'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface EntityAliasesDialogData { entityAliases: EntityAliases; @@ -80,7 +81,8 @@ export class EntityAliasesDialogComponent extends DialogComponent { + aliasFormControl.get('resolveMultiple').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((resolveMultiple: boolean) => { (aliasFormControl.get('filter').value as EntityAliasFilter).resolveMultiple = resolveMultiple; }); return aliasFormControl; diff --git a/ui-ngx/src/app/modules/home/components/attribute/add-widget-to-dashboard-dialog.component.ts b/ui-ngx/src/app/modules/home/components/attribute/add-widget-to-dashboard-dialog.component.ts index 9935ea5bbf..b745b1347e 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/add-widget-to-dashboard-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/attribute/add-widget-to-dashboard-dialog.component.ts @@ -40,6 +40,7 @@ import { AliasesInfo } from '@shared/models/alias.models'; import { ItemBufferService } from '@core/services/item-buffer.service'; import { StateObject } from '@core/api/widget-api.models'; import { FiltersInfo } from '@shared/models/query/query.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface AddWidgetToDashboardDialogData { entityId: EntityId; @@ -83,7 +84,9 @@ export class AddWidgetToDashboardDialogComponent extends } ); - this.addWidgetFormGroup.get('addToDashboardType').valueChanges.subscribe( + this.addWidgetFormGroup.get('addToDashboardType').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe( (addToDashboardType: number) => { if (addToDashboardType === 0) { this.addWidgetFormGroup.get('dashboardId').setValidators([Validators.required]); diff --git a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts index 881961ed34..de405b0f0d 100644 --- a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts @@ -105,7 +105,7 @@ export class AuditLogDetailsDialogComponent extends DialogComponent 0) { const lines = content.split('\n'); - newHeight = 17 * lines.length + 16; + newHeight = 18 * lines.length + 16; let maxLineLength = 0; lines.forEach((row) => { const line = row.replace(/\t/g, ' ').replace(/\n/g, ''); diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-image-dialog.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-image-dialog.component.ts index bff5839bcb..35249f9200 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-image-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-image-dialog.component.ts @@ -28,6 +28,7 @@ import html2canvas from 'html2canvas'; import { map, share } from 'rxjs/operators'; import { BehaviorSubject, from } from 'rxjs'; import { isNumber } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface DashboardImageDialogData { dashboardId: DashboardId; @@ -84,7 +85,9 @@ export class DashboardImageDialogComponent extends DialogComponent { this.updateImage(newImage); } diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts index 2e22975f7e..4a213ce263 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts @@ -874,7 +874,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC public exportDashboard($event: Event) { if ($event) { - $event.stopPropagation(); + $event.preventDefault(); } this.importExport.exportDashboard(this.currentDashboardId); } diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/states/dashboard-state-dialog.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/states/dashboard-state-dialog.component.ts index 223dfdbb54..051f82445f 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/states/dashboard-state-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/states/dashboard-state-dialog.component.ts @@ -34,6 +34,7 @@ import { DashboardState } from '@app/shared/models/dashboard.models'; import { DashboardStateInfo } from '@home/components/dashboard-page/states/manage-dashboard-states-dialog.component.models'; import { TranslateService } from '@ngx-translate/core'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface DashboardStateDialogData { states: {[id: string]: DashboardState }; @@ -89,11 +90,15 @@ export class DashboardStateDialogComponent extends root: [this.state.root, []], }); - this.stateFormGroup.get('name').valueChanges.subscribe((name: string) => { + this.stateFormGroup.get('name').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((name: string) => { this.checkStateName(name); }); - this.stateFormGroup.get('id').valueChanges.subscribe((id: string) => { + this.stateFormGroup.get('id').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((id: string) => { this.stateIdTouched = true; }); } diff --git a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts index 7169e4a20b..207958758b 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts @@ -41,7 +41,9 @@ import { DashboardCallbacks, DashboardWidget, DashboardWidgets, - IDashboardComponent + IDashboardComponent, + maxGridsterCol, + maxGridsterRow } from '../../models/dashboard-component.models'; import { ReplaySubject, Subject, Subscription } from 'rxjs'; import { WidgetLayout, WidgetLayouts } from '@shared/models/dashboard.models'; @@ -231,10 +233,10 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo disableAutoPositionOnConflict: false, pushItems: false, swap: false, - maxRows: 3000, + maxRows: maxGridsterRow, minCols: this.columns ? this.columns : 24, setGridSize: this.setGridSize, - maxCols: 3000, + maxCols: maxGridsterCol, maxItemCols: 1000, maxItemRows: 1000, maxItemArea: 1000000, @@ -253,7 +255,10 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo draggable: { enabled: this.isEdit && !this.isEditingWidget, delayStart: 100, - stop: (_, itemComponent) => {(itemComponent.item as DashboardWidget).updatePosition(itemComponent.$item.x, itemComponent.$item.y);} + stop: (_, itemComponent) => { + (itemComponent.item as DashboardWidget).updatePosition(itemComponent.$item.x, itemComponent.$item.y); + this.notifyGridsterOptionsChanged(); + } }, itemChangeCallback: () => this.dashboardWidgets.sortWidgets(), itemInitCallback: (_, itemComponent) => { diff --git a/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.ts b/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.ts index b43aaf6100..ec85e1bf40 100644 --- a/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.ts +++ b/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.ts @@ -17,6 +17,7 @@ import { ChangeDetectorRef, Component, + DestroyRef, ElementRef, forwardRef, Inject, @@ -38,6 +39,7 @@ import { DeviceInfoFilter } from '@shared/models/device.models'; import { isDefinedAndNotNull } from '@core/utils'; import { EntityInfoData } from '@shared/models/entity.models'; import { DeviceProfileService } from '@core/http/device-profile.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export const DEVICE_FILTER_CONFIG_DATA = new InjectionToken('DeviceFilterConfigData'); @@ -96,7 +98,8 @@ export class DeviceInfoFilterComponent implements OnInit, OnDestroy, ControlValu private nativeElement: ElementRef, private viewContainerRef: ViewContainerRef, private deviceProfileService: DeviceProfileService, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -108,7 +111,9 @@ export class DeviceInfoFilterComponent implements OnInit, OnDestroy, ControlValu deviceProfileId: [null, []], active: ['', []] }); - this.deviceInfoFilterForm.valueChanges.subscribe( + this.deviceInfoFilterForm.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => { this.updateValidators(); if (!this.buttonMode) { diff --git a/ui-ngx/src/app/modules/home/components/entity/contact-based.component.ts b/ui-ngx/src/app/modules/home/components/entity/contact-based.component.ts index 2d08fd9f50..3e23cbf8b1 100644 --- a/ui-ngx/src/app/modules/home/components/entity/contact-based.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/contact-based.component.ts @@ -18,15 +18,18 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { UntypedFormBuilder, UntypedFormGroup, ValidatorFn, Validators } from '@angular/forms'; import { ContactBased } from '@shared/models/contact-based.model'; -import { AfterViewInit, ChangeDetectorRef, Directive } from '@angular/core'; +import { AfterViewInit, ChangeDetectorRef, DestroyRef, Directive, inject } from '@angular/core'; import { HasId } from '@shared/models/base-data'; import { EntityComponent } from './entity.component'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { CountryData } from '@shared/models/country.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Directive() export abstract class ContactBasedComponent> extends EntityComponent implements AfterViewInit { + protected destroyRef = inject(DestroyRef); + protected constructor(protected store: Store, protected fb: UntypedFormBuilder, protected entityValue: T, @@ -65,7 +68,9 @@ export abstract class ContactBasedComponent> exten } ngAfterViewInit() { - this.entityForm.get('country').valueChanges.subscribe( + this.entityForm.get('country').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( (country) => { this.entityForm.get('zip').setValidators(this.zipValidators(country)); this.entityForm.get('zip').updateValueAndValidity({onlySelf: true}); diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html similarity index 66% rename from ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html rename to ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html index b779ae1e4c..8bc399974c 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html @@ -23,10 +23,9 @@ [disabled]="disabled" (click)="openDebugStrategyPanel($event, matButton)"> bug_report - common.disabled - debug-config.all - - {{ !allEnabled ? (allEnabledUntil | durationLeft) : ('debug-config.min' | translate: { number: maxDebugModeDurationMinutes }) }} - - debug-config.failures + @if (isDebugAllActive$ | async) { + {{ (allEnabled$ | async) === false ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) }} + } @else { + {{ (failuresEnabled ? 'debug-settings.failures' : 'common.disabled') | translate }} + } diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts similarity index 67% rename from ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts rename to ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts index dafda75c0b..8579d8df12 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts @@ -14,26 +14,34 @@ /// limitations under the License. /// -import { ChangeDetectionStrategy, Component, forwardRef, Input, Renderer2, ViewContainerRef } from '@angular/core'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + forwardRef, + Input, + Renderer2, + ViewContainerRef +} from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@shared/shared.module'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; import { TbPopoverService } from '@shared/components/popover.service'; import { MatButton } from '@angular/material/button'; -import { DebugSettingsPanelComponent } from './debug-settings-panel.component'; +import { EntityDebugSettingsPanelComponent } from './entity-debug-settings-panel.component'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { of, shareReplay, timer } from 'rxjs'; -import { SECOND } from '@shared/models/time/time.models'; -import { DebugSettings } from '@shared/models/entity.models'; -import { map, startWith, switchMap, takeWhile } from 'rxjs/operators'; +import { BehaviorSubject, of, shareReplay, timer } from 'rxjs'; +import { SECOND, MINUTE } from '@shared/models/time/time.models'; +import { EntityDebugSettings } from '@shared/models/entity.models'; +import { map, switchMap, takeWhile } from 'rxjs/operators'; import { getCurrentAuthState } from '@core/auth/auth.selectors'; import { AppState } from '@core/core.state'; import { Store } from '@ngrx/store'; import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ - selector: 'tb-debug-settings-button', - templateUrl: './debug-settings-button.component.html', + selector: 'tb-entity-debug-settings-button', + templateUrl: './entity-debug-settings-button.component.html', standalone: true, imports: [ CommonModule, @@ -43,15 +51,16 @@ import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/f providers: [ { provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => DebugSettingsButtonComponent), + useExisting: forwardRef(() => EntityDebugSettingsButtonComponent), multi: true }, ], changeDetection: ChangeDetectionStrategy.OnPush }) -export class DebugSettingsButtonComponent implements ControlValueAccessor { +export class EntityDebugSettingsButtonComponent implements ControlValueAccessor { @Input() debugLimitsConfiguration: string; + @Input() entityLabel: string; debugSettingsFormGroup = this.fb.group({ failuresEnabled: [false], @@ -60,11 +69,12 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { }); disabled = false; + private allEnabledSubject = new BehaviorSubject(false); + allEnabled$ = this.allEnabledSubject.asObservable(); - isDebugAllActive$ = this.debugSettingsFormGroup.get('allEnabled').valueChanges.pipe( - startWith(null), - switchMap(() => { - if (this.allEnabled) { + isDebugAllActive$ = this.allEnabled$.pipe( + switchMap((value) => { + if (value) { return of(true); } else { return timer(0, SECOND).pipe( @@ -77,31 +87,32 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { shareReplay(1) ); - readonly maxDebugModeDurationMinutes = getCurrentAuthState(this.store).maxDebugModeDurationMinutes; + readonly maxDebugModeDuration = getCurrentAuthState(this.store).maxDebugModeDurationMinutes * MINUTE; - private propagateChange: (settings: DebugSettings) => void = () => {}; + private propagateChange: (settings: EntityDebugSettings) => void = () => {}; constructor(private popoverService: TbPopoverService, private renderer: Renderer2, private store: Store, private viewContainerRef: ViewContainerRef, private fb: FormBuilder, + private cd : ChangeDetectorRef, ) { this.debugSettingsFormGroup.valueChanges.pipe( takeUntilDestroyed() ).subscribe(value => { this.propagateChange(value); - }) + }); + + this.debugSettingsFormGroup.get('allEnabled').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe(value => this.allEnabledSubject.next(value)); } get failuresEnabled(): boolean { return this.debugSettingsFormGroup.get('failuresEnabled').value; } - get allEnabled(): boolean { - return this.debugSettingsFormGroup.get('allEnabled').value; - } - get allEnabledUntil(): number { return this.debugSettingsFormGroup.get('allEnabledUntil').value; } @@ -117,30 +128,33 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { this.popoverService.hidePopover(trigger); } else { const debugStrategyPopover = this.popoverService.displayPopover(trigger, this.renderer, - this.viewContainerRef, DebugSettingsPanelComponent, 'bottom', true, null, + this.viewContainerRef, EntityDebugSettingsPanelComponent, 'bottom', true, null, { ...debugSettings, - maxDebugModeDurationMinutes: this.maxDebugModeDurationMinutes, - debugLimitsConfiguration: this.debugLimitsConfiguration + maxDebugModeDuration: this.maxDebugModeDuration, + debugLimitsConfiguration: this.debugLimitsConfiguration, + entityLabel: this.entityLabel }, {}, {}, {}, true); debugStrategyPopover.tbComponentRef.instance.popover = debugStrategyPopover; - debugStrategyPopover.tbComponentRef.instance.onSettingsApplied.subscribe((settings: DebugSettings) => { + debugStrategyPopover.tbComponentRef.instance.onSettingsApplied.subscribe((settings: EntityDebugSettings) => { this.debugSettingsFormGroup.patchValue(settings); + this.cd.markForCheck(); debugStrategyPopover.hide(); }); } } - registerOnChange(fn: (settings: DebugSettings) => void): void { + registerOnChange(fn: (settings: EntityDebugSettings) => void): void { this.propagateChange = fn; } registerOnTouched(_: () => void): void {} - writeValue(settings: DebugSettings): void { + writeValue(settings: EntityDebugSettings): void { this.debugSettingsFormGroup.patchValue(settings, {emitEvent: false}); + this.allEnabledSubject.next(settings?.allEnabled); this.debugSettingsFormGroup.get('allEnabled').updateValueAndValidity({onlySelf: true}); } diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.html similarity index 68% rename from ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html rename to ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.html index 5fe0aa2546..7576d4b2fe 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.html @@ -16,26 +16,26 @@ -->

-
debug-config.label
+
debug-settings.label
@if (debugLimitsConfiguration) { - {{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, sec: maxTimeFrameSec } }} + {{ 'debug-settings.hint.main-limited' | translate: { entity: entityLabel ?? ('debug-settings.entity' | translate), msg: maxMessagesCount, time: (maxTimeFrameDuration | milliSecondsToTimeString: true : true) } }} } @else { - {{ 'debug-config.hint.main' | translate }} + {{ 'debug-settings.hint.main' | translate }} }
-
- {{ 'debug-config.on-failure' | translate }} +
+ {{ 'debug-settings.on-failure' | translate }}
-
- {{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : ('debug-config.min' | translate: { number: maxDebugModeDurationMinutes }) } }} +
+ {{ 'debug-settings.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) } }}
diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.ts similarity index 85% rename from ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts rename to ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.ts index a9e237245c..989d13d9be 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.ts @@ -32,12 +32,12 @@ import { SECOND } from '@shared/models/time/time.models'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; import { of, shareReplay, timer } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { DebugSettings } from '@shared/models/entity.models'; +import { EntityDebugSettings } from '@shared/models/entity.models'; import { distinctUntilChanged, map, startWith, switchMap, takeWhile } from 'rxjs/operators'; @Component({ - selector: 'tb-debug-settings-panel', - templateUrl: './debug-settings-panel.component.html', + selector: 'tb-entity-debug-settings-panel', + templateUrl: './entity-debug-settings-panel.component.html', standalone: true, imports: [ SharedModule, @@ -46,20 +46,21 @@ import { distinctUntilChanged, map, startWith, switchMap, takeWhile } from 'rxjs ], changeDetection: ChangeDetectionStrategy.OnPush }) -export class DebugSettingsPanelComponent extends PageComponent implements OnInit { +export class EntityDebugSettingsPanelComponent extends PageComponent implements OnInit { - @Input() popover: TbPopoverComponent; + @Input() popover: TbPopoverComponent; @Input({ transform: booleanAttribute }) failuresEnabled = false; @Input({ transform: booleanAttribute }) allEnabled = false; + @Input() entityLabel: string; @Input() allEnabledUntil = 0; - @Input() maxDebugModeDurationMinutes: number; + @Input() maxDebugModeDuration: number; @Input() debugLimitsConfiguration: string; onFailuresControl = this.fb.control(false); debugAllControl = this.fb.control(false); maxMessagesCount: string; - maxTimeFrameSec: string; + maxTimeFrameDuration: number; initialAllEnabled: boolean; isDebugAllActive$ = this.debugAllControl.valueChanges.pipe( @@ -78,7 +79,7 @@ export class DebugSettingsPanelComponent extends PageComponent implements OnInit shareReplay(1), ); - onSettingsApplied = new EventEmitter(); + onSettingsApplied = new EventEmitter(); constructor(private fb: FormBuilder, private cd: ChangeDetectorRef) { @@ -99,7 +100,7 @@ export class DebugSettingsPanelComponent extends PageComponent implements OnInit ngOnInit(): void { this.maxMessagesCount = this.debugLimitsConfiguration?.split(':')[0]; - this.maxTimeFrameSec = this.debugLimitsConfiguration?.split(':')[1]; + this.maxTimeFrameDuration = parseInt(this.debugLimitsConfiguration?.split(':')[1]) * SECOND; this.onFailuresControl.patchValue(this.failuresEnabled); this.debugAllControl.patchValue(this.allEnabled); this.initialAllEnabled = this.allEnabled || this.allEnabledUntil > new Date().getTime(); @@ -128,6 +129,7 @@ export class DebugSettingsPanelComponent extends PageComponent implements OnInit onReset(): void { this.debugAllControl.patchValue(true); + this.debugAllControl.markAsDirty(); this.allEnabledUntil = 0; this.cd.markForCheck(); } diff --git a/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts b/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts index cb19815c5b..1361062f3a 100644 --- a/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -32,6 +32,7 @@ import { EntityKeyValueType, FilterPredicateType } from '@shared/models/query/query.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-boolean-filter-predicate', @@ -68,7 +69,8 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, Va private propagateChange = null; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -76,7 +78,9 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, Va operation: [BooleanOperation.EQUAL, [Validators.required]], value: [null, [Validators.required]] }); - this.booleanFilterPredicateFormGroup.valueChanges.subscribe(() => { + this.booleanFilterPredicateFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html index 2e9f12da2b..fd1cf2ea27 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html @@ -40,13 +40,13 @@
-
{{ complexOperationTranslations.get(operation) | translate }}
-
+
{{ hintText | translate }}
-
+
diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts index 2f3d29df91..2192b33583 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -35,6 +35,7 @@ import { inheritModeForDynamicValueSourceType, StringOperation } from '@shared/models/query/query.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-filter-predicate-value', @@ -113,7 +114,8 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, Vali private propagateChange = null; private propagateChangePending = false; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -147,7 +149,9 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, Vali } ) }); - this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceType').valueChanges.subscribe( + this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( (sourceType) => { if (!sourceType) { this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceAttribute').patchValue(null, {emitEvent: false}); @@ -156,7 +160,9 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, Vali } ); this.updateValidationDynamicMode(); - this.filterPredicateValueFormGroup.valueChanges.subscribe(() => { + this.filterPredicateValueFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate.component.ts b/ui-ngx/src/app/modules/home/components/filter/filter-predicate.component.ts index d7bc07af2c..80a8d0b9ee 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -26,6 +26,7 @@ import { Validators } from '@angular/forms'; import { EntityKeyValueType, FilterPredicateType, KeyFilterPredicateInfo } from '@shared/models/query/query.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-filter-predicate', @@ -66,7 +67,8 @@ export class FilterPredicateComponent implements ControlValueAccessor, Validator private propagateChange = null; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -74,7 +76,9 @@ export class FilterPredicateComponent implements ControlValueAccessor, Validator predicate: [null, [Validators.required]], userInfo: [null, []] }); - this.filterPredicateFormGroup.valueChanges.subscribe(() => { + this.filterPredicateFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.ts b/ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.ts index 7d2c4f7e7c..de582ba8c3 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.ts @@ -29,6 +29,7 @@ import { StringOperation } from '@shared/models/query/query.models'; import { TranslateService } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface FilterUserInfoDialogData { key: string; @@ -73,7 +74,9 @@ export class FilterUserInfoDialogComponent extends ); this.onAutogeneratedLabelChange(); if (!this.data.readonly) { - this.filterUserInfoFormGroup.get('autogeneratedLabel').valueChanges.subscribe(() => { + this.filterUserInfoFormGroup.get('autogeneratedLabel').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe(() => { this.onAutogeneratedLabelChange(); }); } else { diff --git a/ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.ts b/ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.ts index 8210e7b06c..c1d36a7ee4 100644 --- a/ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -32,6 +32,7 @@ import { NumericOperation, numericOperationTranslationMap, } from '@shared/models/query/query.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-numeric-filter-predicate', @@ -70,7 +71,8 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, Va private propagateChange = null; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -78,7 +80,9 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, Va operation: [NumericOperation.EQUAL, [Validators.required]], value: [null, [Validators.required]] }); - this.numericFilterPredicateFormGroup.valueChanges.subscribe(() => { + this.numericFilterPredicateFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.ts b/ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.ts index 2e6d80029a..7cd62c41e9 100644 --- a/ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -32,6 +32,7 @@ import { StringOperation, stringOperationTranslationMap } from '@shared/models/query/query.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-string-filter-predicate', @@ -68,7 +69,8 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, Val private propagateChange = null; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -77,7 +79,9 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, Val value: [null, [Validators.required]], ignoreCase: [false] }); - this.stringFilterPredicateFormGroup.valueChanges.subscribe(() => { + this.stringFilterPredicateFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.ts b/ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.ts index ee5a780eff..0072b68d08 100644 --- a/ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; +import { Component, DestroyRef, Inject, OnInit, SkipSelf } from '@angular/core'; import { ErrorStateMatcher } from '@angular/material/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; @@ -38,6 +38,7 @@ import { UserFilterInputInfo } from '@shared/models/query/query.models'; import { isDefinedAndNotNull } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface UserFilterDialogData { filter: Filter; @@ -66,7 +67,8 @@ export class UserFilterDialogComponent extends DialogComponent, private fb: UntypedFormBuilder, - public translate: TranslateService) { + public translate: TranslateService, + private destroyRef: DestroyRef) { super(store, router, dialogRef); this.filter = data.filter; const userInputs = filterToUserFilterInfoList(this.filter, translate); @@ -91,7 +93,9 @@ export class UserFilterDialogComponent extends DialogComponent { + userInputControl.get('value').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(userValue => { (userInput.info.keyFilterPredicate as any).value.userValue = userValue; }); return userInputControl; diff --git a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts index cee7426e26..83bfdf77a6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts @@ -46,6 +46,7 @@ import { Observable } from 'rxjs'; import { BreakpointObserver } from '@angular/cdk/layout'; import { MediaBreakpoints } from '@shared/models/constants'; import { map } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface AddDeviceProfileDialogData { deviceProfileName: string; @@ -125,7 +126,9 @@ export class AddDeviceProfileDialogComponent extends [Validators.required]] } ); - this.transportConfigFormGroup.get('transportType').valueChanges.subscribe(() => { + this.transportConfigFormGroup.get('transportType').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe(() => { this.deviceProfileTransportTypeChanged(); }); diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-duration-predicate-value.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-duration-predicate-value.component.ts index ad821389cd..2063ca1522 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-duration-predicate-value.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-duration-predicate-value.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { DynamicValueSourceType, @@ -24,6 +24,7 @@ import { inheritModeForDynamicValueSourceType } from '@shared/models/query/query.models'; import { AlarmConditionType } from '@shared/models/device.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-alarm-duration-predicate-value', @@ -76,7 +77,8 @@ export class AlarmDurationPredicateValueComponent implements ControlValueAccesso private propagateChange = null; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -90,7 +92,9 @@ export class AlarmDurationPredicateValueComponent implements ControlValueAccesso } ) }); - this.alarmDurationPredicateValueFormGroup.get('dynamicValue').get('sourceType').valueChanges.subscribe( + this.alarmDurationPredicateValueFormGroup.get('dynamicValue').get('sourceType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( (sourceType) => { if (!sourceType) { this.alarmDurationPredicateValueFormGroup.get('dynamicValue').get('sourceAttribute').patchValue(null, {emitEvent: false}); @@ -98,7 +102,9 @@ export class AlarmDurationPredicateValueComponent implements ControlValueAccesso this.updateShowInheritMode(sourceType); } ); - this.alarmDurationPredicateValueFormGroup.valueChanges.subscribe(() => { + this.alarmDurationPredicateValueFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.ts index bb6a9bdbc8..f8e07d8381 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -26,6 +26,7 @@ import { dynamicValueSourceTypeTranslationMap, getDynamicSourcesForAllowUser } from '@shared/models/query/query.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-alarm-dynamic-value', @@ -49,7 +50,8 @@ export class AlarmDynamicValue implements ControlValueAccessor, OnInit{ @Input() disabled: boolean; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -58,7 +60,9 @@ export class AlarmDynamicValue implements ControlValueAccessor, OnInit{ sourceAttribute: [null] }) - this.dynamicValue.get('sourceType').valueChanges.subscribe( + this.dynamicValue.get('sourceType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( (sourceType) => { if (!sourceType) { this.dynamicValue.get('sourceAttribute').patchValue(null, {emitEvent: false}); @@ -66,7 +70,9 @@ export class AlarmDynamicValue implements ControlValueAccessor, OnInit{ } ); - this.dynamicValue.valueChanges.subscribe(() => { + this.dynamicValue.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }) } diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition-dialog.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition-dialog.component.ts index 6e341b58b5..8eb0f196b7 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition-dialog.component.ts @@ -27,6 +27,7 @@ import { keyFilterInfosToKeyFilters, keyFiltersToKeyFilterInfos } from '@shared/ import { AlarmCondition, AlarmConditionType, AlarmConditionTypeTranslationMap } from '@shared/models/device.models'; import { TimeUnit, timeUnitTranslationMap } from '@shared/models/time/time.models'; import { EntityId } from '@shared/models/id/entity-id'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface AlarmRuleConditionDialogData { readonly: boolean; @@ -74,7 +75,9 @@ export class AlarmRuleConditionDialogComponent extends DialogComponent { + this.conditionFormGroup.get('spec.type').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((type) => { this.updateValidators(type, true, true); }); if (this.readonly) { diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.ts index 96829a3f8c..bba8907da3 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -36,6 +36,7 @@ import { import { EntityId } from '@shared/models/id/entity-id'; import { DashboardId } from '@shared/models/id/dashboard-id'; import { UtilsService } from '@core/services/utils.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-alarm-rule', @@ -81,7 +82,8 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat constructor(private dialog: MatDialog, private utils: UtilsService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -98,7 +100,9 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat alarmDetails: [null], dashboardId: [null] }); - this.alarmRuleFormGroup.valueChanges.subscribe(() => { + this.alarmRuleFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts index cf9b2fcff6..bdbd3d989d 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -40,6 +40,7 @@ import { import { isDefined, isDefinedAndNotNull } from '@core/utils'; import { MatCheckboxChange } from '@angular/material/checkbox'; import { getDefaultTimezone } from '@shared/models/time/time.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-alarm-schedule', @@ -80,7 +81,8 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, private propagateChange = (v: any) => { }; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -94,13 +96,17 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, dynamicValue: [null] }); - this.alarmScheduleForm.get('type').valueChanges.subscribe((type) => { + this.alarmScheduleForm.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((type) => { const defaultTimezone = getDefaultTimezone(); this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: defaultTimezone}, {emitEvent: false}); this.updateValidators(type, true); this.alarmScheduleForm.updateValueAndValidity(); }); - this.alarmScheduleForm.valueChanges.subscribe(() => { + this.alarmScheduleForm.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts index 2754969fac..637c9a59f7 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -31,6 +31,7 @@ import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; import { MatChipInputEvent } from '@angular/material/chips'; import { EntityId } from '@shared/models/id/entity-id'; import { UtilsService } from '@core/services/utils.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-device-profile-alarm', @@ -74,7 +75,8 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit constructor(private dialog: MatDialog, private utils: UtilsService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -101,7 +103,9 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit propagateToOwner: [null], propagateToTenant: [null] }, { validators: deviceProfileAlarmValidator }); - this.alarmFormGroup.valueChanges.subscribe(() => { + this.alarmFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile-provision-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device-profile-provision-configuration.component.ts index df6cdd52e1..6713877480 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile-provision-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile-provision-configuration.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -37,6 +37,7 @@ import { ActionNotificationShow } from '@core/notification/notification.actions' import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-device-profile-provision-configuration', @@ -79,7 +80,8 @@ export class DeviceProfileProvisionConfigurationComponent implements ControlValu constructor(protected store: Store, private fb: UntypedFormBuilder, - private translate: TranslateService) { + private translate: TranslateService, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -91,7 +93,9 @@ export class DeviceProfileProvisionConfigurationComponent implements ControlValu certificateRegExPattern: [{value: null, disabled: true}, Validators.required], allowCreateNewDevicesByX509Certificate: [{value: null, disabled: true}, Validators.required] }); - this.provisionConfigurationFormGroup.get('type').valueChanges.subscribe((type) => { + this.provisionConfigurationFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((type) => { if (type === DeviceProvisionType.DISABLED) { for (const field in this.provisionConfigurationFormGroup.controls) { if (field !== 'type') { @@ -129,7 +133,9 @@ export class DeviceProfileProvisionConfigurationComponent implements ControlValu this.provisionConfigurationFormGroup.get('provisionDeviceKey').enable({emitEvent: false}); } }); - this.provisionConfigurationFormGroup.valueChanges.subscribe(() => { + this.provisionConfigurationFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts index 930e44a5d9..ea2d6fb1ca 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, Inject, Input, Optional } from '@angular/core'; +import { ChangeDetectorRef, Component, DestroyRef, Inject, Input, Optional } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; @@ -43,6 +43,7 @@ import { EntityId } from '@shared/models/id/entity-id'; import { OtaUpdateType } from '@shared/models/ota-package.models'; import { DashboardId } from '@shared/models/id/dashboard-id'; import { RuleChainType } from '@shared/models/rule-chain.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-device-profile', @@ -83,7 +84,8 @@ export class DeviceProfileComponent extends EntityComponent { @Optional() @Inject('entity') protected entityValue: DeviceProfile, @Optional() @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig, protected fb: UntypedFormBuilder, - protected cd: ChangeDetectorRef) { + protected cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { super(store, fb, entityValue, entitiesTableConfigValue, cd); } @@ -129,10 +131,14 @@ export class DeviceProfileComponent extends EntityComponent { description: [entity ? entity.description : '', []], } ); - form.get('type').valueChanges.subscribe(() => { + form.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.deviceProfileTypeChanged(form); }); - form.get('transportType').valueChanges.subscribe(() => { + form.get('transportType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.deviceProfileTransportTypeChanged(form); }); this.checkIsNewDeviceProfile(entity, form); diff --git a/ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-configuration.component.ts index ff8e947ba3..1b09268536 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-configuration.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; @@ -23,6 +23,7 @@ import { DeviceProfileConfiguration, DeviceProfileType } from '@shared/models/device.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-default-device-profile-configuration', @@ -44,7 +45,8 @@ export class DefaultDeviceProfileConfigurationComponent implements ControlValueA private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -58,7 +60,9 @@ export class DefaultDeviceProfileConfigurationComponent implements ControlValueA this.defaultDeviceProfileConfigurationFormGroup = this.fb.group({ configuration: [null, Validators.required] }); - this.defaultDeviceProfileConfigurationFormGroup.valueChanges.subscribe(() => { + this.defaultDeviceProfileConfigurationFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-transport-configuration.component.ts index 1289d83745..5bbf451cde 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-transport-configuration.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALIDATORS, @@ -29,6 +29,7 @@ import { import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { DefaultDeviceProfileTransportConfiguration, DeviceTransportType } from '@shared/models/device.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-default-device-profile-transport-configuration', @@ -57,7 +58,8 @@ export class DefaultDeviceProfileTransportConfigurationComponent implements Cont private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -71,7 +73,9 @@ export class DefaultDeviceProfileTransportConfigurationComponent implements Cont this.defaultDeviceProfileTransportConfigurationFormGroup = this.fb.group({ configuration: [null, Validators.required] }); - this.defaultDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { + this.defaultDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts index 998235aefd..47d782dbdb 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALIDATORS, @@ -29,6 +29,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { DeviceProfileTransportConfiguration, DeviceTransportType } from '@shared/models/device.models'; import { deepClone } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-device-profile-transport-configuration', @@ -64,7 +65,8 @@ export class DeviceProfileTransportConfigurationComponent implements ControlValu private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -78,7 +80,9 @@ export class DeviceProfileTransportConfigurationComponent implements ControlValu this.deviceProfileTransportConfigurationFormGroup = this.fb.group({ configuration: [null] }); - this.deviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { + this.deviceProfileTransportConfigurationFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts index 950ac38cdb..775a5c963e 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts @@ -34,6 +34,7 @@ import { Direction } from '@shared/models/page/sort-order'; import { isDefined, isDefinedAndNotNull, isObject, isString } from '@core/utils'; import { PageLink } from '@shared/models/page/page-link'; import { TruncatePipe } from '@shared/pipe/truncate.pipe'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-profile-lwm2m-object-list', @@ -90,7 +91,9 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V objectsList: [this.objectsList], objectLwm2m: [''] }); - this.lwm2mListFormGroup.get('objectsList').valueChanges.subscribe((value) => { + this.lwm2mListFormGroup.get('objectsList').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((value) => { if (!value.length || (value.length && isObject(value[0]))) { let formValue = null; if (this.lwm2mListFormGroup.valid) { diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts index 9035b3dbaa..98a31db155 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, Inject, Input, Optional } from '@angular/core'; +import { ChangeDetectorRef, Component, DestroyRef, Inject, Input, Optional } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; @@ -24,6 +24,7 @@ import { TranslateService } from '@ngx-translate/core'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { EntityComponent } from '../entity/entity.component'; import { guid } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-tenant-profile', @@ -40,7 +41,8 @@ export class TenantProfileComponent extends EntityComponent { @Optional() @Inject('entity') protected entityValue: TenantProfile, @Optional() @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig, protected fb: UntypedFormBuilder, - protected cd: ChangeDetectorRef) { + protected cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { super(store, fb, entityValue, entitiesTableConfigValue, cd); } @@ -142,7 +144,9 @@ export class TenantProfileComponent extends EntityComponent { description: [entity ? entity.description : '', []], } ); - formGroup.get('isolatedTbRuleEngine').valueChanges.subscribe((value) => { + formGroup.get('isolatedTbRuleEngine').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((value) => { if (value) { formGroup.get('profileData').patchValue({ queueConfiguration: mainQueue diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts index 490bdc902b..fdf48af08c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts @@ -86,7 +86,6 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA tenantNotificationRequestsRateLimit: [null, []], tenantNotificationRequestsPerRuleRateLimit: [null, []], maxTransportMessages: [null, [Validators.required, Validators.min(0)]], - maxDebugModeDurationMinutes: [null, [Validators.min(0)]], maxTransportDataPoints: [null, [Validators.required, Validators.min(0)]], maxREExecutions: [null, [Validators.required, Validators.min(0)]], maxJSExecutions: [null, [Validators.required, Validators.min(0)]], @@ -97,6 +96,7 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA maxSms: [null, []], smsEnabled: [null, []], maxCreatedAlarms: [null, [Validators.required, Validators.min(0)]], + maxDebugModeDurationMinutes: [null, [Validators.min(0)]], defaultStorageTtlDays: [null, [Validators.required, Validators.min(0)]], alarmsTtlDays: [null, [Validators.required, Validators.min(0)]], rpcTtlDays: [null, [Validators.required, Validators.min(0)]], @@ -128,7 +128,9 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA } ); - this.defaultTenantProfileConfigurationFormGroup.valueChanges.subscribe(() => { + this.defaultTenantProfileConfigurationFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits-list.component.ts index dbc8f3e5df..38508c81c6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits-list.component.ts @@ -56,7 +56,6 @@ export class RateLimitsListComponent implements ControlValueAccessor, Validator, rateLimitsArray: Array; - private valueChangeSubscription: Subscription = null; private destroy$ = new Subject(); private propagateChange = (v: any) => { }; @@ -66,7 +65,7 @@ export class RateLimitsListComponent implements ControlValueAccessor, Validator, this.rateLimitsListFormGroup = this.fb.group({ rateLimits: this.fb.array([]) }); - this.valueChangeSubscription = this.rateLimitsListFormGroup.valueChanges + this.rateLimitsListFormGroup.valueChanges .pipe(takeUntil(this.destroy$)) .subscribe((value) => { this.updateView(value?.rateLimits); diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/tenant-profile-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant/tenant-profile-configuration.component.ts index 9494957cf5..c90485bc0b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/tenant-profile-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/tenant-profile-configuration.component.ts @@ -14,13 +14,14 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { deepClone } from '@core/utils'; import { TenantProfileConfiguration, TenantProfileType } from '@shared/models/tenant.model'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-tenant-profile-configuration', @@ -55,7 +56,8 @@ export class TenantProfileConfigurationComponent implements ControlValueAccessor private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -69,7 +71,9 @@ export class TenantProfileConfigurationComponent implements ControlValueAccessor this.tenantProfileConfigurationFormGroup = this.fb.group({ configuration: [null, Validators.required] }); - this.tenantProfileConfigurationFormGroup.valueChanges.subscribe(() => { + this.tenantProfileConfigurationFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/public-api.ts b/ui-ngx/src/app/modules/home/components/public-api.ts index 461c106b51..9a320664da 100644 --- a/ui-ngx/src/app/modules/home/components/public-api.ts +++ b/ui-ngx/src/app/modules/home/components/public-api.ts @@ -20,6 +20,7 @@ export * from './widget/lib/settings/common/widget-settings-common.module'; export * from './widget/widget-components.module'; export * from './widget/config/widget-config-components.module'; +export * from './widget/lib/chart/echarts-widget.models'; export * from './widget/config/widget-config.component.models'; export * from './widget/lib/table-widget.models'; export * from './widget/lib/flot-widget.models'; diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts index c9a09a262d..25ae1ae873 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -35,6 +35,7 @@ import { } from '@shared/models/queue.models'; import { isDefinedAndNotNull } from '@core/utils'; import { Subscription } from 'rxjs'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-queue-form', @@ -53,7 +54,7 @@ import { Subscription } from 'rxjs'; } ] }) -export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestroy, Validator { +export class QueueFormComponent implements ControlValueAccessor, OnInit, Validator { @Input() disabled: boolean; @@ -77,10 +78,10 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr private modelValue: QueueInfo; private propagateChange = null; private propagateChangePending = false; - private valueChange$: Subscription = null; constructor(private utils: UtilsService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -122,24 +123,23 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr duplicateMsgToAllPartitions: [false] }) }); - this.valueChange$ = this.queueFormGroup.valueChanges.subscribe(() => { + this.queueFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.queueFormGroup.get('name').valueChanges.subscribe((value) => { + this.queueFormGroup.get('name').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((value) => { this.queueFormGroup.patchValue({topic: `tb_rule_engine.${value}`}); }); - this.queueFormGroup.get('submitStrategy').get('type').valueChanges.subscribe(() => { + this.queueFormGroup.get('submitStrategy').get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.submitStrategyTypeChanged(); }); } - ngOnDestroy() { - if (this.valueChange$) { - this.valueChange$.unsubscribe(); - this.valueChange$ = null; - } - } - setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (this.disabled) { diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.ts b/ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.ts index a8553c1b7c..28d02c5ba2 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, Inject, OnInit, SkipSelf, ViewChild } from '@angular/core'; +import { Component, DestroyRef, Inject, OnInit, SkipSelf, ViewChild } from '@angular/core'; import { ErrorStateMatcher } from '@angular/material/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; @@ -32,6 +32,7 @@ import { forkJoin, Observable } from 'rxjs'; import { JsonObjectEditComponent } from '@shared/components/json-object-edit.component'; import { Router } from '@angular/router'; import { DialogComponent } from '@shared/components/dialog.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface RelationDialogData { isAdd: boolean; @@ -65,7 +66,8 @@ export class RelationDialogComponent extends DialogComponent, - public fb: UntypedFormBuilder) { + public fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store, router, dialogRef); this.isAdd = data.isAdd; this.direction = data.direction; @@ -83,7 +85,9 @@ export class RelationDialogComponent extends DialogComponent { this.submitted = false; } diff --git a/ui-ngx/src/app/modules/home/components/sms/aws-sns-provider-configuration.component.ts b/ui-ngx/src/app/modules/home/components/sms/aws-sns-provider-configuration.component.ts index add25cda9f..e6af9d8613 100644 --- a/ui-ngx/src/app/modules/home/components/sms/aws-sns-provider-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/sms/aws-sns-provider-configuration.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; @@ -25,6 +25,7 @@ import { SmsProviderConfiguration, SmsProviderType } from '@shared/models/settings.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-aws-sns-provider-configuration', @@ -57,7 +58,8 @@ export class AwsSnsProviderConfigurationComponent implements ControlValueAccesso private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -73,7 +75,9 @@ export class AwsSnsProviderConfigurationComponent implements ControlValueAccesso secretAccessKey: [null, [Validators.required]], region: [null, [Validators.required]] }); - this.awsSnsProviderConfigurationFormGroup.valueChanges.subscribe(() => { + this.awsSnsProviderConfigurationFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.ts b/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.ts index 5fb4d2208b..7a4c1c0975 100644 --- a/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { AwsSnsSmsProviderConfiguration, @@ -33,6 +33,7 @@ import { } from '@shared/models/settings.models'; import { isDefinedAndNotNull } from '@core/utils'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-smpp-sms-provider-configuration', @@ -46,7 +47,8 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion'; }) export class SmppSmsProviderConfigurationComponent implements ControlValueAccessor, OnInit{ - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } private requiredValue: boolean; @@ -98,7 +100,9 @@ export class SmppSmsProviderConfigurationComponent implements ControlValueAcces codingScheme: [null, []], }); - this.smppSmsProviderConfigurationFormGroup.valueChanges.subscribe(() => { + this.smppSmsProviderConfigurationFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValue(); }); } diff --git a/ui-ngx/src/app/modules/home/components/sms/sms-provider-configuration.component.ts b/ui-ngx/src/app/modules/home/components/sms/sms-provider-configuration.component.ts index 0a10859d4b..b167121c38 100644 --- a/ui-ngx/src/app/modules/home/components/sms/sms-provider-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/sms/sms-provider-configuration.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; @@ -31,6 +31,7 @@ import { SmsProviderType, smsProviderTypeTranslationMap } from '@shared/models/settings.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-sms-provider-configuration', @@ -65,7 +66,8 @@ export class SmsProviderConfigurationComponent implements ControlValueAccessor, private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -80,10 +82,14 @@ export class SmsProviderConfigurationComponent implements ControlValueAccessor, type: [null, Validators.required], configuration: [null, smsProviderConfigurationValidator(true)] }); - this.smsProviderConfigurationFormGroup.valueChanges.subscribe(() => { + this.smsProviderConfigurationFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.smsProviderConfigurationFormGroup.get('type').valueChanges.subscribe(() => { + this.smsProviderConfigurationFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.smsProviderTypeChanged(); }); } diff --git a/ui-ngx/src/app/modules/home/components/sms/twilio-sms-provider-configuration.component.ts b/ui-ngx/src/app/modules/home/components/sms/twilio-sms-provider-configuration.component.ts index f8a73e60ca..5fb010794a 100644 --- a/ui-ngx/src/app/modules/home/components/sms/twilio-sms-provider-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/sms/twilio-sms-provider-configuration.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; @@ -26,6 +26,7 @@ import { SmsProviderType, TwilioSmsProviderConfiguration } from '@shared/models/settings.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-twilio-sms-provider-configuration', @@ -60,7 +61,8 @@ export class TwilioSmsProviderConfigurationComponent implements ControlValueAcce private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -76,7 +78,9 @@ export class TwilioSmsProviderConfigurationComponent implements ControlValueAcce accountSid: [null, Validators.required], accountToken: [null, Validators.required] }); - this.twilioSmsProviderConfigurationFormGroup.valueChanges.subscribe(() => { + this.twilioSmsProviderConfigurationFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts index 95a172ab1b..ebfea4462f 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -40,6 +40,7 @@ import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; import { isDefinedAndNotNull } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-entity-types-version-create', @@ -80,7 +81,8 @@ export class EntityTypesVersionCreateComponent extends PageComponent implements constructor(protected store: Store, private translate: TranslateService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -88,7 +90,9 @@ export class EntityTypesVersionCreateComponent extends PageComponent implements this.entityTypesVersionCreateFormGroup = this.fb.group({ entityTypes: this.fb.array([], []) }); - this.entityTypesVersionCreateFormGroup.valueChanges.subscribe(() => { + this.entityTypesVersionCreateFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } @@ -152,7 +156,9 @@ export class EntityTypesVersionCreateComponent extends PageComponent implements } ); this.updateEntityTypeValidators(entityTypeControl); - entityTypeControl.get('config.allEntities').valueChanges.subscribe(() => { + entityTypeControl.get('config.allEntities').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateEntityTypeValidators(entityTypeControl); }); return entityTypeControl; diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts index c663745954..5275daecd5 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit, Renderer2, ViewContainerRef } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit, Renderer2, ViewContainerRef } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -40,6 +40,7 @@ import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.m import { MatCheckbox } from '@angular/material/checkbox'; import { TbPopoverService } from '@shared/components/popover.service'; import { RemoveOtherEntitiesConfirmComponent } from '@home/components/vc/remove-other-entities-confirm.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-entity-types-version-load', @@ -79,7 +80,8 @@ export class EntityTypesVersionLoadComponent extends PageComponent implements On private popoverService: TbPopoverService, private renderer: Renderer2, private viewContainerRef: ViewContainerRef, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -87,7 +89,9 @@ export class EntityTypesVersionLoadComponent extends PageComponent implements On this.entityTypesVersionLoadFormGroup = this.fb.group({ entityTypes: this.fb.array([], []) }); - this.entityTypesVersionLoadFormGroup.valueChanges.subscribe(() => { + this.entityTypesVersionLoadFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.ts b/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.ts index 2cdef0dea4..d6fc7f413c 100644 --- a/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, DestroyRef, Input, OnInit } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { UntypedFormBuilder, UntypedFormGroup, FormGroupDirective, Validators } from '@angular/forms'; import { select, Store } from '@ngrx/store'; @@ -35,6 +35,7 @@ import { catchError, mergeMap, take } from 'rxjs/operators'; import { of } from 'rxjs'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-repository-settings', @@ -71,7 +72,8 @@ export class RepositorySettingsComponent extends PageComponent implements OnInit private dialogService: DialogService, private translate: TranslateService, private cd: ChangeDetectorRef, - public fb: UntypedFormBuilder) { + public fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -89,10 +91,14 @@ export class RepositorySettingsComponent extends PageComponent implements OnInit privateKeyPassword: [null, []] }); this.updateValidators(false); - this.repositorySettingsForm.get('authMethod').valueChanges.subscribe(() => { + this.repositorySettingsForm.get('authMethod').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.repositorySettingsForm.get('privateKeyFileName').valueChanges.subscribe(() => { + this.repositorySettingsForm.get('privateKeyFileName').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(false); }); this.store.pipe( diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts index 245e58422e..5f100dbe3e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts @@ -17,6 +17,7 @@ import { ChangeDetectorRef, Component, + DestroyRef, EventEmitter, forwardRef, Input, @@ -53,6 +54,7 @@ import { } from '@home/components/widget/lib/cards/aggregated-value-card.models'; import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; import { FormProperty } from '@shared/models/dynamic-form.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-aggregated-data-key-row', @@ -128,7 +130,8 @@ export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnIn private cd: ChangeDetectorRef, public translate: TranslateService, public truncate: TruncatePipe, - private widgetConfigComponent: WidgetConfigComponent) { + private widgetConfigComponent: WidgetConfigComponent, + private destroyRef: DestroyRef) { } ngOnInit() { @@ -140,7 +143,9 @@ export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnIn color: [null, []], showArrow: [null, []] }); - this.keyRowFormGroup.valueChanges.subscribe( + this.keyRowFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => this.updateModel() ); } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.ts index cb93e516fb..39481874e3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.ts @@ -17,6 +17,7 @@ import { ChangeDetectorRef, Component, + DestroyRef, forwardRef, Input, OnChanges, @@ -39,6 +40,7 @@ import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { UtilsService } from '@core/services/utils.service'; import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; import { aggregatedValueCardDefaultKeySettings } from '@home/components/widget/lib/cards/aggregated-value-card.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-aggregated-data-keys-panel', @@ -87,14 +89,17 @@ export class AggregatedDataKeysPanelComponent implements ControlValueAccessor, O private dialog: MatDialog, private cd: ChangeDetectorRef, private utils: UtilsService, - private widgetConfigComponent: WidgetConfigComponent) { + private widgetConfigComponent: WidgetConfigComponent, + private destroyRef: DestroyRef) { } ngOnInit() { this.keysListFormGroup = this.fb.group({ keys: [this.fb.array([]), []] }); - this.keysListFormGroup.valueChanges.subscribe( + this.keysListFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( (val) => this.propagateChange(this.keysListFormGroup.get('keys').value) ); this.updateParams(); diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-key-row.component.ts index 8836a31f8b..48ffda9c7c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-key-row.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectorRef, Component, DestroyRef, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -29,6 +29,7 @@ import { DatasourceType } from '@shared/models/widget.models'; import { deepClone } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-comparison-key-row', @@ -60,7 +61,8 @@ export class ComparisonKeyRowComponent implements ControlValueAccessor, OnInit { private propagateChange = (_val: any) => {}; constructor(private fb: UntypedFormBuilder, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { } ngOnInit() { @@ -70,10 +72,14 @@ export class ComparisonKeyRowComponent implements ControlValueAccessor, OnInit { comparisonValuesLabel: [null, []], color: [null, []] }); - this.keyRowFormGroup.valueChanges.subscribe( + this.keyRowFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => this.updateModel() ); - this.keyRowFormGroup.get('showValuesForComparison').valueChanges.subscribe(() => this.updateValidators()); + this.keyRowFormGroup.get('showValuesForComparison').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => this.updateValidators()); } registerOnChange(fn: any): void { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-keys-table.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-keys-table.component.ts index b8241531b3..b605ef88e7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-keys-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-keys-table.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -24,6 +24,7 @@ import { UntypedFormGroup } from '@angular/forms'; import { DataKey, DatasourceType } from '@shared/models/widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-comparison-keys-table', @@ -55,14 +56,17 @@ export class ComparisonKeysTableComponent implements ControlValueAccessor, OnIni private propagateChange = (_val: any) => {}; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit() { this.keysListFormGroup = this.fb.group({ keys: [this.fb.array([]), []] }); - this.keysListFormGroup.valueChanges.subscribe( + this.keysListFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => { const keys: DataKey[] = this.keysListFormGroup.get('keys').value; this.propagateChange(keys); diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts index 18a2e833f6..29e60afa69 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts @@ -52,6 +52,7 @@ import { TimeSeriesChartYAxisId } from '@home/components/widget/lib/chart/time-series-chart.models'; import { TimeSeriesChartTooltipTrigger } from '@home/components/widget/lib/chart/time-series-chart-tooltip.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-time-series-chart-basic-config', @@ -204,7 +205,9 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon if (this.chartType === TimeSeriesChartType.state) { this.timeSeriesChartWidgetConfigForm.addControl('states', this.fb.control(settings.states, [])); } - this.timeSeriesChartWidgetConfigForm.get('comparisonEnabled').valueChanges.subscribe(() => this.updateSeriesState()); + this.timeSeriesChartWidgetConfigForm.get('comparisonEnabled').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => this.updateSeriesState()); } protected prepareOutputConfig(config: any): WidgetConfigComponentData { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts index cbccbb7fc2..95332c3791 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts @@ -17,6 +17,7 @@ import { ChangeDetectorRef, Component, + DestroyRef, EventEmitter, forwardRef, Input, @@ -60,6 +61,7 @@ import { } from '@home/components/widget/lib/chart/time-series-chart.models'; import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; import { FormProperty } from '@shared/models/dynamic-form.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export const dataKeyValid = (key: DataKey): boolean => !!key && !!key.type && !!key.name; @@ -228,7 +230,8 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan constructor(private fb: UntypedFormBuilder, private dialog: MatDialog, private cd: ChangeDetectorRef, - private widgetConfigComponent: WidgetConfigComponent) { + private widgetConfigComponent: WidgetConfigComponent, + private destroyRef: DestroyRef) { } ngOnInit() { @@ -246,7 +249,9 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan this.keyRowFormGroup.addControl('yAxis', this.fb.control(null)); this.keyRowFormGroup.addControl('timeSeriesType', this.fb.control(null)); } - merge(this.keyFormControl.valueChanges, this.keyRowFormGroup.valueChanges).subscribe( + merge(this.keyFormControl.valueChanges, this.keyRowFormGroup.valueChanges).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => this.updateModel() ); } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts index c66ce1a289..a9bea410ab 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts @@ -17,6 +17,7 @@ import { ChangeDetectorRef, Component, + DestroyRef, forwardRef, Input, OnChanges, @@ -46,6 +47,7 @@ import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/wid import { coerceBoolean } from '@shared/decorators/coercion'; import { TimeSeriesChartYAxisId } from '@home/components/widget/lib/chart/time-series-chart.models'; import { FormProperty } from '@shared/models/dynamic-form.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-data-keys-panel', @@ -181,14 +183,17 @@ export class DataKeysPanelComponent implements ControlValueAccessor, OnInit, OnC private dialog: MatDialog, private cd: ChangeDetectorRef, private utils: UtilsService, - private widgetConfigComponent: WidgetConfigComponent) { + private widgetConfigComponent: WidgetConfigComponent, + private destroyRef: DestroyRef) { } ngOnInit() { this.keysListFormGroup = this.fb.group({ keys: [this.fb.array([]), []] }); - this.keysListFormGroup.valueChanges.subscribe( + this.keysListFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => { let keys: DataKey[] = this.keysListFormGroup.get('keys').value; if (keys) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts index 82d1fa4094..37c5ab7f01 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, forwardRef, Input, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { WidgetActionsData } from '@home/components/widget/action/manage-widget-actions.component.models'; @@ -25,6 +25,7 @@ import { } from '@home/components/widget/action/manage-widget-actions-dialog.component'; import { deepClone } from '@core/utils'; import { MatDialog } from '@angular/material/dialog'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-widget-actions-panel', @@ -50,14 +51,17 @@ export class WidgetActionsPanelComponent implements ControlValueAccessor, OnInit constructor(private fb: UntypedFormBuilder, private dialog: MatDialog, private cd: ChangeDetectorRef, - private widgetConfigComponent: WidgetConfigComponent) { + private widgetConfigComponent: WidgetConfigComponent, + private destroyRef: DestroyRef) { } ngOnInit() { this.actionsFormGroup = this.fb.group({ actions: [null, []] }); - this.actionsFormGroup.get('actions').valueChanges.subscribe( + this.actionsFormGroup.get('actions').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( (val) => this.propagateChange(val) ); } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts index 2a9994486c..3f7baa5d7c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts @@ -63,6 +63,7 @@ import { map, share, tap } from 'rxjs/operators'; import { Observable, of, ReplaySubject } from 'rxjs'; import { ResourcesService } from '@core/services/resources.service'; import { UtilsService } from '@core/services/utils.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'liquid-level-card-basic-config', @@ -228,7 +229,9 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon actions: [configData.config.actions || {}, []] }); - this.levelCardWidgetConfigForm.get('selectedShape').valueChanges.subscribe(() => { + this.levelCardWidgetConfigForm.get('selectedShape').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.cd.detectChanges(); this.layoutsImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges(); }); diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts index 4dcdf305d4..b5b6ed4333 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, DestroyRef, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -59,6 +59,7 @@ import { WidgetComponentService } from '@home/components/widget/widget-component import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; import { isNotEmptyTbFunction, TbFunction } from '@shared/models/js-function.models'; import { FormProperty } from '@shared/models/dynamic-form.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-data-key-config', @@ -189,7 +190,8 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con private translate: TranslateService, private widgetService: WidgetService, private widgetComponentService: WidgetComponentService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); this.functionScopeVariables = this.widgetService.getWidgetScopeVariables(); } @@ -237,7 +239,9 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con this.dataKeySettingsFormGroup = this.fb.group({ settings: [null, []] }); - this.dataKeySettingsFormGroup.valueChanges.subscribe(() => { + this.dataKeySettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } @@ -257,7 +261,9 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con postFuncBody: [null, []] }); - this.dataKeyFormGroup.get('aggregationType').valueChanges.subscribe( + this.dataKeyFormGroup.get('aggregationType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( (aggType) => { if (!this.dataKeyFormGroup.get('label').dirty) { let newLabel = this.dataKeyFormGroup.get('name').value; @@ -271,23 +277,31 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con } ); - this.dataKeyFormGroup.get('comparisonEnabled').valueChanges.subscribe( + this.dataKeyFormGroup.get('comparisonEnabled').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => { this.updateComparisonValues(); } ); - this.dataKeyFormGroup.get('timeForComparison').valueChanges.subscribe( + this.dataKeyFormGroup.get('timeForComparison').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => { this.updateComparisonValues(); } ); - this.dataKeyFormGroup.valueChanges.subscribe(() => { + this.dataKeyFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.dataKeyFormGroup.get('usePostProcessing').valueChanges.subscribe((usePostProcessing: boolean) => { + this.dataKeyFormGroup.get('usePostProcessing').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((usePostProcessing: boolean) => { const postFuncBody: TbFunction = this.dataKeyFormGroup.get('postFuncBody').value; if (usePostProcessing && !isNotEmptyTbFunction(postFuncBody)) { this.dataKeyFormGroup.get('postFuncBody').patchValue('return value;'); diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts index df7c6162fb..b62f462576 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit, Optional } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit, Optional } from '@angular/core'; import { ControlValueAccessor, NG_VALIDATORS, @@ -44,6 +44,7 @@ import { EntityType } from '@shared/models/entity-type.models'; import { DatasourcesComponent } from '@home/components/widget/config/datasources.component'; import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; import { FormProperty } from '@shared/models/dynamic-form.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-datasource', @@ -189,7 +190,8 @@ export class DatasourceComponent implements ControlValueAccessor, OnInit, Valida constructor(private fb: UntypedFormBuilder, @Optional() private datasourcesComponent: DatasourcesComponent, - private widgetConfigComponent: WidgetConfigComponent) { + private widgetConfigComponent: WidgetConfigComponent, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -239,10 +241,14 @@ export class DatasourceComponent implements ControlValueAccessor, OnInit, Valida if (this.hasAdditionalLatestDataKeys) { this.datasourceFormGroup.addControl('latestDataKeys', this.fb.control(null)); } - this.datasourceFormGroup.get('type').valueChanges.subscribe(() => { + this.datasourceFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); - this.datasourceFormGroup.valueChanges.subscribe( + this.datasourceFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => { this.datasourceUpdated(this.datasourceFormGroup.value); } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts index 7b048d0945..65f46240c8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -43,6 +43,7 @@ import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/wid import { TranslateService } from '@ngx-translate/core'; import { coerceBoolean } from '@shared/decorators/coercion'; import { FormProperty } from '@shared/models/dynamic-form.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-datasources', @@ -150,7 +151,8 @@ export class DatasourcesComponent implements ControlValueAccessor, OnInit, Valid constructor(private fb: UntypedFormBuilder, private utils: UtilsService, public translate: TranslateService, - private widgetConfigComponent: WidgetConfigComponent) { + private widgetConfigComponent: WidgetConfigComponent, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -178,7 +180,9 @@ export class DatasourcesComponent implements ControlValueAccessor, OnInit, Valid this.datasourcesFormGroup = this.fb.group({ datasources: this.fb.array([]) }); - this.datasourcesFormGroup.valueChanges.subscribe( + this.datasourcesFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => { this.datasourcesUpdated(this.datasourcesFormGroup.get('datasources').value); } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/target-device.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/target-device.component.ts index 58893cb2a7..14e3187f61 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/target-device.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/target-device.component.ts @@ -24,7 +24,7 @@ import { Validator, Validators } from '@angular/forms'; -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { UtilsService } from '@core/services/utils.service'; import { TranslateService } from '@ngx-translate/core'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; @@ -32,6 +32,7 @@ import { TargetDevice, TargetDeviceType } from '@shared/models/widget.models'; import { EntityType } from '@shared/models/entity-type.models'; import { IAliasController } from '@core/api/widget-api.models'; import { EntityAliasSelectCallbacks } from '@home/components/alias/entity-alias-select.component.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-target-device', @@ -80,7 +81,8 @@ export class TargetDeviceComponent implements ControlValueAccessor, OnInit, Vali constructor(private fb: UntypedFormBuilder, private utils: UtilsService, public translate: TranslateService, - private widgetConfigComponent: WidgetConfigComponent) { + private widgetConfigComponent: WidgetConfigComponent, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -111,10 +113,14 @@ export class TargetDeviceComponent implements ControlValueAccessor, OnInit, Vali deviceId: [null, (!this.widgetEditMode && !this.targetDeviceOptional) ? [Validators.required] : []], entityAliasId: [null, (!this.widgetEditMode && !this.targetDeviceOptional) ? [Validators.required] : []] }); - this.targetDeviceFormGroup.get('type').valueChanges.subscribe(() => { + this.targetDeviceFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); - this.targetDeviceFormGroup.valueChanges.subscribe( + this.targetDeviceFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => { this.targetDeviceUpdated(this.targetDeviceFormGroup.value); } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.ts index 4ab2de5aea..6e2a34d6d7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { WidgetConfig, widgetType } from '@shared/models/widget.models'; @@ -23,6 +23,7 @@ import { TranslateService } from '@ngx-translate/core'; import { coerceBoolean } from '@shared/decorators/coercion'; import { isDefined } from '@core/utils'; import { TimewindowStyle } from '@shared/models/widget-settings.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface TimewindowConfigData { useDashboardTimewindow: boolean; @@ -81,7 +82,8 @@ export class TimewindowConfigPanelComponent implements ControlValueAccessor, OnI constructor(private fb: UntypedFormBuilder, public translate: TranslateService, - private widgetConfigComponent: WidgetConfigComponent) { + private widgetConfigComponent: WidgetConfigComponent, + private destroyRef: DestroyRef) { } ngOnInit() { @@ -91,13 +93,19 @@ export class TimewindowConfigPanelComponent implements ControlValueAccessor, OnI timewindow: [null, []], timewindowStyle: [null, []] }); - this.timewindowConfig.valueChanges.subscribe( + this.timewindowConfig.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => this.propagateChange(this.timewindowConfig.getRawValue()) ); - this.timewindowConfig.get('useDashboardTimewindow').valueChanges.subscribe(() => { + this.timewindowConfig.get('useDashboardTimewindow').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateTimewindowConfigEnabledState(); }); - this.timewindowConfig.get('displayTimewindow').valueChanges.subscribe(() => { + this.timewindowConfig.get('displayTimewindow').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateTimewindowConfigEnabledState(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.ts index a70bcf196f..664108a783 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { defaultTimewindowStyle, TimewindowStyle } from '@shared/models/widget-settings.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; @@ -23,6 +23,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { Timewindow } from '@shared/models/time/time.models'; import { deepClone } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-timewindow-style-panel', @@ -50,7 +51,8 @@ export class TimewindowStylePanelComponent extends PageComponent implements OnIn previewTimewindowStyle: TimewindowStyle; constructor(private fb: UntypedFormBuilder, - protected store: Store) { + protected store: Store, + private destroyRef: DestroyRef) { super(store); } @@ -69,13 +71,17 @@ export class TimewindowStylePanelComponent extends PageComponent implements OnIn ); this.updatePreviewStyle(this.timewindowStyle); this.updateTimewindowStyleEnabledState(); - this.timewindowStyleFormGroup.valueChanges.subscribe((timewindowStyle: TimewindowStyle) => { + this.timewindowStyleFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((timewindowStyle: TimewindowStyle) => { if (this.timewindowStyleFormGroup.valid) { this.updatePreviewStyle(timewindowStyle); setTimeout(() => {this.popover?.updatePosition();}, 0); } }); - this.timewindowStyleFormGroup.get('showIcon').valueChanges.subscribe(() => { + this.timewindowStyleFormGroup.get('showIcon').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateTimewindowStyleEnabledState(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts index 07f8b1b501..ec588d69ad 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts @@ -18,7 +18,7 @@ import { WidgetActionCallbacks } from '@home/components/widget/action/manage-wid import { DatasourceCallbacks } from '@home/components/widget/config/datasource.component.models'; import { WidgetConfigComponentData } from '@home/models/widget-component.models'; import { Observable } from 'rxjs'; -import { AfterViewInit, Directive, EventEmitter, Inject, OnInit } from '@angular/core'; +import { AfterViewInit, DestroyRef, Directive, EventEmitter, inject, Inject, OnInit } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -27,6 +27,7 @@ import { DataKey, DatasourceType, WidgetConfigMode, widgetType } from '@shared/m import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { isDefinedAndNotNull } from '@core/utils'; import { IAliasController } from '@core/api/widget-api.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export type WidgetConfigCallbacks = DatasourceCallbacks & WidgetActionCallbacks; @@ -77,6 +78,8 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement widgetConfigChangedEmitter = new EventEmitter(); widgetConfigChanged = this.widgetConfigChangedEmitter.asObservable(); + protected destroyRef = inject(DestroyRef); + protected constructor(@Inject(Store) protected store: Store, protected widgetConfigComponent: WidgetConfigComponent) { super(store); @@ -104,11 +107,15 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement for (const part of path) { control = control.get(part); } - control.valueChanges.subscribe(() => { + control.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true, trigger); }); } - this.configForm().valueChanges.subscribe(() => { + this.configForm().valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.onConfigChanged(this.prepareOutputConfig(this.configForm().getRawValue())); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts index 4743bff002..ab63e0e55d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts @@ -153,6 +153,10 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, if (this.definedSettingsComponentRef) { this.definedSettingsComponentRef.destroy(); } + if (this.changeSubscription) { + this.changeSubscription.unsubscribe(); + this.changeSubscription = null; + } } setDisabledState(isDisabled: boolean): void { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts index efb25a0258..ed023c1a0d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts @@ -112,9 +112,6 @@ export class TimeSeriesChartWidgetComponent implements OnInit, OnDestroy, AfterV legendKey.dataKey.settings = mergeDeep({} as TimeSeriesChartKeySettings, timeSeriesChartKeyDefaultSettings, legendKey.dataKey.settings); legendKey.dataKey.hidden = legendKey.dataKey.settings.dataHiddenByDefault; - if (this.settings.yAxes[legendKey.dataKey.settings.yAxisId]) { - this.settings.yAxes[legendKey.dataKey.settings.yAxisId].show = !legendKey.dataKey.settings.dataHiddenByDefault; - } }); this.legendKeys = this.legendKeys.filter(legendKey => legendKey.dataKey.settings.showInLegend); if (!this.legendKeys.length) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss index b7c02acef6..1de8288241 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss @@ -44,12 +44,19 @@ } .tb-usage-items-progress { + --mdc-linear-progress-track-height: 8px; + --mdc-linear-progress-active-indicator-height: 8px; width: 34px; @media #{$mat-md} { display: none; } + .mat-mdc-progress-bar { + &.critical { + --mdc-linear-progress-track-color: rgba(209, 39, 48, 0.06); + --mdc-linear-progress-active-indicator-color: #D12730; + } + } .mdc-linear-progress { - height: 8px; margin-top: 6px; margin-bottom: 6px; border-radius: 2px; @@ -93,21 +100,3 @@ color: rgba(0, 0, 0, 0.76); } } - -:host ::ng-deep { - .tb-usage-items-progress { - .mat-mdc-progress-bar { - .mdc-linear-progress__bar-inner { - border-top-width: 8px; - } - &.critical { - .mdc-linear-progress__buffer-bar { - background: rgba(209, 39, 48, 0.06); - } - .mdc-linear-progress__bar-inner { - border-top-color: #D12730; - } - } - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.ts index f60db9763d..42050c2228 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, Input, OnInit } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { WidgetContext } from '@home/models/widget-component.models'; import { Store } from '@ngrx/store'; @@ -31,6 +31,7 @@ import { EntityType } from '@shared/models/entity-type.models'; import { createLabelFromDatasource, isDefinedAndNotNull } from '@core/utils'; import { Observable } from 'rxjs'; import { jsonRequired } from '@shared/components/json-object-edit.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; enum JsonInputWidgetMode { ATTRIBUTE = 'ATTRIBUTE', @@ -77,7 +78,8 @@ export class JsonInputWidgetComponent extends PageComponent implements OnInit { private utils: UtilsService, private fb: UntypedFormBuilder, private attributeService: AttributeService, - private translate: TranslateService) { + private translate: TranslateService, + private destroyRef: DestroyRef) { super(store); } @@ -137,7 +139,9 @@ export class JsonInputWidgetComponent extends PageComponent implements OnInit { this.attributeUpdateFormGroup = this.fb.group({ currentValue: [{}, validators] }); - this.attributeUpdateFormGroup.valueChanges.subscribe(() => { + this.attributeUpdateFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.ctx.detectChanges(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts index 0abcc9f7c8..d91a84d3b2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts @@ -23,14 +23,14 @@ import { PosFunction, WidgetUnitedMapSettings } from '../map-models'; -import { forkJoin, Observable, of, ReplaySubject, switchMap } from 'rxjs'; +import { combineLatest, Observable, of, ReplaySubject, switchMap } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { calculateNewPointCoordinate, loadImageWithAspect } from '@home/components/widget/lib/maps/common-maps-utils'; import { WidgetContext } from '@home/models/widget-component.models'; import { DataSet, DatasourceType, FormattedData, widgetType } from '@shared/models/widget.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; -import { isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, parseFunction, parseTbFunction } from '@core/utils'; +import { isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, parseTbFunction } from '@core/utils'; import { EntityDataPageLink } from '@shared/models/query/query.models'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { CompiledTbFunction } from '@shared/models/js-function.models'; @@ -55,7 +55,7 @@ export class ImageMap extends LeafletMap { mapImage: this.mapImage(options) }; - forkJoin(initData).subscribe(inited => { + combineLatest(initData).subscribe(inited => { this.posFunction = inited.posFunction; const mapImage = inited.mapImage; this.imageUrl = mapImage.imageUrl; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.ts index de79261d2b..8742d88411 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, OnInit } from '@angular/core'; +import { Component, DestroyRef, OnInit } from '@angular/core'; import { DialogComponent } from '@shared/components/dialog.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -23,6 +23,7 @@ import { MatDialogRef } from '@angular/material/dialog'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { RequestData } from '@shared/models/rpc.models'; import { TranslateService } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-persistent-add-dialog', @@ -41,7 +42,8 @@ export class PersistentAddDialogComponent extends DialogComponent, private fb: UntypedFormBuilder, - private translate: TranslateService) { + private translate: TranslateService, + private destroyRef: DestroyRef) { super(store, router, dialogRef); this.persistentFormGroup = this.fb.group( @@ -62,7 +64,9 @@ export class PersistentAddDialogComponent extends DialogComponent { this.rpcMessageTypeText = this.translate.instant(`widgets.persistent-table.message-types.${this.persistentFormGroup.get('oneWayElseTwoWay').value}`); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.ts index 7e47cb6433..262df80d67 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.ts @@ -14,13 +14,14 @@ /// limitations under the License. /// -import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; import { WidgetFont } from '@home/components/widget/lib/settings/common/widget-font.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface LabelWidgetLabel { pattern: string; @@ -61,7 +62,8 @@ export class LabelWidgetLabelComponent extends PageComponent implements OnInit, constructor(protected store: Store, private translate: TranslateService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -73,7 +75,9 @@ export class LabelWidgetLabelComponent extends PageComponent implements OnInit, backgroundColor: [null, []], font: [null, []] }); - this.labelWidgetLabelFormGroup.valueChanges.subscribe(() => { + this.labelWidgetLabelFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.ts index 3f31a27a58..28363dff34 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -35,6 +35,7 @@ import { TranslateService } from '@ngx-translate/core'; import { WidgetService } from '@core/http/widget.service'; import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { IAliasController } from 'src/app/core/api/widget-api.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export function flotDataKeyDefaultSettings(chartType: ChartType): TbFlotKeySettings { const settings: TbFlotKeySettings = { @@ -128,7 +129,8 @@ export class FlotKeySettingsComponent extends PageComponent implements OnInit, C constructor(protected store: Store, private translate: TranslateService, private widgetService: WidgetService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -189,23 +191,33 @@ export class FlotKeySettingsComponent extends PageComponent implements OnInit, C }); - this.flotKeySettingsFormGroup.get('showLines').valueChanges.subscribe(() => { + this.flotKeySettingsFormGroup.get('showLines').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(false); }); - this.flotKeySettingsFormGroup.get('fillLines').valueChanges.subscribe(() => { + this.flotKeySettingsFormGroup.get('fillLines').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(false); }); - this.flotKeySettingsFormGroup.get('showPoints').valueChanges.subscribe(() => { + this.flotKeySettingsFormGroup.get('showPoints').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(false); }); - this.flotKeySettingsFormGroup.get('comparisonSettings.showValuesForComparison').valueChanges.subscribe(() => { + this.flotKeySettingsFormGroup.get('comparisonSettings.showValuesForComparison').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(false); }); - this.flotKeySettingsFormGroup.valueChanges.subscribe(() => { + this.flotKeySettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.ts index b6b4c88962..2c793abc82 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.ts @@ -15,7 +15,16 @@ /// import { ValueSourceProperty } from '@home/components/widget/lib/settings/common/value-source.component'; -import { Component, EventEmitter, forwardRef, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { + Component, + DestroyRef, + EventEmitter, + forwardRef, + Input, + OnInit, + Output, + ViewEncapsulation +} from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -28,6 +37,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { IAliasController } from '@core/api/widget-api.models'; import { TbFlotKeyThreshold } from '@home/components/widget/lib/flot-widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-flot-threshold', @@ -60,7 +70,8 @@ export class FlotThresholdComponent extends PageComponent implements OnInit, Con public thresholdFormGroup: UntypedFormGroup; constructor(protected store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -70,7 +81,9 @@ export class FlotThresholdComponent extends PageComponent implements OnInit, Con lineWidth: [null, [Validators.min(0)]], color: [null, []] }); - this.thresholdFormGroup.valueChanges.subscribe(() => { + this.thresholdFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.ts index 1e5e88e7cf..44b04406e7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -41,6 +41,7 @@ import { } from '@home/components/widget/lib/settings/chart/label-data-key.component'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { defaultLegendConfig, widgetType } from '@shared/models/widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export const flotDefaultSettings = (chartType: ChartType): Partial => { const settings: Partial = { @@ -140,7 +141,8 @@ export class FlotWidgetSettingsComponent extends PageComponent implements OnInit constructor(protected store: Store, private translate: TranslateService, private widgetService: WidgetService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -238,34 +240,50 @@ export class FlotWidgetSettingsComponent extends PageComponent implements OnInit this.flotSettingsFormGroup.addControl('dataKeysListForLabels', this.fb.control(this.fb.array([]), [])); } - this.flotSettingsFormGroup.get('showTooltip').valueChanges.subscribe(() => { + this.flotSettingsFormGroup.get('showTooltip').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.flotSettingsFormGroup.get('xaxis.showLabels').valueChanges.subscribe(() => { + this.flotSettingsFormGroup.get('xaxis.showLabels').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.flotSettingsFormGroup.get('yaxis.showLabels').valueChanges.subscribe(() => { + this.flotSettingsFormGroup.get('yaxis.showLabels').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); if (this.chartType === 'graph' || this.chartType === 'bar') { - this.flotSettingsFormGroup.get('showLegend').valueChanges.subscribe(() => { + this.flotSettingsFormGroup.get('showLegend').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.flotSettingsFormGroup.get('comparisonEnabled').valueChanges.subscribe(() => { + this.flotSettingsFormGroup.get('comparisonEnabled').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.flotSettingsFormGroup.get('timeForComparison').valueChanges.subscribe(() => { + this.flotSettingsFormGroup.get('timeForComparison').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.flotSettingsFormGroup.get('customLegendEnabled').valueChanges.subscribe(() => { + this.flotSettingsFormGroup.get('customLegendEnabled').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); } - this.flotSettingsFormGroup.valueChanges.subscribe(() => { + this.flotSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.ts index 793f6ea6d1..30361117a7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -28,6 +28,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface LabelDataKey { name: string; @@ -75,7 +76,8 @@ export class LabelDataKeyComponent extends PageComponent implements OnInit, Cont constructor(protected store: Store, private translate: TranslateService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -84,7 +86,9 @@ export class LabelDataKeyComponent extends PageComponent implements OnInit, Cont name: [null, [Validators.required]], type: [DataKeyType.attribute, [Validators.required]] }); - this.labelDataKeyFormGroup.valueChanges.subscribe(() => { + this.labelDataKeyFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts index 2f0ca0197a..60f841a69a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -41,6 +41,7 @@ import { chartShapes, chartShapeTranslations } from '@home/components/widget/lib/chart/chart.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-time-series-chart-line-settings', @@ -90,7 +91,8 @@ export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValu constructor(protected store: Store, private dataKeyConfigComponent: DataKeyConfigComponent, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -112,14 +114,18 @@ export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValu pointSize: [null, [Validators.min(0)]], fillAreaSettings: [null, []] }); - this.lineSettingsFormGroup.valueChanges.subscribe(() => { + this.lineSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); merge(this.lineSettingsFormGroup.get('showLine').valueChanges, this.lineSettingsFormGroup.get('step').valueChanges, this.lineSettingsFormGroup.get('showPointLabel').valueChanges, - this.lineSettingsFormGroup.get('enablePointLabelBackground').valueChanges) - .subscribe(() => { + this.lineSettingsFormGroup.get('enablePointLabelBackground').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.ts index 6abc51b003..a4339b7e12 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; @@ -35,6 +35,7 @@ import { IAliasController } from '@core/api/widget-api.models'; import { WidgetService } from '@core/http/widget.service'; import { AlarmSeverity, alarmSeverityTranslations } from '@shared/models/alarm.models'; import { EntityType } from '@shared/models/entity-type.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-get-value-action-settings-panel', @@ -103,7 +104,8 @@ export class GetValueActionSettingsPanelComponent extends PageComponent implemen constructor(private fb: UntypedFormBuilder, private widgetService: WidgetService, - protected store: Store) { + protected store: Store, + private destroyRef: DestroyRef) { super(store); } @@ -145,7 +147,10 @@ export class GetValueActionSettingsPanelComponent extends PageComponent implemen merge(this.getValueSettingsFormGroup.get('action').valueChanges, this.getValueSettingsFormGroup.get('dataToValue').get('type').valueChanges, - this.getValueSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges).subscribe(() => { + this.getValueSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); this.updateValidators(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.ts index a573023152..e949f765c6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -42,6 +42,7 @@ import { } from '@home/components/widget/lib/settings/common/action/mobile-action-editor.models'; import { WidgetService } from '@core/http/widget.service'; import { TbFunction } from '@shared/models/js-function.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-mobile-action-editor', @@ -81,7 +82,8 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit private propagateChange = (_v: any) => { }; constructor(private fb: UntypedFormBuilder, - private widgetService: WidgetService) { + private widgetService: WidgetService, + private destroyRef: DestroyRef) { this.functionScopeVariables = this.widgetService.getWidgetScopeVariables(); } @@ -98,14 +100,18 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit handleEmptyResultFunction: [null], handleErrorFunction: [null] }); - this.mobileActionFormGroup.get('type').valueChanges.subscribe((type: WidgetMobileActionType) => { + this.mobileActionFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((type: WidgetMobileActionType) => { let action: WidgetMobileActionDescriptor = this.mobileActionFormGroup.value; if (this.mobileActionTypeFormGroup) { action = {...action, ...this.mobileActionTypeFormGroup.value}; } this.updateMobileActionType(type, action); }); - this.mobileActionFormGroup.valueChanges.subscribe(() => { + this.mobileActionFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } @@ -250,7 +256,9 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit break; } } - this.mobileActionTypeFormGroup.valueChanges.subscribe(() => { + this.mobileActionTypeFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.ts index 819ed2a3fc..48120ade8e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; @@ -33,6 +33,7 @@ import { AttributeScope, DataKeyType, telemetryTypeTranslationsShort } from '@sh import { IAliasController } from '@core/api/widget-api.models'; import { WidgetService } from '@core/http/widget.service'; import { ValueType } from '@shared/models/constants'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-set-value-action-settings-panel', @@ -89,7 +90,8 @@ export class SetValueActionSettingsPanelComponent extends PageComponent implemen constructor(private fb: UntypedFormBuilder, private widgetService: WidgetService, - protected store: Store) { + protected store: Store, + private destroyRef: DestroyRef) { super(store); } @@ -122,7 +124,10 @@ export class SetValueActionSettingsPanelComponent extends PageComponent implemen merge(this.setValueSettingsFormGroup.get('action').valueChanges, this.setValueSettingsFormGroup.get('valueToData').get('type').valueChanges, - this.setValueSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges).subscribe(() => { + this.setValueSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); this.updateValidators(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.ts index bfb6433a82..a9d547974d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -29,6 +29,7 @@ import { IAliasController } from '@core/api/widget-api.models'; import { AdvancedColorRange } from '@shared/models/widget-settings.models'; import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; import { Datasource } from '@shared/models/widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-advanced-range', @@ -66,7 +67,8 @@ export class AdvancedRangeComponent extends PageComponent implements OnInit, Con public advancedRangeLevelFormGroup: UntypedFormGroup; constructor(protected store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -76,7 +78,9 @@ export class AdvancedRangeComponent extends PageComponent implements OnInit, Con to: [null, []], color: [null, [Validators.required]] }); - this.advancedRangeLevelFormGroup.valueChanges.subscribe(() => { + this.advancedRangeLevelFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/auto-date-format-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/auto-date-format-settings-panel.component.ts index dcfc942778..7c8685ed0c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/auto-date-format-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/auto-date-format-settings-panel.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { AutoDateFormatSettings, defaultAutoDateFormatSettings, @@ -27,6 +27,7 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { DatePipe } from '@angular/common'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-auto-date-format-settings-panel', @@ -59,7 +60,8 @@ export class AutoDateFormatSettingsPanelComponent extends PageComponent implemen constructor(private date: DatePipe, private fb: UntypedFormBuilder, - protected store: Store) { + protected store: Store, + private destroyRef: DestroyRef) { super(store); } @@ -68,7 +70,9 @@ export class AutoDateFormatSettingsPanelComponent extends PageComponent implemen for (const unit of formatTimeUnits) { this.autoDateFormatFormGroup.addControl(unit, this.fb.control(this.autoDateFormatSettings[unit] || this.defaultValues[unit], [Validators.required])); - this.autoDateFormatFormGroup.get(unit).valueChanges.subscribe((value: string) => { + this.autoDateFormatFormGroup.get(unit).valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((value: string) => { this.previewText[unit] = this.date.transform(Date.now(), value); }); this.previewText[unit] = this.date.transform(Date.now(), this.autoDateFormatSettings[unit] || this.defaultValues[unit]); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.scss index 6bc83fe967..d907e20f64 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.scss @@ -17,6 +17,8 @@ .tb-background-settings-panel { width: 620px; + min-height: 300px; + max-height: 90vh; display: flex; flex-direction: column; gap: 16px; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.ts index c1c85a05e1..6229b25ba8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.ts @@ -14,7 +14,16 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { + ChangeDetectorRef, + Component, + DestroyRef, + EventEmitter, + Input, + OnInit, + Output, + ViewEncapsulation +} from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { backgroundStyle, @@ -30,6 +39,7 @@ import { AppState } from '@core/core.state'; import { Observable } from 'rxjs'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-background-settings-panel', @@ -64,7 +74,8 @@ export class BackgroundSettingsPanelComponent extends PageComponent implements O private imagePipe: ImagePipe, private sanitizer: DomSanitizer, protected store: Store, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { super(store); } @@ -81,13 +92,19 @@ export class BackgroundSettingsPanelComponent extends PageComponent implements O }) } ); - this.backgroundSettingsFormGroup.get('type').valueChanges.subscribe(() => { + this.backgroundSettingsFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { setTimeout(() => {this.popover?.updatePosition();}, 0); }); - this.backgroundSettingsFormGroup.get('overlay').get('enabled').valueChanges.subscribe(() => { + this.backgroundSettingsFormGroup.get('overlay').get('enabled').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); - this.backgroundSettingsFormGroup.valueChanges.subscribe(() => { + this.backgroundSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateBackgroundStyle(); }); this.updateValidators(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings.component.ts index 66dd774a21..828f424b51 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings.component.ts @@ -110,7 +110,7 @@ export class BackgroundSettingsComponent implements OnInit, ControlValueAccessor backgroundSettings: this.modelValue }; const backgroundSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, - this.viewContainerRef, BackgroundSettingsPanelComponent, ['leftOnly', 'leftTopOnly', 'leftBottomOnly'], true, null, + this.viewContainerRef, BackgroundSettingsPanelComponent, ['left'], true, null, ctx, {}, {}, {}, true); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.ts index f9afc0b5d1..552de96ab9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { WidgetButtonAppearance, @@ -25,6 +25,7 @@ import { } from '@shared/components/button/widget-button.models'; import { merge } from 'rxjs'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-widget-button-appearance', @@ -72,7 +73,8 @@ export class WidgetButtonAppearanceComponent implements OnInit, ControlValueAcce private propagateChange = (_val: any) => {}; - constructor(private fb: UntypedFormBuilder) {} + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) {} ngOnInit(): void { this.appearanceFormGroup = this.fb.group({ @@ -97,12 +99,16 @@ export class WidgetButtonAppearanceComponent implements OnInit, ControlValueAcce customStyle.addControl(state, this.fb.control(null, [])); } this.appearanceFormGroup.addControl('customStyle', customStyle); - this.appearanceFormGroup.valueChanges.subscribe(() => { + this.appearanceFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); merge(this.appearanceFormGroup.get('showLabel').valueChanges, - this.appearanceFormGroup.get('showIcon').valueChanges) - .subscribe(() => { + this.appearanceFormGroup.get('showIcon').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.ts index 5bb043b9d8..2f27026f87 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.ts @@ -17,6 +17,7 @@ import { ChangeDetectorRef, Component, + DestroyRef, EventEmitter, Input, OnInit, @@ -42,6 +43,7 @@ import { import { merge } from 'rxjs'; import { deepClone } from '@core/utils'; import { WidgetButtonComponent } from '@shared/components/button/widget-button.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-widget-button-custom-style-panel', @@ -99,7 +101,8 @@ export class WidgetButtonCustomStylePanelComponent extends PageComponent impleme constructor(private fb: UntypedFormBuilder, protected store: Store, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { super(store); } @@ -118,11 +121,15 @@ export class WidgetButtonCustomStylePanelComponent extends PageComponent impleme ); merge(this.customStyleFormGroup.get('overrideMainColor').valueChanges, this.customStyleFormGroup.get('overrideBackgroundColor').valueChanges, - this.customStyleFormGroup.get('overrideDropShadow').valueChanges) - .subscribe(() => { + this.customStyleFormGroup.get('overrideDropShadow').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); - this.customStyleFormGroup.valueChanges.subscribe(() => { + this.customStyleFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updatePreviewAppearance(); }); this.setStyle(this.customStyle); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component.ts index 98ced2dc2e..321eaa31cf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component.ts @@ -17,6 +17,7 @@ import { ChangeDetectorRef, Component, + DestroyRef, EventEmitter, Input, OnInit, @@ -44,6 +45,7 @@ import { widgetButtonToggleStates, widgetButtonToggleStatesTranslations } from '@home/components/widget/lib/button/segmented-button-widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-widget-button-toggle-custom-style-panel', @@ -106,7 +108,8 @@ export class WidgetButtonToggleCustomStylePanelComponent extends PageComponent i constructor(private fb: UntypedFormBuilder, protected store: Store, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { super(store); } @@ -126,11 +129,15 @@ export class WidgetButtonToggleCustomStylePanelComponent extends PageComponent i ); merge(this.customStyleFormGroup.get('overrideMainColor').valueChanges, this.customStyleFormGroup.get('overrideBackgroundColor').valueChanges, - this.customStyleFormGroup.get('overrideBorderColor').valueChanges) - .subscribe(() => { - this.updateValidators(); - }); - this.customStyleFormGroup.valueChanges.subscribe(() => { + this.customStyleFormGroup.get('overrideBorderColor').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateValidators(); + }); + this.customStyleFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updatePreviewAppearance(); }); this.setStyle(this.customStyle); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-animation-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-animation-settings.component.ts index 19ccfcd18b..b0e2d107c6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-animation-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-animation-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -23,6 +23,7 @@ import { Validators } from '@angular/forms'; import { chartAnimationEasings, ChartAnimationSettings } from '@home/components/widget/lib/chart/chart.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-chart-animation-settings', @@ -51,7 +52,8 @@ export class ChartAnimationSettingsComponent implements OnInit, ControlValueAcce public animationSettingsFormGroup: UntypedFormGroup; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -65,11 +67,14 @@ export class ChartAnimationSettingsComponent implements OnInit, ControlValueAcce animationEasingUpdate: [null, []], animationDelayUpdate: [null, [Validators.min(0)]] }); - this.animationSettingsFormGroup.valueChanges.subscribe(() => { + this.animationSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.animationSettingsFormGroup.get('animation').valueChanges - .subscribe(() => { + this.animationSettingsFormGroup.get('animation').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); } @@ -97,7 +102,9 @@ export class ChartAnimationSettingsComponent implements OnInit, ControlValueAcce value, {emitEvent: false} ); this.updateValidators(); - this.animationSettingsFormGroup.get('animation').valueChanges.subscribe((animation) => { + this.animationSettingsFormGroup.get('animation').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((animation) => { this.settingsExpanded = animation; }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-bar-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-bar-settings.component.ts index 6fef380a40..88304e2b20 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-bar-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-bar-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit, Optional } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit, Optional } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -37,6 +37,7 @@ import { pieChartLabelPositionTranslations } from '@home/components/widget/lib/chart/chart.models'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-chart-bar-settings', @@ -77,7 +78,8 @@ export class ChartBarSettingsComponent implements OnInit, ControlValueAccessor { constructor(protected store: Store, @Optional() private dataKeyConfigComponent: DataKeyConfigComponent, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -104,13 +106,17 @@ export class ChartBarSettingsComponent implements OnInit, ControlValueAccessor { this.barSettingsFormGroup.addControl('barWidth', this.fb.control(null, [Validators.min(0), Validators.max(100)])); } - this.barSettingsFormGroup.valueChanges.subscribe(() => { + this.barSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); merge(this.barSettingsFormGroup.get('showBorder').valueChanges, this.barSettingsFormGroup.get('showLabel').valueChanges, - this.barSettingsFormGroup.get('enableLabelBackground').valueChanges) - .subscribe(() => { + this.barSettingsFormGroup.get('enableLabelBackground').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.ts index bf02937221..6f1093b9b7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -30,6 +30,7 @@ import { chartFillTypes, chartFillTypeTranslations } from '@home/components/widget/lib/chart/chart.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-chart-fill-settings', @@ -67,7 +68,8 @@ export class ChartFillSettingsComponent implements OnInit, ControlValueAccessor public fillSettingsFormGroup: UntypedFormGroup; constructor(protected store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -79,10 +81,14 @@ export class ChartFillSettingsComponent implements OnInit, ControlValueAccessor end: [null, [Validators.min(0), Validators.max(100)]] }) }); - this.fillSettingsFormGroup.valueChanges.subscribe(() => { + this.fillSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.fillSettingsFormGroup.get('type').valueChanges.subscribe(() => { + this.fillSettingsFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); for (const type of chartFillTypes) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts index 91fe61caa3..590d691bec 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -31,6 +31,7 @@ import { import { merge } from 'rxjs'; import { coerceBoolean } from '@shared/decorators/coercion'; import { WidgetService } from '@core/http/widget.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-time-series-chart-axis-settings', @@ -85,7 +86,8 @@ export class TimeSeriesChartAxisSettingsComponent implements OnInit, ControlValu public axisSettingsFormGroup: UntypedFormGroup; constructor(private fb: UntypedFormBuilder, - private widgetService: WidgetService) { + private widgetService: WidgetService, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -121,15 +123,19 @@ export class TimeSeriesChartAxisSettingsComponent implements OnInit, ControlValu } else if (this.axisType === 'xAxis') { this.axisSettingsFormGroup.addControl('ticksFormat', this.fb.control(null, [])); } - this.axisSettingsFormGroup.valueChanges.subscribe(() => { + this.axisSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); merge(this.axisSettingsFormGroup.get('show').valueChanges, this.axisSettingsFormGroup.get('showTickLabels').valueChanges, this.axisSettingsFormGroup.get('showTicks').valueChanges, this.axisSettingsFormGroup.get('showLine').valueChanges, - this.axisSettingsFormGroup.get('showSplitLines').valueChanges) - .subscribe(() => { + this.axisSettingsFormGroup.get('showSplitLines').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); } @@ -157,7 +163,9 @@ export class TimeSeriesChartAxisSettingsComponent implements OnInit, ControlValu value, {emitEvent: false} ); this.updateValidators(); - this.axisSettingsFormGroup.get('show').valueChanges.subscribe((show) => { + this.axisSettingsFormGroup.get('show').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((show) => { this.settingsExpanded = show; }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-grid-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-grid-settings.component.ts index fd88ce2a8f..a939567b0d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-grid-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-grid-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -24,6 +24,7 @@ import { } from '@angular/forms'; import { TimeSeriesChartGridSettings } from '@home/components/widget/lib/chart/time-series-chart.models'; import { WidgetService } from '@core/http/widget.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-time-series-chart-grid-settings', @@ -51,7 +52,8 @@ export class TimeSeriesChartGridSettingsComponent implements OnInit, ControlValu public gridSettingsFormGroup: UntypedFormGroup; constructor(private fb: UntypedFormBuilder, - private widgetService: WidgetService,) { + private widgetService: WidgetService, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -61,11 +63,14 @@ export class TimeSeriesChartGridSettingsComponent implements OnInit, ControlValu borderWidth: [null, [Validators.min(0)]], borderColor: [null, []], }); - this.gridSettingsFormGroup.valueChanges.subscribe(() => { + this.gridSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.gridSettingsFormGroup.get('show').valueChanges - .subscribe(() => { + this.gridSettingsFormGroup.get('show').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); } @@ -93,7 +98,9 @@ export class TimeSeriesChartGridSettingsComponent implements OnInit, ControlValu value, {emitEvent: false} ); this.updateValidators(); - this.gridSettingsFormGroup.get('show').valueChanges.subscribe((show) => { + this.gridSettingsFormGroup.get('show').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((show) => { this.settingsExpanded = show; }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-state-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-state-row.component.ts index 97c87432c2..3879203939 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-state-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-state-row.component.ts @@ -17,6 +17,7 @@ import { ChangeDetectorRef, Component, + DestroyRef, EventEmitter, forwardRef, Input, @@ -37,6 +38,7 @@ import { timeSeriesStateSourceTypes, timeSeriesStateSourceTypeTranslations } from '@home/components/widget/lib/chart/time-series-chart.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-time-series-chart-state-row', @@ -72,7 +74,8 @@ export class TimeSeriesChartStateRowComponent implements ControlValueAccessor, O private propagateChange = (_val: any) => {}; constructor(private fb: UntypedFormBuilder, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { } ngOnInit() { @@ -84,10 +87,14 @@ export class TimeSeriesChartStateRowComponent implements ControlValueAccessor, O sourceRangeFrom: [null, []], sourceRangeTo: [null, []] }); - this.stateFormGroup.valueChanges.subscribe( + this.stateFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => this.updateModel() ); - this.stateFormGroup.get('sourceType').valueChanges.subscribe(() => { + this.stateFormGroup.get('sourceType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-states-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-states-panel.component.ts index 8c30582425..c7229c2a65 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-states-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-states-panel.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -32,6 +32,7 @@ import { timeSeriesChartStateValid, timeSeriesChartStateValidator } from '@home/components/widget/lib/chart/time-series-chart.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-time-series-chart-states-panel', @@ -60,14 +61,17 @@ export class TimeSeriesChartStatesPanelComponent implements ControlValueAccessor private propagateChange = (_val: any) => {}; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit() { this.statesFormGroup = this.fb.group({ states: [this.fb.array([]), []] }); - this.statesFormGroup.valueChanges.subscribe( + this.statesFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => { let states: TimeSeriesChartStateSettings[] = this.statesFormGroup.get('states').value; if (states) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts index f834066a13..58b5554483 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts @@ -17,6 +17,7 @@ import { ChangeDetectorRef, Component, + DestroyRef, EventEmitter, forwardRef, Input, @@ -52,6 +53,7 @@ import { ValueSourceType, ValueSourceTypeTranslation } from '@shared/models/widget-settings.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-time-series-chart-threshold-row', @@ -121,7 +123,8 @@ export class TimeSeriesChartThresholdRowComponent implements ControlValueAccesso constructor(private fb: UntypedFormBuilder, private thresholdsPanel: TimeSeriesChartThresholdsPanelComponent, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { } ngOnInit() { @@ -137,16 +140,24 @@ export class TimeSeriesChartThresholdRowComponent implements ControlValueAccesso this.latestKeyFormControl = this.fb.control(null, [Validators.required]); this.entityKeyFormControl = this.fb.control(null, [Validators.required]); this.thresholdSettingsFormControl = this.fb.control(null); - this.thresholdFormGroup.valueChanges.subscribe( + this.thresholdFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => this.updateModel() ); - this.latestKeyFormControl.valueChanges.subscribe( + this.latestKeyFormControl.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => this.updateModel() ); - this.entityKeyFormControl.valueChanges.subscribe( + this.entityKeyFormControl.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => this.updateModel() ); - this.thresholdSettingsFormControl.valueChanges.subscribe((thresholdSettings: Partial) => { + this.thresholdSettingsFormControl.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((thresholdSettings: Partial) => { this.modelValue = {...this.modelValue, ...thresholdSettings}; this.thresholdFormGroup.patchValue( { @@ -158,7 +169,9 @@ export class TimeSeriesChartThresholdRowComponent implements ControlValueAccesso {emitEvent: false}); this.propagateChange(this.modelValue); }); - this.thresholdFormGroup.get('type').valueChanges.subscribe(() => { + this.thresholdFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts index eb0a2d79c8..07075bc8de 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { @@ -33,6 +33,7 @@ import { chartShapes, chartShapeTranslations } from '@home/components/widget/lib/chart/chart.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-time-series-chart-threshold-settings-panel', @@ -81,7 +82,8 @@ export class TimeSeriesChartThresholdSettingsPanelComponent implements OnInit { thresholdSettingsFormGroup: UntypedFormGroup; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -108,7 +110,10 @@ export class TimeSeriesChartThresholdSettingsPanelComponent implements OnInit { merge(this.thresholdSettingsFormGroup.get('showLabel').valueChanges, this.thresholdSettingsFormGroup.get('enableLabelBackground').valueChanges, this.thresholdSettingsFormGroup.get('startSymbol').valueChanges, - this.thresholdSettingsFormGroup.get('endSymbol').valueChanges).subscribe(() => { + this.thresholdSettingsFormGroup.get('endSymbol').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); this.updateValidators(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts index 62d2de8402..727783415a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -40,6 +40,7 @@ import { DataKey, Datasource, WidgetConfig } from '@shared/models/widget.models' import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { coerceBoolean } from '@shared/decorators/coercion'; import { ValueSourceType } from '@shared/models/widget-settings.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-time-series-chart-thresholds-panel', @@ -87,14 +88,17 @@ export class TimeSeriesChartThresholdsPanelComponent implements ControlValueAcce private propagateChange = (_val: any) => {}; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit() { this.thresholdsFormGroup = this.fb.group({ thresholds: [this.fb.array([]), []] }); - this.thresholdsFormGroup.valueChanges.subscribe( + this.thresholdsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => { let thresholds: TimeSeriesChartThreshold[] = this.thresholdsFormGroup.get('thresholds').value; if (thresholds) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts index 9879ffe877..4c2a0f7490 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts @@ -14,7 +14,16 @@ /// limitations under the License. /// -import { Component, EventEmitter, forwardRef, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { + Component, + DestroyRef, + EventEmitter, + forwardRef, + Input, + OnInit, + Output, + ViewEncapsulation +} from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -37,6 +46,7 @@ import { import { mergeDeep } from '@core/utils'; import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-time-series-chart-y-axes-panel', @@ -76,14 +86,17 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor, private propagateChange = (_val: any) => {}; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit() { this.yAxesFormGroup = this.fb.group({ axes: [this.fb.array([]), []] }); - this.yAxesFormGroup.valueChanges.subscribe( + this.yAxesFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => { let axes: TimeSeriesChartYAxisSettings[] = this.yAxesFormGroup.get('axes').value; for (let i = 0; i < axes.length; i++) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts index 3fcde90cef..7bb1ff1f07 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts @@ -17,6 +17,7 @@ import { ChangeDetectorRef, Component, + DestroyRef, EventEmitter, forwardRef, Input, @@ -40,6 +41,7 @@ import { } from '@home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings-panel.component'; import { deepClone } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-time-series-chart-y-axis-row', @@ -81,7 +83,8 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O private popoverService: TbPopoverService, private renderer: Renderer2, private viewContainerRef: ViewContainerRef, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { } ngOnInit() { @@ -94,10 +97,14 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O max: [null, []], show: [null, []] }); - this.axisFormGroup.valueChanges.subscribe( + this.axisFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => this.updateModel() ); - this.axisFormGroup.get('show').valueChanges.subscribe(() => { + this.axisFormGroup.get('show').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts index 8601c48159..0107bbaec7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -30,6 +30,7 @@ import { } from '@home/components/widget/lib/chart/time-series-chart.models'; import { merge } from 'rxjs'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-time-series-no-aggregation-bar-width-settings', @@ -64,7 +65,8 @@ export class TimeSeriesNoAggregationBarWidthSettingsComponent implements OnInit, public barWidthSettingsFormGroup: UntypedFormGroup; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -81,13 +83,17 @@ export class TimeSeriesNoAggregationBarWidthSettingsComponent implements OnInit, absoluteWidth: [null, [Validators.required, Validators.min(100)]] }) }); - this.barWidthSettingsFormGroup.valueChanges.subscribe(() => { + this.barWidthSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); merge(this.barWidthSettingsFormGroup.get('strategy').valueChanges, this.barWidthSettingsFormGroup.get('groupWidth.relative').valueChanges, - this.barWidthSettingsFormGroup.get('barWidth.relative').valueChanges) - .subscribe(() => { + this.barWidthSettingsFormGroup.get('barWidth.relative').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts index 70111a1448..4ef6939e67 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { ColorSettings, @@ -34,6 +34,7 @@ import { IAliasController } from '@core/api/widget-api.models'; import { coerceBoolean } from '@shared/decorators/coercion'; import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; import { Datasource } from '@shared/models/widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-color-settings-panel', @@ -91,7 +92,8 @@ export class ColorSettingsPanelComponent extends PageComponent implements OnInit constructor(private fb: UntypedFormBuilder, private widgetService: WidgetService, - protected store: Store) { + protected store: Store, + private destroyRef: DestroyRef) { super(store); } @@ -105,7 +107,9 @@ export class ColorSettingsPanelComponent extends PageComponent implements OnInit colorFunction: [this.colorSettings?.colorFunction, []] } ); - this.colorSettingsFormGroup.get('type').valueChanges.subscribe(() => { + this.colorSettingsFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { setTimeout(() => {this.popover?.updatePosition();}, 0); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.ts index b700dce8f3..9613c4888a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.ts @@ -32,12 +32,13 @@ import { CountWidgetSettings, entityCountCardLayoutImages } from '@home/components/widget/lib/count/count-widget.models'; import {PageComponent} from '@shared/components/page.component'; -import {Component, forwardRef, Input, OnInit} from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { coerceBoolean } from '@shared/decorators/coercion'; import { valueCardLayoutImages, valueCardLayoutTranslations } from '@home/components/widget/lib/cards/value-card-widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-count-widget-settings', @@ -70,7 +71,8 @@ export class CountWidgetSettingsComponent extends PageComponent implements OnIni countWidgetConfigForm: UntypedFormGroup; constructor(protected store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -104,7 +106,9 @@ export class CountWidgetSettingsComponent extends PageComponent implements OnIni chevronSizeUnit: [null, []], chevronColor: [null, []], }); - this.countWidgetConfigForm.valueChanges.subscribe(() => { + this.countWidgetConfigForm.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); for (const trigger of ['showLabel', 'showIcon', 'showIconBackground', 'showChevron']) { @@ -113,7 +117,9 @@ export class CountWidgetSettingsComponent extends PageComponent implements OnIni for (const part of path) { control = this.countWidgetConfigForm.get(part); } - control.valueChanges.subscribe(() => { + control.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-size-input.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-size-input.component.ts index 099778e924..7a6d4b3c0c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-size-input.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-size-input.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, HostBinding, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, HostBinding, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALIDATORS, @@ -28,6 +28,7 @@ import { import { cssUnit, resolveCssSize } from '@shared/models/widget-settings.models'; import { coerceBoolean } from '@shared/decorators/coercion'; import { isDefinedAndNotNull } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-css-size-input', @@ -82,14 +83,17 @@ export class CssSizeInputComponent implements OnInit, ControlValueAccessor, Vali private propagateChange = null; - constructor(private fb: UntypedFormBuilder) {} + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) {} ngOnInit(): void { this.cssSizeFormGroup = this.fb.group({ size: [null, this.required ? [Validators.required, Validators.min(0)] : [Validators.min(0)]], unit: [null, []] }); - this.cssSizeFormGroup.valueChanges.subscribe((value: {size: number; unit: cssUnit}) => { + this.cssSizeFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((value: {size: number; unit: cssUnit}) => { this.updateModel(value); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.ts index 588871b7b9..bdb5a60960 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.ts @@ -14,10 +14,11 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; import { cssUnit, cssUnits } from '@shared/models/widget-settings.models'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-css-unit-select', @@ -51,11 +52,13 @@ export class CssUnitSelectComponent implements OnInit, ControlValueAccessor { private propagateChange = null; - constructor() {} + constructor(private destroyRef: DestroyRef) {} ngOnInit(): void { this.cssUnitFormControl = new UntypedFormControl(); - this.cssUnitFormControl.valueChanges.subscribe((value: cssUnit) => { + this.cssUnitFormControl.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((value: cssUnit) => { this.updateModel(value); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.ts index 144f02fe46..bf81fe8243 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.ts @@ -14,7 +14,16 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit, Renderer2, ViewChild, ViewContainerRef } from '@angular/core'; +import { + Component, + DestroyRef, + forwardRef, + Input, + OnInit, + Renderer2, + ViewChild, + ViewContainerRef +} from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; import { AutoDateFormatSettings, @@ -36,6 +45,7 @@ import { coerceBoolean } from '@shared/decorators/coercion'; import { AutoDateFormatSettingsPanelComponent } from '@home/components/widget/lib/settings/common/auto-date-format-settings-panel.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-date-format-select', @@ -81,14 +91,17 @@ export class DateFormatSelectComponent implements OnInit, ControlValueAccessor { private date: DatePipe, private popoverService: TbPopoverService, private renderer: Renderer2, - private viewContainerRef: ViewContainerRef) {} + private viewContainerRef: ViewContainerRef, + private destroyRef: DestroyRef) {} ngOnInit(): void { const targetDateFormats = this.includeAuto ? dateFormatsWithAuto : dateFormats; this.dateFormatList = this.excludeLastUpdateAgo ? targetDateFormats.filter(format => !format.lastUpdateAgo) : dateFormats; this.dateFormatFormControl = new UntypedFormControl(); - this.dateFormatFormControl.valueChanges.subscribe((value: DateFormatSettings) => { + this.dateFormatFormControl.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((value: DateFormatSettings) => { this.updateModel(value); if (value?.custom) { setTimeout(() => { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.ts index d187644370..5f707adb8c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { DateFormatSettings } from '@shared/models/widget-settings.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; @@ -22,6 +22,7 @@ import { UntypedFormControl, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { DatePipe } from '@angular/common'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-date-format-settings-panel', @@ -46,13 +47,16 @@ export class DateFormatSettingsPanelComponent extends PageComponent implements O previewText = ''; constructor(private date: DatePipe, - protected store: Store) { + protected store: Store, + private destroyRef: DestroyRef) { super(store); } ngOnInit(): void { this.dateFormatFormControl = new UntypedFormControl(this.dateFormat.format, [Validators.required]); - this.dateFormatFormControl.valueChanges.subscribe((value: string) => { + this.dateFormatFormControl.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((value: string) => { this.previewText = this.date.transform(Date.now(), value); }); this.previewText = this.date.transform(Date.now(), this.dateFormat.format); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.ts index d7b5c2f7a1..1c4bfefe33 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.ts @@ -15,7 +15,8 @@ /// import { - Component, DestroyRef, + Component, + DestroyRef, forwardRef, HostBinding, Input, @@ -47,9 +48,9 @@ import { DynamicFormPropertyRowComponent } from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component'; import { coerceBoolean } from '@shared/decorators/coercion'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ImportExportService } from '@shared/import-export/import-export.service'; import { DialogService } from '@core/services/dialog.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-dynamic-form-properties', @@ -116,10 +117,10 @@ export class DynamicFormPropertiesComponent implements ControlValueAccessor, OnI private propagateChange = (_val: any) => {}; constructor(private fb: UntypedFormBuilder, - private destroyRef: DestroyRef, private translate: TranslateService, private importExportService: ImportExportService, - private dialogService: DialogService) { + private dialogService: DialogService, + private destroyRef: DestroyRef) { } ngOnInit() { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.ts index 2d18684227..2cd1d10007 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.ts @@ -16,7 +16,8 @@ import { ChangeDetectorRef, - Component, DestroyRef, + Component, + DestroyRef, ElementRef, EventEmitter, forwardRef, @@ -105,12 +106,12 @@ export class DynamicFormPropertyRowComponent implements ControlValueAccessor, On private propagateChange = (_val: any) => {}; constructor(private fb: UntypedFormBuilder, - private destroyRef: DestroyRef, private cd: ChangeDetectorRef, private popoverService: TbPopoverService, private renderer: Renderer2, private viewContainerRef: ViewContainerRef, - private propertiesComponent: DynamicFormPropertiesComponent) { + private propertiesComponent: DynamicFormPropertiesComponent, + private destroyRef: DestroyRef) { } ngOnInit() { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/entity-alias-input.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/entity-alias-input.component.ts index d36a4843a1..a5aa7cb99e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/entity-alias-input.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/entity-alias-input.component.ts @@ -16,6 +16,7 @@ import { Component, + DestroyRef, ElementRef, forwardRef, HostBinding, @@ -35,6 +36,7 @@ import { coerceBoolean } from '@shared/decorators/coercion'; import { IAliasController } from '@core/api/widget-api.models'; import { map, mergeMap } from 'rxjs/operators'; import { Observable, of } from 'rxjs'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-entity-alias-input', @@ -76,12 +78,15 @@ export class EntityAliasInputComponent implements ControlValueAccessor, OnInit { private propagateChange = (_val: any) => {}; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit() { this.entityAliasFormControl = this.fb.control(null, this.required ? [Validators.required] : []); - this.entityAliasFormControl.valueChanges.subscribe( + this.entityAliasFormControl.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => this.updateModel() ); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.ts index 02266a34ee..13a4a93648 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.ts @@ -16,6 +16,7 @@ import { Component, + DestroyRef, ElementRef, EventEmitter, Input, @@ -42,6 +43,7 @@ import { AppState } from '@core/core.state'; import { Observable } from 'rxjs'; import { map, startWith, tap } from 'rxjs/operators'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-font-settings-panel', @@ -101,7 +103,8 @@ export class FontSettingsPanelComponent extends PageComponent implements OnInit previewStyle: ComponentStyle = {}; constructor(private fb: UntypedFormBuilder, - protected store: Store) { + protected store: Store, + private destroyRef: DestroyRef) { super(store); } @@ -118,7 +121,9 @@ export class FontSettingsPanelComponent extends PageComponent implements OnInit } ); this.updatePreviewStyle(this.font); - this.fontFormGroup.valueChanges.subscribe((font: Font) => { + this.fontFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((font: Font) => { if (this.fontFormGroup.valid) { this.updatePreviewStyle(font); setTimeout(() => {this.popover?.updatePosition();}, 0); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/indicator/status-widget-state-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/indicator/status-widget-state-settings.component.ts index 8425264d89..5776b6fe66 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/indicator/status-widget-state-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/indicator/status-widget-state-settings.component.ts @@ -14,13 +14,14 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { merge } from 'rxjs'; import { StatusWidgetLayout, StatusWidgetStateSettings } from '@home/components/widget/lib/indicator/status-widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-status-widget-state-settings', @@ -50,7 +51,8 @@ export class StatusWidgetStateSettingsComponent implements OnInit, OnChanges, Co public stateSettingsFormGroup: UntypedFormGroup; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -71,12 +73,16 @@ export class StatusWidgetStateSettingsComponent implements OnInit, OnChanges, Co secondaryColorDisabled: [null, []], backgroundDisabled: [null, []] }); - this.stateSettingsFormGroup.valueChanges.subscribe(() => { + this.stateSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); merge(this.stateSettingsFormGroup.get('showLabel').valueChanges, - this.stateSettingsFormGroup.get('showStatus').valueChanges) - .subscribe(() => { + this.stateSettingsFormGroup.get('showStatus').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.ts index f6113a9e85..929e6c5c37 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.ts @@ -14,7 +14,17 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { + ChangeDetectorRef, + Component, + DestroyRef, + forwardRef, + Input, + OnChanges, + OnDestroy, + OnInit, + SimpleChanges +} from '@angular/core'; import { ControlValueAccessor, NG_VALIDATORS, @@ -44,6 +54,7 @@ import { Observable, of } from 'rxjs'; import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models'; import { ImageService } from '@core/http/image.service'; import { map } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-scada-symbol-object-settings', @@ -62,7 +73,7 @@ import { map } from 'rxjs/operators'; } ] }) -export class ScadaSymbolObjectSettingsComponent implements OnInit, OnChanges, ControlValueAccessor, Validator { +export class ScadaSymbolObjectSettingsComponent implements OnInit, OnChanges, ControlValueAccessor, Validator, OnDestroy { ScadaSymbolBehaviorType = ScadaSymbolBehaviorType; @@ -102,7 +113,8 @@ export class ScadaSymbolObjectSettingsComponent implements OnInit, OnChanges, Co constructor(protected store: Store, private fb: UntypedFormBuilder, private imageService: ImageService, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -110,7 +122,9 @@ export class ScadaSymbolObjectSettingsComponent implements OnInit, OnChanges, Co behavior: this.fb.group({}), properties: [{}, []] }); - this.scadaSymbolObjectSettingsFormGroup.valueChanges.subscribe(() => { + this.scadaSymbolObjectSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); this.loadMetadata(); @@ -127,6 +141,13 @@ export class ScadaSymbolObjectSettingsComponent implements OnInit, OnChanges, Co } } + ngOnDestroy() { + if (this.validatorSubscription) { + this.validatorSubscription.unsubscribe(); + this.validatorSubscription = null; + } + } + registerOnChange(fn: any): void { this.propagateChange = fn; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.ts index eb714597b2..a541291b08 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, forwardRef, Input, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -36,6 +36,7 @@ import { ValueSourceTypes, ValueSourceTypeTranslation } from '@shared/models/widget-settings.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-value-source-data-key', @@ -81,7 +82,8 @@ export class ValueSourceDataKeyComponent extends PageComponent implements OnInit constructor(protected store: Store, private fb: UntypedFormBuilder, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { super(store); } @@ -93,16 +95,24 @@ export class ValueSourceDataKeyComponent extends PageComponent implements OnInit }); this.latestKeyFormControl = this.fb.control(null, [Validators.required]); this.entityKeyFormControl = this.fb.control(null, [Validators.required]); - this.valueSourceFormGroup.valueChanges.subscribe( + this.valueSourceFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => this.updateModel() ); - this.latestKeyFormControl.valueChanges.subscribe( + this.latestKeyFormControl.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => this.updateModel() ); - this.entityKeyFormControl.valueChanges.subscribe( + this.entityKeyFormControl.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => this.updateModel() ); - this.valueSourceFormGroup.get('type').valueChanges.subscribe(() => { + this.valueSourceFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts index f290a05083..28bcb1dd4b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, DestroyRef, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; @@ -25,6 +25,7 @@ import { catchError, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/op import { DataKey } from '@shared/models/widget.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { EntityService } from '@core/http/entity.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export declare type ValueSource = 'predefinedValue' | 'entityAttribute'; @@ -84,7 +85,8 @@ export class ValueSourceComponent extends PageComponent implements OnInit, Contr constructor(protected store: Store, private entityService: EntityService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -95,10 +97,14 @@ export class ValueSourceComponent extends PageComponent implements OnInit, Contr attribute: [null, []], value: [null, []] }); - this.valueSourceFormGroup.valueChanges.subscribe(() => { + this.valueSourceFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.valueSourceFormGroup.get('valueSource').valueChanges.subscribe(() => { + this.valueSourceFormGroup.get('valueSource').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-font.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-font.component.ts index f3c84926bd..ef3222cfbd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-font.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-font.component.ts @@ -14,12 +14,13 @@ /// limitations under the License. /// -import { Component, forwardRef, HostBinding, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, HostBinding, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface WidgetFont { family: string; @@ -63,7 +64,8 @@ export class WidgetFontComponent extends PageComponent implements OnInit, Contro constructor(protected store: Store, private translate: TranslateService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -78,7 +80,9 @@ export class WidgetFontComponent extends PageComponent implements OnInit, Contro if (this.hasShadowColor) { this.widgetFontFormGroup.addControl('shadowColor', this.fb.control(null, [])); } - this.widgetFontFormGroup.valueChanges.subscribe(() => { + this.widgetFontFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/device-key-autocomplete.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/device-key-autocomplete.component.ts index 9c0691faed..470e7cd908 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/device-key-autocomplete.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/device-key-autocomplete.component.ts @@ -14,7 +14,17 @@ /// limitations under the License. /// -import { Component, ElementRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core'; +import { + Component, + DestroyRef, + ElementRef, + forwardRef, + Input, + OnChanges, + OnInit, + SimpleChanges, + ViewChild +} from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -36,6 +46,7 @@ import { EntityType } from '@shared/models/entity-type.models'; import { EntityFilter, singleEntityFilterFromDeviceId } from '@shared/models/query/query.models'; import { AliasFilterType } from '@shared/models/alias.models'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-device-key-autocomplete', @@ -101,7 +112,8 @@ export class DeviceKeyAutocompleteComponent extends PageComponent implements OnI constructor(protected store: Store, private entityService: EntityService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -109,7 +121,9 @@ export class DeviceKeyAutocompleteComponent extends PageComponent implements OnI this.deviceKeyFormGroup = this.fb.group({ key: [null, this.required ? [Validators.required] : []] }); - this.deviceKeyFormGroup.valueChanges.subscribe(() => { + this.deviceKeyFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/rpc-button-style.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/rpc-button-style.component.ts index 5263aa8016..6cf3fe7bf8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/rpc-button-style.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/rpc-button-style.component.ts @@ -14,11 +14,12 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface RpcButtonStyle { isRaised: boolean; @@ -51,7 +52,8 @@ export class RpcButtonStyleComponent extends PageComponent implements OnInit, Co public rpcButtonStyleFormGroup: UntypedFormGroup; constructor(protected store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -62,7 +64,9 @@ export class RpcButtonStyleComponent extends PageComponent implements OnInit, Co bgColor: [null, []], textColor: [null, []] }); - this.rpcButtonStyleFormGroup.valueChanges.subscribe(() => { + this.rpcButtonStyleFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/switch-rpc-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/switch-rpc-settings.component.ts index 764bfcfce9..7108eabbf6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/switch-rpc-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/switch-rpc-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, DestroyRef, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { ControlValueAccessor, NG_VALIDATORS, @@ -34,6 +34,7 @@ import { WidgetService } from '@core/http/widget.service'; import { IAliasController } from '@core/api/widget-api.models'; import { EntityService } from '@core/http/entity.service'; import { TargetDevice } from '@shared/models/widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export declare type RpcRetrieveValueMethod = 'none' | 'rpc' | 'attribute' | 'timeseries'; @@ -107,7 +108,8 @@ export class SwitchRpcSettingsComponent extends PageComponent implements OnInit, private translate: TranslateService, private widgetService: WidgetService, private entityService: EntityService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -139,13 +141,19 @@ export class SwitchRpcSettingsComponent extends PageComponent implements OnInit, requestPersistent: [false, []], persistentPollingInterval: [5000, [Validators.min(1000)]], }); - this.switchRpcSettingsFormGroup.get('retrieveValueMethod').valueChanges.subscribe(() => { + this.switchRpcSettingsFormGroup.get('retrieveValueMethod').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.switchRpcSettingsFormGroup.get('requestPersistent').valueChanges.subscribe(() => { + this.switchRpcSettingsFormGroup.get('requestPersistent').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.switchRpcSettingsFormGroup.valueChanges.subscribe(() => { + this.switchRpcSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts index fcbeded87d..200c4d749a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts @@ -15,7 +15,7 @@ /// import { ValueSourceProperty } from '@home/components/widget/lib/settings/common/value-source.component'; -import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; @@ -24,6 +24,7 @@ import { TranslateService } from '@ngx-translate/core'; import { IAliasController } from '@core/api/widget-api.models'; import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; import { Datasource } from '@shared/models/widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-tick-value', @@ -62,7 +63,8 @@ export class TickValueComponent extends PageComponent implements OnInit, Control constructor(protected store: Store, private translate: TranslateService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -70,7 +72,9 @@ export class TickValueComponent extends PageComponent implements OnInit, Control this.tickValueFormGroup = this.fb.group({ tickValue: [null, []] }); - this.tickValueFormGroup.valueChanges.subscribe(() => { + this.tickValueFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gpio/gpio-item.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gpio/gpio-item.component.ts index 22f7105b5d..fd46ccffc2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gpio/gpio-item.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gpio/gpio-item.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -28,6 +28,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; import { isNumber } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface GpioItem { pin: number; @@ -87,7 +88,8 @@ export class GpioItemComponent extends PageComponent implements OnInit, ControlV constructor(protected store: Store, private translate: TranslateService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -101,7 +103,9 @@ export class GpioItemComponent extends PageComponent implements OnInit, ControlV if (this.hasColor) { this.gpioItemFormGroup.addControl('color', this.fb.control(null, [Validators.required])); } - this.gpioItemFormGroup.valueChanges.subscribe(() => { + this.gpioItemFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts index ad424069a8..70a163ac1a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts @@ -53,6 +53,7 @@ import { ResourcesService } from '@core/services/resources.service'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { UtilsService } from '@core/services/utils.service'; import { EntityService } from '@core/http/entity.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'liquid-level-card-widget-settings', @@ -189,7 +190,9 @@ export class LiquidLevelCardWidgetSettingsComponent extends WidgetSettingsCompon tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], }); - this.levelCardWidgetSettingsForm.get('selectedShape').valueChanges.subscribe(() => { + this.levelCardWidgetSettingsForm.get('selectedShape').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.cd.detectChanges(); this.layoutsImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges(); }); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/datakey-select-option.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/datakey-select-option.component.ts index c620fa9e28..5c4e9bbdc3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/datakey-select-option.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/datakey-select-option.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -27,6 +27,7 @@ import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface DataKeySelectOption { value: string; @@ -74,7 +75,8 @@ export class DataKeySelectOptionComponent extends PageComponent implements OnIni constructor(protected store: Store, private translate: TranslateService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -83,7 +85,9 @@ export class DataKeySelectOptionComponent extends PageComponent implements OnIni value: [null, [Validators.required]], label: [null, []] }); - this.selectOptionFormGroup.valueChanges.subscribe(() => { + this.selectOptionFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-attribute-general-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-attribute-general-settings.component.ts index 21ea72be69..1171864b3c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-attribute-general-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-attribute-general-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -28,6 +28,7 @@ import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface UpdateAttributeGeneralSettings { widgetTitle: string; @@ -85,7 +86,8 @@ export class UpdateAttributeGeneralSettingsComponent extends PageComponent imple constructor(protected store: Store, private translate: TranslateService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -99,14 +101,20 @@ export class UpdateAttributeGeneralSettingsComponent extends PageComponent imple }); if (this.hasLabelValue) { this.updateAttributeGeneralSettingsFormGroup.addControl('labelValue', this.fb.control('', [])); - this.updateAttributeGeneralSettingsFormGroup.get('showLabel').valueChanges.subscribe(() => { + this.updateAttributeGeneralSettingsFormGroup.get('showLabel').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); } - this.updateAttributeGeneralSettingsFormGroup.get('isRequired').valueChanges.subscribe(() => { + this.updateAttributeGeneralSettingsFormGroup.get('isRequired').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.updateAttributeGeneralSettingsFormGroup.valueChanges.subscribe(() => { + this.updateAttributeGeneralSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.ts index f8e9b23b20..704f2b3b5b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -36,6 +36,7 @@ import { } from '@home/components/widget/lib/maps/map-models'; import { WidgetService } from '@core/http/widget.service'; import { Widget } from '@shared/models/widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-circle-settings', @@ -77,7 +78,8 @@ export class CircleSettingsComponent extends PageComponent implements OnInit, Co constructor(protected store: Store, private translate: TranslateService, private widgetService: WidgetService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -106,28 +108,44 @@ export class CircleSettingsComponent extends PageComponent implements OnInit, Co useCircleStrokeColorFunction: [null, []], circleStrokeColorFunction: [null, []] }); - this.circleSettingsFormGroup.valueChanges.subscribe(() => { + this.circleSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.circleSettingsFormGroup.get('showCircle').valueChanges.subscribe(() => { + this.circleSettingsFormGroup.get('showCircle').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.circleSettingsFormGroup.get('showCircleLabel').valueChanges.subscribe(() => { + this.circleSettingsFormGroup.get('showCircleLabel').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.circleSettingsFormGroup.get('useCircleLabelFunction').valueChanges.subscribe(() => { + this.circleSettingsFormGroup.get('useCircleLabelFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.circleSettingsFormGroup.get('showCircleTooltip').valueChanges.subscribe(() => { + this.circleSettingsFormGroup.get('showCircleTooltip').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.circleSettingsFormGroup.get('useCircleTooltipFunction').valueChanges.subscribe(() => { + this.circleSettingsFormGroup.get('useCircleTooltipFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.circleSettingsFormGroup.get('useCircleFillColorFunction').valueChanges.subscribe(() => { + this.circleSettingsFormGroup.get('useCircleFillColorFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.circleSettingsFormGroup.get('useCircleStrokeColorFunction').valueChanges.subscribe(() => { + this.circleSettingsFormGroup.get('useCircleStrokeColorFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/common-map-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/common-map-settings.component.ts index 6e5c2bc255..09df84593d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/common-map-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/common-map-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -31,6 +31,7 @@ import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; import { CommonMapSettings, MapProviders } from '@home/components/widget/lib/maps/map-models'; import { Widget } from '@shared/models/widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-common-map-settings', @@ -70,7 +71,8 @@ export class CommonMapSettingsComponent extends PageComponent implements OnInit, constructor(protected store: Store, private translate: TranslateService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -89,7 +91,9 @@ export class CommonMapSettingsComponent extends PageComponent implements OnInit, useDefaultCenterPosition: [null, []], mapPageSize: [null, [Validators.min(1), Validators.required]] }); - this.commonMapSettingsFormGroup.valueChanges.subscribe(() => { + this.commonMapSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/datasources-key-autocomplete.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/datasources-key-autocomplete.component.ts index b0b605c951..aca62b6f1e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/datasources-key-autocomplete.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/datasources-key-autocomplete.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, DestroyRef, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; @@ -25,6 +25,7 @@ import { map, mergeMap } from 'rxjs/operators'; import { Datasource } from '@shared/models/widget.models'; import { EntityService } from '@core/http/entity.service'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-datasources-key-autocomplete', @@ -72,7 +73,8 @@ export class DatasourcesKeyAutocompleteComponent extends PageComponent implement constructor(protected store: Store, private translate: TranslateService, private entityService: EntityService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -80,7 +82,9 @@ export class DatasourcesKeyAutocompleteComponent extends PageComponent implement this.keyFormGroup = this.fb.group({ key: [null, this.required ? [Validators.required] : []] }); - this.keyFormGroup.valueChanges.subscribe(() => { + this.keyFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/google-map-provider-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/google-map-provider-settings.component.ts index bc175a4bbf..dde7175b1e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/google-map-provider-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/google-map-provider-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -34,6 +34,7 @@ import { GoogleMapType, googleMapTypeProviderTranslationMap } from '@home/components/widget/lib/maps/map-models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-google-map-provider-settings', @@ -69,7 +70,8 @@ export class GoogleMapProviderSettingsComponent extends PageComponent implements constructor(protected store: Store, private translate: TranslateService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -78,7 +80,9 @@ export class GoogleMapProviderSettingsComponent extends PageComponent implements gmApiKey: [null, [Validators.required]], gmDefaultMapType: [null, [Validators.required]] }); - this.providerSettingsFormGroup.valueChanges.subscribe(() => { + this.providerSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/here-map-provider-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/here-map-provider-settings.component.ts index b3b627de32..a5b63b26ee 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/here-map-provider-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/here-map-provider-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, FormBuilder, @@ -35,6 +35,7 @@ import { hereMapProviderTranslationMap } from '@home/components/widget/lib/maps/map-models'; import { isDefinedAndNotNull } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-here-map-provider-settings', @@ -70,7 +71,8 @@ export class HereMapProviderSettingsComponent extends PageComponent implements O constructor(protected store: Store, private translate: TranslateService, - private fb: FormBuilder) { + private fb: FormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -84,7 +86,9 @@ export class HereMapProviderSettingsComponent extends PageComponent implements O apiKey: [null, [Validators.required]] }) }); - this.providerSettingsFormGroup.get('credentials.useV3').valueChanges.subscribe(value => { + this.providerSettingsFormGroup.get('credentials.useV3').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(value => { if (value) { this.providerSettingsFormGroup.get('credentials.apiKey').enable({emitEvent: false}); this.providerSettingsFormGroup.get('credentials.app_id').disable({emitEvent: false}); @@ -95,7 +99,9 @@ export class HereMapProviderSettingsComponent extends PageComponent implements O this.providerSettingsFormGroup.get('credentials.app_code').enable({emitEvent: false}); } }); - this.providerSettingsFormGroup.valueChanges.subscribe(() => { + this.providerSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.ts index 51cc5ae6d7..9c655137a0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, DestroyRef, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -35,6 +35,7 @@ import { catchError, map, mergeMap, publishReplay, refCount, startWith, tap } fr import { DataKey } from '@shared/models/widget.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { EntityService } from '@core/http/entity.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-image-map-provider-settings', @@ -85,7 +86,8 @@ export class ImageMapProviderSettingsComponent extends PageComponent implements constructor(protected store: Store, private translate: TranslateService, private entityService: EntityService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -95,7 +97,9 @@ export class ImageMapProviderSettingsComponent extends PageComponent implements imageEntityAlias: [null, []], imageUrlAttribute: [null, []] }); - this.providerSettingsFormGroup.valueChanges.subscribe(() => { + this.providerSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-editor-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-editor-settings.component.ts index b37e2d1447..7fed20ba9e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-editor-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-editor-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -30,6 +30,7 @@ import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; import { MapEditorSettings } from '@home/components/widget/lib/maps/map-models'; import { WidgetService } from '@core/http/widget.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-map-editor-settings', @@ -62,7 +63,8 @@ export class MapEditorSettingsComponent extends PageComponent implements OnInit, constructor(protected store: Store, private translate: TranslateService, private widgetService: WidgetService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -75,10 +77,14 @@ export class MapEditorSettingsComponent extends PageComponent implements OnInit, hideEditControlButton: [null, []], hideRemoveControlButton: [null, []], }); - this.mapEditorSettingsFormGroup.valueChanges.subscribe(() => { + this.mapEditorSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.mapEditorSettingsFormGroup.get('hideAllControlButton').valueChanges.subscribe(() => { + this.mapEditorSettingsFormGroup.get('hideAllControlButton').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-provider-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-provider-settings.component.ts index 0f0fe7cc76..2bc7b93f76 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-provider-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-provider-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -44,6 +44,7 @@ import { } from '@home/components/widget/lib/maps/map-models'; import { extractType } from '@core/utils'; import { IAliasController } from '@core/api/widget-api.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-map-provider-settings', @@ -87,7 +88,8 @@ export class MapProviderSettingsComponent extends PageComponent implements OnIni constructor(protected store: Store, private translate: TranslateService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -103,10 +105,14 @@ export class MapProviderSettingsComponent extends PageComponent implements OnIni imageMapProviderSettings: [null, []], tencentMapProviderSettings: [null, []] }); - this.providerSettingsFormGroup.valueChanges.subscribe(() => { + this.providerSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.providerSettingsFormGroup.get('provider').valueChanges.subscribe(() => { + this.providerSettingsFormGroup.get('provider').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-settings.component.ts index 2bd3a78560..ad4ead491a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALIDATORS, @@ -50,6 +50,7 @@ import { import { extractType } from '@core/utils'; import { IAliasController } from '@core/api/widget-api.models'; import { Widget } from '@shared/models/widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-map-settings', @@ -92,7 +93,8 @@ export class MapSettingsComponent extends PageComponent implements OnInit, Contr constructor(protected store: Store, private translate: TranslateService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -110,19 +112,29 @@ export class MapSettingsComponent extends PageComponent implements OnInit, Contr this.mapSettingsFormGroup.addControl('markerClusteringSettings', this.fb.control(null, [])); } this.mapSettingsFormGroup.addControl('mapEditorSettings', this.fb.control(null, [])); - this.mapSettingsFormGroup.get('mapProviderSettings').valueChanges.subscribe(() => { + this.mapSettingsFormGroup.get('mapProviderSettings').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.mapSettingsFormGroup.get('markersSettings').valueChanges.subscribe(() => { + this.mapSettingsFormGroup.get('markersSettings').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.mapSettingsFormGroup.get('polygonSettings').valueChanges.subscribe(() => { + this.mapSettingsFormGroup.get('polygonSettings').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.mapSettingsFormGroup.get('circleSettings').valueChanges.subscribe(() => { + this.mapSettingsFormGroup.get('circleSettings').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.mapSettingsFormGroup.valueChanges.subscribe(() => { + this.mapSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/marker-clustering-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/marker-clustering-settings.component.ts index bf622b6aa2..d648f951d9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/marker-clustering-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/marker-clustering-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -31,6 +31,7 @@ import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; import { MarkerClusteringSettings } from '@home/components/widget/lib/maps/map-models'; import { WidgetService } from '@core/http/widget.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-marker-clustering-settings', @@ -65,7 +66,8 @@ export class MarkerClusteringSettingsComponent extends PageComponent implements constructor(protected store: Store, private translate: TranslateService, private widgetService: WidgetService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -83,10 +85,14 @@ export class MarkerClusteringSettingsComponent extends PageComponent implements useIconCreateFunction: [null, []], clusterMarkerFunction: [null, []] }); - this.markerClusteringSettingsFormGroup.valueChanges.subscribe(() => { + this.markerClusteringSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.markerClusteringSettingsFormGroup.get('useClusterMarkers').valueChanges.subscribe(() => { + this.markerClusteringSettingsFormGroup.get('useClusterMarkers').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/markers-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/markers-settings.component.ts index cf7be034eb..33ef1526ed 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/markers-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/markers-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -33,6 +33,7 @@ import { MarkersSettings, ShowTooltipAction, showTooltipActionTranslationMap } from '@home/components/widget/lib/maps/map-models'; import { WidgetService } from '@core/http/widget.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-markers-settings', @@ -76,7 +77,8 @@ export class MarkersSettingsComponent extends PageComponent implements OnInit, C constructor(protected store: Store, private translate: TranslateService, private widgetService: WidgetService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -107,25 +109,39 @@ export class MarkersSettingsComponent extends PageComponent implements OnInit, C markerImageFunction: [null, []], markerImages: [null, []] }); - this.markersSettingsFormGroup.valueChanges.subscribe(() => { + this.markersSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.markersSettingsFormGroup.get('showLabel').valueChanges.subscribe(() => { + this.markersSettingsFormGroup.get('showLabel').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.markersSettingsFormGroup.get('useLabelFunction').valueChanges.subscribe(() => { + this.markersSettingsFormGroup.get('useLabelFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.markersSettingsFormGroup.get('showTooltip').valueChanges.subscribe(() => { + this.markersSettingsFormGroup.get('showTooltip').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.markersSettingsFormGroup.get('useTooltipFunction').valueChanges.subscribe(() => { + this.markersSettingsFormGroup.get('useTooltipFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.markersSettingsFormGroup.get('useColorFunction').valueChanges.subscribe(() => { + this.markersSettingsFormGroup.get('useColorFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.markersSettingsFormGroup.get('useMarkerImageFunction').valueChanges.subscribe(() => { + this.markersSettingsFormGroup.get('useMarkerImageFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/openstreet-map-provider-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/openstreet-map-provider-settings.component.ts index b564f31dbe..14675fb5ef 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/openstreet-map-provider-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/openstreet-map-provider-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -34,6 +34,7 @@ import { OpenStreetMapProviderSettings, openStreetMapProviderTranslationMap } from '@home/components/widget/lib/maps/map-models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-openstreet-map-provider-settings', @@ -69,7 +70,8 @@ export class OpenStreetMapProviderSettingsComponent extends PageComponent implem constructor(protected store: Store, private translate: TranslateService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -79,10 +81,14 @@ export class OpenStreetMapProviderSettingsComponent extends PageComponent implem useCustomProvider: [null, []], customProviderTileUrl: [null, [Validators.required]] }); - this.providerSettingsFormGroup.valueChanges.subscribe(() => { + this.providerSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.providerSettingsFormGroup.get('useCustomProvider').valueChanges.subscribe(() => { + this.providerSettingsFormGroup.get('useCustomProvider').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/polygon-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/polygon-settings.component.ts index e87ce981ad..ae0fd0c1f1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/polygon-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/polygon-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -36,6 +36,7 @@ import { } from '@home/components/widget/lib/maps/map-models'; import { WidgetService } from '@core/http/widget.service'; import { Widget } from '@shared/models/widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-polygon-settings', @@ -77,7 +78,8 @@ export class PolygonSettingsComponent extends PageComponent implements OnInit, C constructor(protected store: Store, private translate: TranslateService, private widgetService: WidgetService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -106,28 +108,44 @@ export class PolygonSettingsComponent extends PageComponent implements OnInit, C usePolygonStrokeColorFunction: [null, []], polygonStrokeColorFunction: [null, []] }); - this.polygonSettingsFormGroup.valueChanges.subscribe(() => { + this.polygonSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.polygonSettingsFormGroup.get('showPolygon').valueChanges.subscribe(() => { + this.polygonSettingsFormGroup.get('showPolygon').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.polygonSettingsFormGroup.get('showPolygonLabel').valueChanges.subscribe(() => { + this.polygonSettingsFormGroup.get('showPolygonLabel').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.polygonSettingsFormGroup.get('usePolygonLabelFunction').valueChanges.subscribe(() => { + this.polygonSettingsFormGroup.get('usePolygonLabelFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.polygonSettingsFormGroup.get('showPolygonTooltip').valueChanges.subscribe(() => { + this.polygonSettingsFormGroup.get('showPolygonTooltip').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.polygonSettingsFormGroup.get('usePolygonTooltipFunction').valueChanges.subscribe(() => { + this.polygonSettingsFormGroup.get('usePolygonTooltipFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.polygonSettingsFormGroup.get('usePolygonColorFunction').valueChanges.subscribe(() => { + this.polygonSettingsFormGroup.get('usePolygonColorFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.polygonSettingsFormGroup.get('usePolygonStrokeColorFunction').valueChanges.subscribe(() => { + this.polygonSettingsFormGroup.get('usePolygonStrokeColorFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/route-map-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/route-map-settings.component.ts index a34d9b55a6..3ccbf9211c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/route-map-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/route-map-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -30,6 +30,7 @@ import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; import { PolylineSettings } from '@home/components/widget/lib/maps/map-models'; import { WidgetService } from '@core/http/widget.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-route-map-settings', @@ -62,7 +63,8 @@ export class RouteMapSettingsComponent extends PageComponent implements OnInit, constructor(protected store: Store, private translate: TranslateService, private widgetService: WidgetService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -71,7 +73,9 @@ export class RouteMapSettingsComponent extends PageComponent implements OnInit, strokeWeight: [null, [Validators.min(0)]], strokeOpacity: [null, [Validators.min(0), Validators.max(1)]] }); - this.routeMapSettingsFormGroup.valueChanges.subscribe(() => { + this.routeMapSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/tencent-map-provider-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/tencent-map-provider-settings.component.ts index c2412866b3..868af4968c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/tencent-map-provider-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/tencent-map-provider-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -34,6 +34,7 @@ import { TencentMapType, tencentMapTypeProviderTranslationMap } from '@home/components/widget/lib/maps/map-models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-tencent-map-provider-settings', @@ -69,7 +70,8 @@ export class TencentMapProviderSettingsComponent extends PageComponent implement constructor(protected store: Store, private translate: TranslateService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -78,7 +80,9 @@ export class TencentMapProviderSettingsComponent extends PageComponent implement tmApiKey: [null, [Validators.required]], tmDefaultMapType: [null, [Validators.required]] }); - this.providerSettingsFormGroup.valueChanges.subscribe(() => { + this.providerSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-common-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-common-settings.component.ts index de05df2baa..1e7c484bdb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-common-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-common-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -32,6 +32,7 @@ import { TranslateService } from '@ngx-translate/core'; import { TripAnimationCommonSettings } from '@home/components/widget/lib/maps/map-models'; import { Widget } from '@shared/models/widget.models'; import { WidgetService } from '@core/http/widget.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-trip-animation-common-settings', @@ -69,7 +70,8 @@ export class TripAnimationCommonSettingsComponent extends PageComponent implemen constructor(protected store: Store, private translate: TranslateService, private widgetService: WidgetService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -87,13 +89,19 @@ export class TripAnimationCommonSettingsComponent extends PageComponent implemen tooltipPattern: [null, []], tooltipFunction: [null, []], }); - this.tripAnimationCommonSettingsFormGroup.valueChanges.subscribe(() => { + this.tripAnimationCommonSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.tripAnimationCommonSettingsFormGroup.get('showTooltip').valueChanges.subscribe(() => { + this.tripAnimationCommonSettingsFormGroup.get('showTooltip').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.tripAnimationCommonSettingsFormGroup.get('useTooltipFunction').valueChanges.subscribe(() => { + this.tripAnimationCommonSettingsFormGroup.get('useTooltipFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-marker-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-marker-settings.component.ts index 1a839126e6..422bcd3a2c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-marker-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-marker-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -31,6 +31,7 @@ import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; import { TripAnimationMarkerSettings } from '@home/components/widget/lib/maps/map-models'; import { WidgetService } from '@core/http/widget.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-trip-animation-marker-settings', @@ -65,7 +66,8 @@ export class TripAnimationMarkerSettingsComponent extends PageComponent implemen constructor(protected store: Store, private translate: TranslateService, private widgetService: WidgetService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -82,16 +84,24 @@ export class TripAnimationMarkerSettingsComponent extends PageComponent implemen markerImageFunction: [null, []], markerImages: [null, []] }); - this.tripAnimationMarkerSettingsFormGroup.valueChanges.subscribe(() => { + this.tripAnimationMarkerSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.tripAnimationMarkerSettingsFormGroup.get('showLabel').valueChanges.subscribe(() => { + this.tripAnimationMarkerSettingsFormGroup.get('showLabel').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.tripAnimationMarkerSettingsFormGroup.get('useLabelFunction').valueChanges.subscribe(() => { + this.tripAnimationMarkerSettingsFormGroup.get('useLabelFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.tripAnimationMarkerSettingsFormGroup.get('useMarkerImageFunction').valueChanges.subscribe(() => { + this.tripAnimationMarkerSettingsFormGroup.get('useMarkerImageFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-path-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-path-settings.component.ts index 13cb5ea8f8..e8fd9b64c5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-path-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-path-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -34,6 +34,7 @@ import { PolylineSettings } from '@home/components/widget/lib/maps/map-models'; import { WidgetService } from '@core/http/widget.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-trip-animation-path-settings', @@ -72,7 +73,8 @@ export class TripAnimationPathSettingsComponent extends PageComponent implements constructor(protected store: Store, private translate: TranslateService, private widgetService: WidgetService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -92,16 +94,24 @@ export class TripAnimationPathSettingsComponent extends PageComponent implements endDecoratorOffset: [null, []], decoratorRepeat: [null, []], }); - this.tripAnimationPathSettingsFormGroup.valueChanges.subscribe(() => { + this.tripAnimationPathSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.tripAnimationPathSettingsFormGroup.get('useColorFunction').valueChanges.subscribe(() => { + this.tripAnimationPathSettingsFormGroup.get('useColorFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.tripAnimationPathSettingsFormGroup.get('usePolylineDecorator').valueChanges.subscribe(() => { + this.tripAnimationPathSettingsFormGroup.get('usePolylineDecorator').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.tripAnimationPathSettingsFormGroup.get('useDecoratorCustomColor').valueChanges.subscribe(() => { + this.tripAnimationPathSettingsFormGroup.get('useDecoratorCustomColor').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-point-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-point-settings.component.ts index c934ff0b96..91ef694292 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-point-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-point-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -35,6 +35,7 @@ import { PolylineSettings } from '@home/components/widget/lib/maps/map-models'; import { WidgetService } from '@core/http/widget.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-trip-animation-point-settings', @@ -69,7 +70,8 @@ export class TripAnimationPointSettingsComponent extends PageComponent implement constructor(protected store: Store, private translate: TranslateService, private widgetService: WidgetService, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { super(store); } @@ -84,16 +86,24 @@ export class TripAnimationPointSettingsComponent extends PageComponent implement pointAsAnchorFunction: [null, []], pointTooltipOnRightPanel: [null, []] }); - this.tripAnimationPointSettingsFormGroup.valueChanges.subscribe(() => { + this.tripAnimationPointSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); - this.tripAnimationPointSettingsFormGroup.get('showPoints').valueChanges.subscribe(() => { + this.tripAnimationPointSettingsFormGroup.get('showPoints').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.tripAnimationPointSettingsFormGroup.get('useColorPointFunction').valueChanges.subscribe(() => { + this.tripAnimationPointSettingsFormGroup.get('useColorPointFunction').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); - this.tripAnimationPointSettingsFormGroup.get('usePointAsAnchor').valueChanges.subscribe(() => { + this.tripAnimationPointSettingsFormGroup.get('usePointAsAnchor').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true); }); this.updateValidators(false); diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts index 52fce38975..1721a7f068 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts @@ -18,6 +18,7 @@ import { ChangeDetectorRef, Component, ComponentRef, + DestroyRef, forwardRef, Input, OnDestroy, @@ -82,6 +83,7 @@ import { basicWidgetConfigComponentsMap } from '@home/components/widget/config/b import { TimewindowConfigData } from '@home/components/widget/config/timewindow-config-panel.component'; import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; import { defaultFormProperties, FormProperty } from '@shared/models/dynamic-form.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import Timeout = NodeJS.Timeout; @Component({ @@ -202,7 +204,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe private dialog: MatDialog, public translate: TranslateService, private fb: UntypedFormBuilder, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { super(store); } @@ -237,7 +240,10 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe }); merge(this.widgetSettings.get('showTitle').valueChanges, - this.widgetSettings.get('showTitleIcon').valueChanges).subscribe(() => { + this.widgetSettings.get('showTitleIcon').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateWidgetSettingsEnabledState(); }); @@ -250,7 +256,9 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe desktopHide: [false] }); - this.layoutSettings.get('resizable').valueChanges.subscribe(() => { + this.layoutSettings.get('resizable').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateLayoutEnabledState(); }); diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html index f6679d0c94..2db2717cb8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html @@ -66,6 +66,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts index d1517a2e72..eaf6b6105e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts @@ -150,9 +150,11 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges, @Input() dashboardWidget: DashboardWidget; + @Input() + widget: Widget; + @ViewChild('widgetContent', {read: ViewContainerRef, static: true}) widgetContentContainer: ViewContainerRef; - widget: Widget; widgetInfo: WidgetInfo; errorMessages: string[]; widgetContext: WidgetContext; @@ -223,8 +225,6 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges, this.loadingData = true; - this.widget = this.dashboardWidget.widget; - const actionDescriptorsBySourceId: {[actionSourceId: string]: Array} = {}; if (this.widget.config.actions) { for (const actionSourceId of Object.keys(this.widget.config.actions)) { diff --git a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts index 12c00597d5..0de6161e27 100644 --- a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts @@ -111,6 +111,9 @@ interface DashboardWidgetUpdateRecord { operation: DashboardWidgetUpdateOperation; } +export const maxGridsterCol = 3000; +export const maxGridsterRow = 3000; + export class DashboardWidgets implements Iterable { highlightedMode = false; diff --git a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html index ff10de6fc2..35ba6048c0 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html @@ -243,23 +243,11 @@
- - admin.oauth2.scope - - - {{scope}} - cancel - - - - - {{ 'admin.oauth2.scope-required' | translate }} - - + + diff --git a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.ts b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.ts index 3db82626fe..9cf14e8c49 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.ts @@ -18,8 +18,7 @@ import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { PageComponent } from '@shared/components/page.component'; -import { FormBuilder, FormGroup, UntypedFormArray, Validators } from '@angular/forms'; -import { ActivatedRoute, Router } from '@angular/router'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { AdminSettings, MailConfigTemplate, @@ -37,9 +36,6 @@ import { forkJoin, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { DomainSchema, domainSchemaTranslations, } from '@shared/models/oauth2.models'; import { WINDOW } from '@core/services/window.service'; -import { AuthService } from '@core/auth/auth.service'; -import { COMMA, ENTER } from '@angular/cdk/keycodes'; -import { MatChipInputEvent } from '@angular/material/chips'; @Component({ selector: 'tb-mail-server', @@ -51,7 +47,7 @@ export class MailServerComponent extends PageComponent implements OnInit, OnDest smtpProtocols = Object.values(SmtpProtocol); showChangePassword = false; - protocols = Object.values(DomainSchema).filter(value => value !== DomainSchema.MIXED); + protocols: DomainSchema[] = Object.values(DomainSchema).filter(value => value !== DomainSchema.MIXED); domainSchemaTranslations = domainSchemaTranslations; mailServerOauth2Provider = MailServerOauth2Provider; @@ -64,8 +60,6 @@ export class MailServerComponent extends PageComponent implements OnInit, OnDest templateProvider = ['CUSTOM']; - readonly separatorKeysCodes: number[] = [ENTER, COMMA]; - private destroy$ = new Subject(); private DOMAIN_AND_PORT_REGEXP = /^(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?$/; private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.,?+=&%@\-/]*)?$/; @@ -98,7 +92,7 @@ export class MailServerComponent extends PageComponent implements OnInit, OnDest providerTenantId: [{value: '', disabled: true}, [Validators.required]], authUri: [{value: '', disabled: true}, [Validators.required, Validators.pattern(this.URL_REGEXP)]], tokenUri: [{value: '', disabled: true}, [Validators.required, Validators.pattern(this.URL_REGEXP)]], - scope: [], + scope: this.fb.control>({value: [], disabled: true}, Validators.required), redirectUri: [{ value:'', disabled: true}] }); @@ -134,10 +128,7 @@ export class MailServerComponent extends PageComponent implements OnInit, OnDest }); constructor(protected store: Store, - private router: Router, - private route: ActivatedRoute, private adminService: AdminService, - private authService: AuthService, private translate: TranslateService, public fb: FormBuilder, @Inject(WINDOW) private window: Window) { @@ -148,14 +139,14 @@ export class MailServerComponent extends PageComponent implements OnInit, OnDest this.mailServerSettingsForm(); this.domainFormConfiguration(); - forkJoin([ - this.adminService.getLoginProcessingUrl(), - this.adminService.getMailConfigTemplate(), - this.adminService.getAdminSettings('mail') - ]).subscribe(([loginProcessingUrl, mailConfigTemplate, adminSettings]) => { - this.loginProcessingUrl = loginProcessingUrl; - this.initTemplates(mailConfigTemplate); - this.adminSettings = adminSettings; + forkJoin({ + loginProcessingUrl: this.adminService.getLoginProcessingUrl(), + mailConfigTemplate: this.adminService.getMailConfigTemplate(), + adminSettings: this.adminService.getAdminSettings('mail') + }).subscribe((data) => { + this.loginProcessingUrl = data.loginProcessingUrl; + this.initTemplates(data.mailConfigTemplate); + this.adminSettings = data.adminSettings; if (this.adminSettings.jsonValue && isString(this.adminSettings.jsonValue.enableTls)) { this.adminSettings.jsonValue.enableTls = (this.adminSettings.jsonValue.enableTls as any) === 'true'; } @@ -187,7 +178,7 @@ export class MailServerComponent extends PageComponent implements OnInit, OnDest super.ngOnDestroy(); } - private initTemplates(templates): void { + private initTemplates(templates: Array): void { templates.map(provider => { delete provider.additionalInfo; this.templates.set(provider.providerId, provider); @@ -294,6 +285,7 @@ export class MailServerComponent extends PageComponent implements OnInit, OnDest this.mailSettings.get('clientId').enable({emitEvent: false}); this.mailSettings.get('clientSecret').enable({emitEvent: false}); this.mailSettings.get('redirectUri').enable({emitEvent: false}); + this.mailSettings.get('scope').enable({emitEvent: false}); if (this.mailSettings.get('providerId').value === this.mailServerOauth2Provider.CUSTOM) { this.mailSettings.get('authUri').enable({emitEvent: false}); this.mailSettings.get('tokenUri').enable({emitEvent: false}); @@ -307,6 +299,7 @@ export class MailServerComponent extends PageComponent implements OnInit, OnDest this.mailSettings.get('redirectUri').disable({emitEvent: false}); this.mailSettings.get('authUri').disable({emitEvent: false}); this.mailSettings.get('tokenUri').disable({emitEvent: false}); + this.mailSettings.get('scope').disable({emitEvent: false}); } } @@ -422,31 +415,6 @@ export class MailServerComponent extends PageComponent implements OnInit, OnDest return formValue; } - trackByParams(index: number): number { - return index; - } - - removeScope(i: number): void { - const controller = this.mailSettings.get('scope') as UntypedFormArray; - controller.removeAt(i); - controller.markAsTouched(); - controller.markAsDirty(); - } - - addScope(event: MatChipInputEvent): void { - const input = event.chipInput.inputElement; - const value = event.value; - const controller = this.mailSettings.get('scope') as UntypedFormArray; - if ((value.trim() !== '')) { - controller.push(this.fb.control(value.trim())); - controller.markAsDirty(); - } - - if (input) { - input.value = ''; - } - } - toggleEditMode(path: string): void { this.mailSettings.get(path).disabled ? this.mailSettings.get(path).enable() : this.mailSettings.get(path).disable(); } diff --git a/ui-ngx/src/app/modules/home/pages/admin/resource/js-resource.component.html b/ui-ngx/src/app/modules/home/pages/admin/resource/js-resource.component.html index d587629352..076eab7000 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/resource/js-resource.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/resource/js-resource.component.html @@ -80,6 +80,7 @@ (fileNameChanged)="entityForm?.get('fileName').patchValue($event)"> this.jwtSecuritySettingsFormGroup.get('refreshTokenExpTime').updateValueAndValidity({onlySelf: true}) ); } diff --git a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts index 4a519fc52b..65aab17297 100644 --- a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component } from '@angular/core'; +import { Component, DestroyRef } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; @@ -24,6 +24,7 @@ import { deviceTransportTypeHintMap, deviceTransportTypeTranslationMap } from '@shared/models/device.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-device-profile-tabs', @@ -40,13 +41,16 @@ export class DeviceProfileTabsComponent extends EntityTabsComponent) { + constructor(protected store: Store, + private destroyRef: DestroyRef) { super(store); } ngOnInit() { super.ngOnInit(); - this.detailsForm.get('transportType').valueChanges.subscribe(() => { + this.detailsForm.get('transportType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.isTransportTypeChanged = true; }); } diff --git a/ui-ngx/src/app/modules/home/pages/device/data/default-device-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/default-device-configuration.component.ts index 034d7c26de..46eafb44e5 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/default-device-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/data/default-device-configuration.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; @@ -24,6 +24,7 @@ import { DeviceConfiguration, DeviceProfileType } from '@shared/models/device.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-default-device-configuration', @@ -54,7 +55,8 @@ export class DefaultDeviceConfigurationComponent implements ControlValueAccessor private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -68,7 +70,9 @@ export class DefaultDeviceConfigurationComponent implements ControlValueAccessor this.defaultDeviceConfigurationFormGroup = this.fb.group({ configuration: [null, Validators.required] }); - this.defaultDeviceConfigurationFormGroup.valueChanges.subscribe(() => { + this.defaultDeviceConfigurationFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/pages/device/data/default-device-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/default-device-transport-configuration.component.ts index d04df8a40f..fdf2dfb2b2 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/default-device-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/data/default-device-transport-configuration.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; @@ -24,6 +24,7 @@ import { DeviceTransportConfiguration, DeviceTransportType } from '@shared/models/device.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-default-device-transport-configuration', @@ -54,7 +55,8 @@ export class DefaultDeviceTransportConfigurationComponent implements ControlValu private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -68,7 +70,9 @@ export class DefaultDeviceTransportConfigurationComponent implements ControlValu this.defaultDeviceTransportConfigurationFormGroup = this.fb.group({ configuration: [null, Validators.required] }); - this.defaultDeviceTransportConfigurationFormGroup.valueChanges.subscribe(() => { + this.defaultDeviceTransportConfigurationFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/pages/device/data/device-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/device-configuration.component.ts index 4748d4bc3c..732203cc2c 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/device-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/data/device-configuration.component.ts @@ -14,13 +14,14 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DeviceConfiguration, DeviceProfileType } from '@shared/models/device.models'; import { deepClone } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-device-configuration', @@ -55,7 +56,8 @@ export class DeviceConfigurationComponent implements ControlValueAccessor, OnIni private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -69,7 +71,9 @@ export class DeviceConfigurationComponent implements ControlValueAccessor, OnIni this.deviceConfigurationFormGroup = this.fb.group({ configuration: [null, Validators.required] }); - this.deviceConfigurationFormGroup.valueChanges.subscribe(() => { + this.deviceConfigurationFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/pages/device/data/device-data.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/device-data.component.ts index ed1fd3ecd0..2032371121 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/device-data.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/data/device-data.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -33,6 +33,7 @@ import { deviceProfileTypeConfigurationInfoMap, deviceTransportTypeConfigurationInfoMap } from '@shared/models/device.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-device-data', @@ -73,7 +74,8 @@ export class DeviceDataComponent implements ControlValueAccessor, OnInit, Valida private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -88,7 +90,9 @@ export class DeviceDataComponent implements ControlValueAccessor, OnInit, Valida configuration: [null, Validators.required], transportConfiguration: [null, Validators.required] }); - this.deviceDataFormGroup.valueChanges.subscribe(() => { + this.deviceDataFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.ts index ecc09c9e4a..df9a1b4845 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALIDATORS, @@ -30,6 +30,7 @@ import { AppState } from '@app/core/core.state'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DeviceTransportConfiguration, DeviceTransportType } from '@shared/models/device.models'; import { deepClone } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-device-transport-configuration', @@ -70,7 +71,8 @@ export class DeviceTransportConfigurationComponent implements ControlValueAccess private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -84,7 +86,9 @@ export class DeviceTransportConfigurationComponent implements ControlValueAccess this.deviceTransportConfigurationFormGroup = this.fb.group({ configuration: [null, Validators.required] }); - this.deviceTransportConfigurationFormGroup.valueChanges.subscribe(() => { + this.deviceTransportConfigurationFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/pages/device/data/mqtt-device-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/mqtt-device-transport-configuration.component.ts index 453f982d95..4d6e77fc75 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/mqtt-device-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/data/mqtt-device-transport-configuration.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; @@ -23,6 +23,7 @@ import { DeviceTransportConfiguration, DeviceTransportType, MqttDeviceTransportConfiguration } from '@shared/models/device.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-mqtt-device-transport-configuration', @@ -53,7 +54,8 @@ export class MqttDeviceTransportConfigurationComponent implements ControlValueAc private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -67,7 +69,9 @@ export class MqttDeviceTransportConfigurationComponent implements ControlValueAc this.mqttDeviceTransportConfigurationFormGroup = this.fb.group({ configuration: [null, Validators.required] }); - this.mqttDeviceTransportConfigurationFormGroup.valueChanges.subscribe(() => { + this.mqttDeviceTransportConfigurationFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts index 06d8565e07..c7685f1c32 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, UntypedFormBuilder, @@ -39,6 +39,7 @@ import { SnmpPrivacyProtocolTranslationMap } from '@shared/models/device.models'; import { isDefinedAndNotNull } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-snmp-device-transport-configuration', @@ -82,7 +83,8 @@ export class SnmpDeviceTransportConfigurationComponent implements ControlValueAc private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: UntypedFormBuilder) { + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } registerOnChange(fn: any): void { @@ -107,10 +109,14 @@ export class SnmpDeviceTransportConfigurationComponent implements ControlValueAc privacyPassphrase: ['', [Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*')]], engineId: [''] }); - this.snmpDeviceTransportConfigurationFormGroup.get('protocolVersion').valueChanges.subscribe((protocol: SnmpDeviceProtocolVersion) => { + this.snmpDeviceTransportConfigurationFormGroup.get('protocolVersion').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((protocol: SnmpDeviceProtocolVersion) => { this.updateDisabledFormValue(protocol); }); - this.snmpDeviceTransportConfigurationFormGroup.valueChanges.subscribe(() => { + this.snmpDeviceTransportConfigurationFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/pages/device/device.component.ts b/ui-ngx/src/app/modules/home/pages/device/device.component.ts index 24e9973f53..bea985e153 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/device.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, Inject } from '@angular/core'; +import { ChangeDetectorRef, Component, DestroyRef, Inject } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { EntityComponent } from '../../components/entity/entity.component'; @@ -36,7 +36,7 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.mod import { Subject } from 'rxjs'; import { OtaUpdateType } from '@shared/models/ota-package.models'; import { distinctUntilChanged } from 'rxjs/operators'; -import { getEntityDetailsPageURL } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-device', @@ -58,7 +58,8 @@ export class DeviceComponent extends EntityComponent { @Inject('entity') protected entityValue: DeviceInfo, @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig, public fb: UntypedFormBuilder, - protected cd: ChangeDetectorRef) { + protected cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { super(store, fb, entityValue, entitiesTableConfigValue, cd); } @@ -99,7 +100,8 @@ export class DeviceComponent extends EntityComponent { } ); form.get('deviceProfileId').valueChanges.pipe( - distinctUntilChanged((prev, curr) => prev?.id === curr?.id) + distinctUntilChanged((prev, curr) => prev?.id === curr?.id), + takeUntilDestroyed(this.destroyRef) ).subscribe(profileId => { if (profileId && this.isEdit) { this.entityForm.patchValue({ diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts index 20ac575b2c..4de8628ec2 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts @@ -70,7 +70,7 @@ export class MobileAppTableConfigResolver { this.config.columns.push( new DateEntityTableColumn('createdTime', 'common.created-time', this.datePipe, '170px'), new EntityTableColumn('pkgName', 'mobile.application-package', '20%', (entity) => entity.pkgName ?? '', () => ({}), - false, () => ({}), () => undefined, false, + true, () => ({}), () => undefined, false, { name: this.translate.instant('mobile.copy-application-package'), icon: 'content_copy', @@ -85,7 +85,7 @@ export class MobileAppTableConfigResolver { }), new EntityTableColumn('appSecret', 'mobile.application-secret', '15%', (entity) => this.truncatePipe.transform(entity.appSecret, true, 10, '…'), () => ({}), - false, () => ({}), () => undefined, false, + true, () => ({}), () => undefined, false, { name: this.translate.instant('mobile.copy-application-secret'), icon: 'content_copy', diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app.component.ts index 803748c7d5..0d7a9c081c 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app.component.ts @@ -76,9 +76,9 @@ export class MobileAppComponent extends EntityComponent { }), storeInfo: this.fb.group({ storeLink: [entity?.storeInfo?.storeLink ? entity.storeInfo.storeLink : '', - Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+$/)], + Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+(?:&[a-zA-Z0-9._-]+=[a-zA-Z0-9._%-]*)*$/)], sha256CertFingerprints: [entity?.storeInfo?.sha256CertFingerprints ? entity.storeInfo.sha256CertFingerprints : '', - Validators.pattern(/^[A-Fa-f0-9]{2}(:[A-Fa-f0-9]{2}){1,31}$/)], + Validators.pattern(/^[A-Fa-f0-9]{2}(:[A-Fa-f0-9]{2}){31}$/)], appId: [entity?.storeInfo?.appId ? entity.storeInfo.appId : '', Validators.pattern(/^[A-Z0-9]{10}\.[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*$/)], }), }); @@ -89,11 +89,11 @@ export class MobileAppComponent extends EntityComponent { if (value === PlatformType.ANDROID) { form.get('storeInfo.sha256CertFingerprints').enable({emitEvent: false}); form.get('storeInfo.appId').disable({emitEvent: false}); - form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+$/)); + form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+(?:&[a-zA-Z0-9._-]+=[a-zA-Z0-9._%-]*)*$/)); } else if (value === PlatformType.IOS) { form.get('storeInfo.sha256CertFingerprints').disable({emitEvent: false}); form.get('storeInfo.appId').enable({emitEvent: false}); - form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/apps\.apple\.com\/[a-z]{2}\/app\/[\w-]+\/id\d{7,10}$/)); + form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/apps\.apple\.com\/[a-z]{2}\/app\/[\w-]+\/id\d{7,10}(?:\?[^\s]*)?$/)); } form.get('storeInfo.storeLink').setValue('', {emitEvent: false}); }); diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.html b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.html index f543126660..1c89c7e06b 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.html +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.html @@ -30,6 +30,22 @@ mobile.page-name + + warning + + + warning +
@@ -59,7 +75,7 @@ - {{ 'mobile.url-pattern' | translate }} + {{ 'mobile.invalid-url-format' | translate }} @@ -69,7 +85,7 @@ - {{ 'mobile.path-pattern' | translate }} + {{ 'mobile.invalid-path-format' | translate }} diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts index 5ba2ee3c6f..3075ae772b 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts @@ -60,7 +60,7 @@ export class CustomMobilePageComponent implements ControlValueAccessor, Validato customMobilePageForm = this.fb.group({ visible: [true], icon: ['star'], - label: ['', Validators.required], + label: ['', [Validators.required, Validators.pattern(/\S/)]], type: [MobilePageType.DASHBOARD], dashboardId: this.fb.control(null, Validators.required), url: [{value:'', disabled: true}, [Validators.required, Validators.pattern(/^(https?:\/\/)?(localhost|([\w\-]+\.)+[\w\-]+)(:\d+)?(\/[\w\-._~:\/?#[\]@!$&'()*+,;=%]*)?$/)]], diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-layout.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-layout.component.ts index 2b561a4fce..c7dd980378 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-layout.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-layout.component.ts @@ -207,7 +207,7 @@ export class MobileLayoutComponent implements ControlValueAccessor, Validator { private updateModel() { if (isDefaultMobilePagesConfig(this.pagesForm.value.pages as MobilePage[])) { - this.propagateChange({pages: []}); + this.propagateChange(null); } else { this.propagateChange(this.pagesForm.value); } diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html index eeec353103..6ece48086b 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html @@ -42,6 +42,14 @@ class="tb-error"> warning + + warning + diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts index eb9f7a2d63..7083d98860 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts @@ -99,7 +99,7 @@ export class MobilePageItemRowComponent implements ControlValueAccessor, OnInit, mobilePageRowForm = this.fb.group({ visible: [true, []], icon: ['', []], - label: ['', []], + label: ['', [Validators.pattern(/\S/)]], type: [MobilePageType.DEFAULT] }); @@ -187,7 +187,7 @@ export class MobilePageItemRowComponent implements ControlValueAccessor, OnInit, } } else { this.isCustomMenuItem = true; - this.mobilePageRowForm.get('label').setValidators([Validators.required]); + this.mobilePageRowForm.get('label').addValidators([Validators.required]); this.mobilePageRowForm.get('label').updateValueAndValidity({emitEvent: false}); } this.updateCleanupState(); @@ -271,7 +271,7 @@ export class MobilePageItemRowComponent implements ControlValueAccessor, OnInit, private updateModel() { this.modelValue.visible = this.mobilePageRowForm.get('visible').value; const label = this.mobilePageRowForm.get('label').value; - if (label) { + if (label?.trim()) { this.modelValue.label = label; } else { delete this.modelValue.label; diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-app-configuration-dialog.component.html b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-app-configuration-dialog.component.html index 25315fca53..4497ece277 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-app-configuration-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-app-configuration-dialog.component.html @@ -72,7 +72,7 @@
mobile.configuration-step.more-information
rocket_launch{{ 'mobile.configuration-step.getting-started' | translate }} diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts index 24a503147e..177f41ba1b 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts @@ -24,13 +24,13 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { MobileAppBundleInfo } from '@shared/models/mobile-app.models'; -import { ActivatedRouteSnapshot } from '@angular/router'; +import { ActivatedRouteSnapshot, Router } from '@angular/router'; import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; import { Direction } from '@shared/models/page/sort-order'; import { MobileBundleTableHeaderComponent } from '@home/pages/mobile/bundes/mobile-bundle-table-header.component'; import { DatePipe } from '@angular/common'; import { MobileAppService } from '@core/http/mobile-app.service'; -import { map, take } from 'rxjs/operators'; +import { finalize, map, skip, take, takeUntil } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { EntityAction } from '@home/models/entity/entity-component.models'; import { MatDialog } from '@angular/material/dialog'; @@ -52,11 +52,14 @@ export class MobileBundleTableConfigResolver { private readonly config: EntityTableConfig = new EntityTableConfig(); + private openingEditDialog = false; + constructor( private datePipe: DatePipe, private mobileAppService: MobileAppService, private translate : TranslateService, private dialog: MatDialog, + private router: Router, private store: Store ) { this.config.selectionEnabled = false; @@ -108,9 +111,15 @@ export class MobileBundleTableConfigResolver { this.config.handleRowClick = ($event, bundle) => { $event?.stopPropagation(); - this.mobileAppService.getMobileAppBundleInfoById(bundle.id.id).subscribe(appBundleInfo => { - this.editBundle($event, appBundleInfo); - }) + if (!this.openingEditDialog) { + this.openingEditDialog = true; + this.mobileAppService.getMobileAppBundleInfoById(bundle.id.id).pipe( + takeUntil(this.router.events.pipe(skip(1))), + finalize(() => {this.openingEditDialog = false;}) + ).subscribe( + appBundleInfo => this.editBundle($event, appBundleInfo) + ); + } return true; }; diff --git a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.scss b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.scss index 00a2ebca2d..87dd67e8e0 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.scss +++ b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.scss @@ -32,6 +32,7 @@ } .tb-editor { height: 400px; + overflow-y: auto; } .tb-editor-buttons { height: 40px; diff --git a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts index 9b9b282e9d..92ab23f3c2 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts @@ -17,6 +17,7 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { FormBuilder, FormControl } from '@angular/forms'; import { TbPopoverComponent } from '@shared/components/popover.component'; +import { EditorOptions } from 'tinymce'; @Component({ selector: 'tb-release-notes-panel', @@ -43,7 +44,7 @@ export class EditorPanelComponent implements OnInit { editorControl: FormControl; - tinyMceOptions: Record = { + tinyMceOptions: Partial = { base_url: '/assets/tinymce', suffix: '.min', plugins: ['lists'], @@ -55,7 +56,14 @@ export class EditorPanelComponent implements OnInit { autofocus: false, branding: false, promotion: false, - resize: false + resize: false, + setup: (editor) => { + editor.on('PostRender', function() { + const container = editor.getContainer().closest('.tb-popover-content'); + const uiContainer = document.querySelector('.tox.tox-tinymce-aux'); + container.parentNode.appendChild(uiContainer); + }); + } }; constructor(private fb: FormBuilder) { diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts index 76cc84d905..31f66f5ea2 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts @@ -37,6 +37,7 @@ import { Subject } from 'rxjs'; import { deepClone, isDefinedAndNotNull } from '@core/utils'; import { coerceBoolean } from '@shared/decorators/coercion'; import { TranslateService } from '@ngx-translate/core'; +import { EditorOptions } from 'tinymce'; @Component({ selector: 'tb-template-configuration', @@ -81,7 +82,7 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co readonly NotificationDeliveryMethod = NotificationDeliveryMethod; readonly NotificationTemplateTypeTranslateMap = NotificationTemplateTypeTranslateMap; - tinyMceOptions: Record = { + tinyMceOptions: Partial = { base_url: '/assets/tinymce', suffix: '.min', plugins: ['link', 'table', 'image', 'lists', 'code', 'fullscreen'], @@ -93,7 +94,8 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co height: 400, autofocus: false, branding: false, - promotion: false + promotion: false, + relative_urls: false }; private propagateChange = null; diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html index 13cc24a7b7..e160914cd6 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html @@ -35,9 +35,10 @@
- - diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts index 35b10a27b3..71e808a980 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts @@ -95,6 +95,7 @@ import { ComponentClusteringMode } from '@shared/models/component-descriptor.mod import { MatDrawer } from '@angular/material/sidenav'; import { HttpStatusCode } from '@angular/common/http'; import { TbContextMenuEvent } from '@shared/models/jquery-event.models'; +import { EntityDebugSettings } from '@shared/models/entity.models'; import Timeout = NodeJS.Timeout; @Component({ @@ -1415,17 +1416,20 @@ export class RuleChainPageComponent extends PageComponent this.ruleChainCanvas.modelService.deleteSelected(); } - isDebugModeEnabled(): boolean { - const res = this.ruleChainModel.nodes.find((node) => node.debugMode); + isDebugSettingsEnabled(): boolean { + const res = this.ruleChainModel.nodes.find((node) => node?.debugSettings && this.isDebugSettingsActive(node.debugSettings)); return typeof res !== 'undefined'; } - resetDebugModeInAllNodes() { + resetDebugSettingsInAllNodes(): void { let changed = false; this.ruleChainModel.nodes.forEach((node) => { if (node.component.type !== RuleNodeType.INPUT) { - changed = changed || node.debugMode; - node.debugMode = false; + const nodeHasActiveDebugSettings = node?.debugSettings && this.isDebugSettingsActive(node.debugSettings); + changed = changed || nodeHasActiveDebugSettings; + if (nodeHasActiveDebugSettings) { + node.debugSettings = { allEnabled: false, failuresEnabled: false, allEnabledUntil: 0 }; + } } }); if (changed) { @@ -1433,6 +1437,10 @@ export class RuleChainPageComponent extends PageComponent } } + private isDebugSettingsActive(debugSettings: EntityDebugSettings): boolean { + return debugSettings.allEnabled || debugSettings.failuresEnabled || debugSettings.allEnabledUntil > new Date().getTime(); + } + validate() { setTimeout(() => { this.isInvalid = false; diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts index 1fdf11c780..4cc83b0074 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts @@ -33,7 +33,7 @@ import { RuleNodeLinkComponent } from './rule-node-link.component'; import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.component'; import { RuleNodeConfigComponent } from './rule-node-config.component'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; -import { DebugSettingsButtonComponent } from '@home/components/debug-settings/debug-settings-button.component'; +import { EntityDebugSettingsButtonComponent } from '@home/components/entity/debug/entity-debug-settings-button.component'; @NgModule({ declarations: [ @@ -63,7 +63,7 @@ import { DebugSettingsButtonComponent } from '@home/components/debug-settings/de HomeComponentsModule, RuleChainRoutingModule, DurationLeftPipe, - DebugSettingsButtonComponent + EntityDebugSettingsButtonComponent ] }) export class RuleChainModule { } diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behavior-panel.component.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behavior-panel.component.ts index f7ce9c7a0e..2c2441d662 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behavior-panel.component.ts +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behavior-panel.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { merge } from 'rxjs'; @@ -32,6 +32,7 @@ import { mergeDeep } from '@core/utils'; import { WidgetAction, widgetType } from '@shared/models/widget.models'; import { IAliasController } from '@core/api/widget-api.models'; import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-scada-symbol-behavior-panel', @@ -84,7 +85,8 @@ export class ScadaSymbolBehaviorPanelComponent implements OnInit { behaviorFormGroup: UntypedFormGroup; constructor(private fb: UntypedFormBuilder, - private widgetService: WidgetService) { + private widgetService: WidgetService, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -109,7 +111,10 @@ export class ScadaSymbolBehaviorPanelComponent implements OnInit { this.behaviorFormGroup.disable({emitEvent: false}); } else { merge(this.behaviorFormGroup.get('type').valueChanges, - this.behaviorFormGroup.get('valueType').valueChanges).subscribe(() => { + this.behaviorFormGroup.get('valueType').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(); }); this.updateValidators(); diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behavior-row.component.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behavior-row.component.ts index ac73b10550..e172197d81 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behavior-row.component.ts +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behavior-row.component.ts @@ -17,6 +17,7 @@ import { ChangeDetectorRef, Component, + DestroyRef, ElementRef, EventEmitter, forwardRef, @@ -58,6 +59,7 @@ import { import { IAliasController } from '@core/api/widget-api.models'; import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models'; import { isNotEmptyTbFunction } from '@shared/models/js-function.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export const behaviorValid = (behavior: ScadaSymbolBehavior): boolean => { if (!behavior.id || !behavior.name || !behavior.type) { @@ -143,7 +145,8 @@ export class ScadaSymbolBehaviorRowComponent implements ControlValueAccessor, On private popoverService: TbPopoverService, private renderer: Renderer2, private viewContainerRef: ViewContainerRef, - private behaviorsComponent: ScadaSymbolBehaviorsComponent) { + private behaviorsComponent: ScadaSymbolBehaviorsComponent, + private destroyRef: DestroyRef) { } ngOnInit() { @@ -152,10 +155,14 @@ export class ScadaSymbolBehaviorRowComponent implements ControlValueAccessor, On name: [null, [Validators.required]], type: [null, [Validators.required]] }); - this.behaviorRowFormGroup.valueChanges.subscribe( + this.behaviorRowFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => this.updateModel() ); - this.behaviorRowFormGroup.get('type').valueChanges.subscribe((newType: ScadaSymbolBehaviorType) => { + this.behaviorRowFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((newType: ScadaSymbolBehaviorType) => { this.onTypeChanged(newType); }); } diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behaviors.component.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behaviors.component.ts index 8dd34e7c77..ffd8571d56 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behaviors.component.ts +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behaviors.component.ts @@ -16,6 +16,7 @@ import { Component, + DestroyRef, forwardRef, HostBinding, Input, @@ -51,6 +52,7 @@ import { TranslateService } from '@ngx-translate/core'; import { mergeDeep } from '@core/utils'; import { IAliasController } from '@core/api/widget-api.models'; import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-scada-symbol-metadata-behaviors', @@ -98,14 +100,17 @@ export class ScadaSymbolBehaviorsComponent implements ControlValueAccessor, OnIn private propagateChange = (_val: any) => {}; constructor(private fb: UntypedFormBuilder, - private translate: TranslateService) { + private translate: TranslateService, + private destroyRef: DestroyRef) { } ngOnInit() { this.behaviorsFormGroup = this.fb.group({ behaviors: this.fb.array([]) }); - this.behaviorsFormGroup.valueChanges.subscribe( + this.behaviorsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => { let behaviors: ScadaSymbolBehavior[] = this.behaviorsFormGroup.get('behaviors').value; if (behaviors) { diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.ts index 42dfe8b7e2..99dc24b692 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.ts +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.ts @@ -16,6 +16,7 @@ import { Component, + DestroyRef, forwardRef, HostBinding, Input, @@ -42,6 +43,7 @@ import { ScadaSymbolMetadataTagComponent } from '@home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component'; import { TbEditorCompleter } from '@shared/models/ace/completion.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const tagIsEmpty = (tag: ScadaSymbolTag): boolean => !tag.stateRenderFunction && !tag.actions?.click?.actionFunction; @@ -90,7 +92,8 @@ export class ScadaSymbolMetadataTagsComponent implements ControlValueAccessor, O private propagateChange = (_val: any) => {}; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { } ngOnInit() { @@ -100,7 +103,9 @@ export class ScadaSymbolMetadataTagsComponent implements ControlValueAccessor, O const tagsResult = this.setupTags(); this.tagsFormGroup.setControl('tags', this.prepareTagsFormArray(tagsResult.tags), {emitEvent: false}); - this.tagsFormGroup.valueChanges.subscribe( + this.tagsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => { let value: ScadaSymbolTag[] = this.tagsFormGroup.get('tags').value; if (value) { diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.ts index 5846f98d0c..45f412667d 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.ts +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.ts @@ -16,6 +16,7 @@ import { Component, + DestroyRef, forwardRef, Input, OnChanges, @@ -54,6 +55,7 @@ import { import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; import { IAliasController } from '@core/api/widget-api.models'; import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-scada-symbol-metadata', @@ -126,7 +128,8 @@ export class ScadaSymbolMetadataComponent extends PageComponent implements OnIni constructor(protected store: Store, private fb: UntypedFormBuilder, private translate: TranslateService, - private customTranslate: CustomTranslatePipe) { + private customTranslate: CustomTranslatePipe, + private destroyRef: DestroyRef) { super(store); } @@ -144,7 +147,9 @@ export class ScadaSymbolMetadataComponent extends PageComponent implements OnIni }); this.updateFunctionCompleters(emptyMetadata()); - this.metadataFormGroup.valueChanges.subscribe(() => { + this.metadataFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); }); } diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.component.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.component.ts index e13cc953df..a87be5ee43 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.component.ts +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.component.ts @@ -18,9 +18,11 @@ import { AfterViewInit, ChangeDetectorRef, Component, + DestroyRef, ElementRef, EventEmitter, - Input, NgZone, + Input, + NgZone, OnChanges, OnDestroy, OnInit, @@ -39,6 +41,7 @@ import { parseScadaSymbolsTagsFromContent, removeScadaSymbolMetadata } from '@home/components/widget/lib/scada/scada-symbol.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface ScadaSymbolEditorData { scadaSymbolContent: string; @@ -106,7 +109,8 @@ export class ScadaSymbolEditorComponent implements OnInit, OnDestroy, AfterViewI } constructor(private cd: ChangeDetectorRef, - private zone: NgZone) { + private zone: NgZone, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -115,7 +119,9 @@ export class ScadaSymbolEditorComponent implements OnInit, OnDestroy, AfterViewI } else { this.svgContentFormControl.enable({emitEvent: false}); } - this.svgContentFormControl.valueChanges.subscribe((svgContent) => { + this.svgContentFormControl.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((svgContent) => { if (this.svgContent !== svgContent) { this.svgContent = svgContent; this.editObjectCallbacks.onSymbolEditObjectDirty(true); diff --git a/ui-ngx/src/app/modules/home/pages/user/user.component.html b/ui-ngx/src/app/modules/home/pages/user/user.component.html index 933e244290..3c52d89fc6 100644 --- a/ui-ngx/src/app/modules/home/pages/user/user.component.html +++ b/ui-ngx/src/app/modules/home/pages/user/user.component.html @@ -25,25 +25,25 @@ @@ -95,7 +95,7 @@ timewindowForm.get('realtime.realtimeType').value === realtimeTypes.INTERVAL"> @@ -137,7 +137,7 @@ timewindowForm.get('history.historyType').value === historyTypes.LAST_INTERVAL"> @@ -177,7 +177,7 @@ timewindowForm.get('history.historyType').value === historyTypes.INTERVAL"> @@ -199,7 +199,7 @@ formControlName="type" [allowedAggregationTypes]="timewindowForm.get('allowedAggTypes').value"> diff --git a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts index a5a3533b79..a3ab4614e6 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts @@ -245,9 +245,9 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On this.timewindowForm.get('realtime.advancedParams.lastAggIntervalsConfig').value; if (lastAggIntervalsConfig?.hasOwnProperty(timewindowMs) && lastAggIntervalsConfig[timewindowMs].defaultAggInterval) { - this.timewindowForm.get('realtime.interval').patchValue( + setTimeout(() => this.timewindowForm.get('realtime.interval').patchValue( lastAggIntervalsConfig[timewindowMs].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('realtime.quickInterval').valueChanges.pipe( @@ -257,9 +257,9 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On this.timewindowForm.get('realtime.advancedParams.quickAggIntervalsConfig').value; if (quickAggIntervalsConfig?.hasOwnProperty(quickInterval) && quickAggIntervalsConfig[quickInterval].defaultAggInterval) { - this.timewindowForm.get('realtime.interval').patchValue( + setTimeout(() => this.timewindowForm.get('realtime.interval').patchValue( quickAggIntervalsConfig[quickInterval].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('history.timewindowMs').valueChanges.pipe( @@ -269,9 +269,9 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On this.timewindowForm.get('history.advancedParams.lastAggIntervalsConfig').value; if (lastAggIntervalsConfig?.hasOwnProperty(timewindowMs) && lastAggIntervalsConfig[timewindowMs].defaultAggInterval) { - this.timewindowForm.get('history.interval').patchValue( + setTimeout(() => this.timewindowForm.get('history.interval').patchValue( lastAggIntervalsConfig[timewindowMs].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('history.quickInterval').valueChanges.pipe( @@ -281,9 +281,9 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On this.timewindowForm.get('history.advancedParams.quickAggIntervalsConfig').value; if (quickAggIntervalsConfig?.hasOwnProperty(quickInterval) && quickAggIntervalsConfig[quickInterval].defaultAggInterval) { - this.timewindowForm.get('history.interval').patchValue( + setTimeout(() => this.timewindowForm.get('history.interval').patchValue( quickAggIntervalsConfig[quickInterval].defaultAggInterval, {emitEvent: false} - ); + )); } }); diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts index 14247997ac..d3be3e8449 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts @@ -308,9 +308,9 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O ).subscribe((timewindowMs: number) => { if (this.realtimeAdvancedParams?.lastAggIntervalsConfig?.hasOwnProperty(timewindowMs) && this.realtimeAdvancedParams.lastAggIntervalsConfig[timewindowMs].defaultAggInterval) { - this.timewindowForm.get('realtime.interval').patchValue( + setTimeout(() => this.timewindowForm.get('realtime.interval').patchValue( this.realtimeAdvancedParams.lastAggIntervalsConfig[timewindowMs].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('realtime.quickInterval').valueChanges.pipe( @@ -318,9 +318,9 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O ).subscribe((quickInterval: number) => { if (this.realtimeAdvancedParams?.quickAggIntervalsConfig?.hasOwnProperty(quickInterval) && this.realtimeAdvancedParams.quickAggIntervalsConfig[quickInterval].defaultAggInterval) { - this.timewindowForm.get('realtime.interval').patchValue( + setTimeout(() => this.timewindowForm.get('realtime.interval').patchValue( this.realtimeAdvancedParams.quickAggIntervalsConfig[quickInterval].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('history.timewindowMs').valueChanges.pipe( @@ -328,9 +328,9 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O ).subscribe((timewindowMs: number) => { if (this.historyAdvancedParams?.lastAggIntervalsConfig?.hasOwnProperty(timewindowMs) && this.historyAdvancedParams.lastAggIntervalsConfig[timewindowMs].defaultAggInterval) { - this.timewindowForm.get('history.interval').patchValue( + setTimeout(() => this.timewindowForm.get('history.interval').patchValue( this.historyAdvancedParams.lastAggIntervalsConfig[timewindowMs].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('history.quickInterval').valueChanges.pipe( @@ -338,9 +338,9 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O ).subscribe((quickInterval: number) => { if (this.historyAdvancedParams?.quickAggIntervalsConfig?.hasOwnProperty(quickInterval) && this.historyAdvancedParams.quickAggIntervalsConfig[quickInterval].defaultAggInterval) { - this.timewindowForm.get('history.interval').patchValue( + setTimeout(() => this.timewindowForm.get('history.interval').patchValue( this.historyAdvancedParams.quickAggIntervalsConfig[quickInterval].defaultAggInterval, {emitEvent: false} - ); + )); } }); diff --git a/ui-ngx/src/app/shared/directives/truncate-with-tooltip.directive.ts b/ui-ngx/src/app/shared/directives/truncate-with-tooltip.directive.ts index d37841be6f..51f0b78df1 100644 --- a/ui-ngx/src/app/shared/directives/truncate-with-tooltip.directive.ts +++ b/ui-ngx/src/app/shared/directives/truncate-with-tooltip.directive.ts @@ -14,78 +14,55 @@ /// limitations under the License. /// -import { - AfterViewInit, - Directive, - ElementRef, - Input, - OnDestroy, - OnInit, - Renderer2, -} from '@angular/core'; -import { fromEvent, Subject } from 'rxjs'; -import { filter, takeUntil, tap } from 'rxjs/operators'; +import { booleanAttribute, Directive, ElementRef, input, OnInit, Renderer2 } from '@angular/core'; import { MatTooltip, TooltipPosition } from '@angular/material/tooltip'; -import { coerceBoolean } from '@shared/decorators/coercion'; +import { ContentObserver } from '@angular/cdk/observers'; +import { merge } from 'rxjs'; +import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop'; @Directive({ selector: '[tbTruncateWithTooltip]', - providers: [MatTooltip], + hostDirectives: [{ + directive: MatTooltip, + inputs: ['matTooltipClass', 'matTooltipTouchGestures'], + }] }) -export class TruncateWithTooltipDirective implements OnInit, AfterViewInit, OnDestroy { +export class TruncateWithTooltipDirective implements OnInit { - @Input('tbTruncateWithTooltip') - text: string; + text = input(undefined, {alias: 'tbTruncateWithTooltip'}); - @Input() - @coerceBoolean() - tooltipEnabled = true; + tooltipEnabled = input(true, {transform: booleanAttribute}); - @Input() - position: TooltipPosition = 'above'; - - private destroy$ = new Subject(); + position = input('above'); constructor( - private elementRef: ElementRef, + private elementRef: ElementRef, private renderer: Renderer2, - private tooltip: MatTooltip - ) {} + private tooltip: MatTooltip, + private contentObserver: ContentObserver + ) { + merge(toObservable(this.text), this.contentObserver.observe(this.elementRef)).pipe( + takeUntilDestroyed() + ).subscribe(() => { + this.tooltip.message = this.text() || this.elementRef.nativeElement.innerText + }) + } ngOnInit(): void { - this.observeMouseEvents(); this.applyTruncationStyles(); + this.tooltip.position = this.position(); + this.showTooltipOnOverflow(this); } - ngAfterViewInit(): void { - this.tooltip.position = this.position; - } - - ngOnDestroy(): void { - if (this.tooltip._isTooltipVisible()) { - this.hideTooltip(); - } - this.destroy$.next(); - this.destroy$.complete(); - } - - private observeMouseEvents(): void { - fromEvent(this.elementRef.nativeElement, 'mouseenter') - .pipe( - filter(() => this.tooltipEnabled), - filter(() => this.isOverflown(this.elementRef.nativeElement)), - tap(() => this.showTooltip()), - takeUntil(this.destroy$), - ) - .subscribe(); - fromEvent(this.elementRef.nativeElement, 'mouseleave') - .pipe( - filter(() => this.tooltipEnabled), - filter(() => this.tooltip._isTooltipVisible()), - tap(() => this.hideTooltip()), - takeUntil(this.destroy$), - ) - .subscribe(); + private showTooltipOnOverflow(ctx: TruncateWithTooltipDirective) { + ctx.tooltip.show = (function(old) { + function extendsFunction() { + if (ctx.tooltipEnabled() && ctx.isOverflown()) { + old.apply(ctx.tooltip, arguments); + } + } + return extendsFunction; + })(ctx.tooltip.show); } private applyTruncationStyles(): void { @@ -94,16 +71,7 @@ export class TruncateWithTooltipDirective implements OnInit, AfterViewInit, OnDe this.renderer.setStyle(this.elementRef.nativeElement, 'text-overflow', 'ellipsis'); } - private isOverflown(element: HTMLElement): boolean { - return element.clientWidth < element.scrollWidth; - } - - private showTooltip(): void { - this.tooltip.message = this.text || this.elementRef.nativeElement.innerText; - this.tooltip.show(); - } - - private hideTooltip(): void { - this.tooltip.hide(); + private isOverflown(): boolean { + return this.elementRef.nativeElement.clientWidth < this.elementRef.nativeElement.scrollWidth; } } diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 6aafef4b83..9bdc7c3c23 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -193,8 +193,8 @@ export const HelpLinks = { scadaSymbolDev: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada/scada-symbols-dev-guide/`, scadaSymbolDevAnimation: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada/scada-symbols-dev-guide/#scadasymbolanimation`, mobileApplication: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/mobile-qr-code/`, - mobileBundle: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/mobile-qr-code/`, - mobileQrCode: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/mobile-qr-code/`, + mobileBundle: `${helpBaseUrl}/docs${docPlatformPrefix}/mobile-center/mobile-center/`, + mobileQrCode: `${helpBaseUrl}/docs${docPlatformPrefix}/mobile-center/applications/`, } }; /* eslint-enable max-len */ diff --git a/ui-ngx/src/app/shared/models/entity.models.ts b/ui-ngx/src/app/shared/models/entity.models.ts index 19b0718336..9934da65aa 100644 --- a/ui-ngx/src/app/shared/models/entity.models.ts +++ b/ui-ngx/src/app/shared/models/entity.models.ts @@ -193,11 +193,11 @@ export interface HasVersion { version?: number; } -export interface HasDebugSettings { - debugSettings?: DebugSettings; +export interface HasEntityDebugSettings { + debugSettings?: EntityDebugSettings; } -export interface DebugSettings { +export interface EntityDebugSettings { failuresEnabled?: boolean; allEnabled?: boolean; allEnabledUntil?: number; diff --git a/ui-ngx/src/app/shared/models/rule-node.models.ts b/ui-ngx/src/app/shared/models/rule-node.models.ts index ddfad98f11..a79fdbc376 100644 --- a/ui-ngx/src/app/shared/models/rule-node.models.ts +++ b/ui-ngx/src/app/shared/models/rule-node.models.ts @@ -21,19 +21,20 @@ import { ComponentDescriptor } from '@shared/models/component-descriptor.models' import { FcEdge, FcNode } from 'ngx-flowchart'; import { Observable } from 'rxjs'; import { PageComponent } from '@shared/components/page.component'; -import { AfterViewInit, EventEmitter, Inject, OnInit, Directive } from '@angular/core'; +import { AfterViewInit, EventEmitter, Inject, OnInit, Directive, DestroyRef, inject } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { AbstractControl, UntypedFormGroup } from '@angular/forms'; import { RuleChainType } from '@shared/models/rule-chain.models'; import { DebugRuleNodeEventBody } from '@shared/models/event.models'; -import { HasDebugSettings } from '@shared/models/entity.models'; +import { HasEntityDebugSettings } from '@shared/models/entity.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface RuleNodeConfiguration { [key: string]: any; } -export interface RuleNode extends BaseData, HasDebugSettings { +export interface RuleNode extends BaseData, HasEntityDebugSettings { ruleChainId?: RuleChainId; type: string; name: string; @@ -102,6 +103,7 @@ export abstract class RuleNodeConfigurationComponent extends PageComponent imple private configurationSet = false; private disabledValue = false; + private destroyRef = inject(DestroyRef); set disabled(value: boolean) { if (this.disabledValue !== value) { @@ -159,11 +161,15 @@ export abstract class RuleNodeConfigurationComponent extends PageComponent imple for (const part of path) { control = control.get(part); } - control.valueChanges.subscribe(() => { + control.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true, trigger); }); } - this.configForm().valueChanges.subscribe((updated: RuleNodeConfiguration) => { + this.configForm().valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((updated: RuleNodeConfiguration) => { this.onConfigurationChanged(updated); }); } @@ -331,7 +337,7 @@ export interface RuleNodeComponentDescriptor extends ComponentDescriptor { configurationDescriptor?: RuleNodeConfigurationDescriptor; } -export interface FcRuleNodeType extends FcNode, HasDebugSettings { +export interface FcRuleNodeType extends FcNode, HasEntityDebugSettings { component?: RuleNodeComponentDescriptor; singletonMode?: boolean; queueName?: string; diff --git a/ui-ngx/src/app/shared/models/tenant.model.ts b/ui-ngx/src/app/shared/models/tenant.model.ts index 0520dd2b4e..aefc85a7f6 100644 --- a/ui-ngx/src/app/shared/models/tenant.model.ts +++ b/ui-ngx/src/app/shared/models/tenant.model.ts @@ -31,6 +31,7 @@ export interface DefaultTenantProfileConfiguration { maxUsers: number; maxDashboards: number; maxRuleChains: number; + maxEdges: number; maxResourcesInBytes: number; maxOtaPackagesInBytes: number; maxResourceSize: number; @@ -42,6 +43,13 @@ export interface DefaultTenantProfileConfiguration { transportDeviceTelemetryMsgRateLimit?: string; transportDeviceTelemetryDataPointsRateLimit?: string; + transportGatewayMsgRateLimit?: string; + transportGatewayTelemetryMsgRateLimit?: string; + transportGatewayTelemetryDataPointsRateLimit?: string; + transportGatewayDeviceMsgRateLimit?: string; + transportGatewayDeviceTelemetryMsgRateLimit?: string; + transportGatewayDeviceTelemetryDataPointsRateLimit?: string; + tenantEntityExportRateLimit?: string; tenantEntityImportRateLimit?: string; tenantNotificationRequestsRateLimit?: string; @@ -59,6 +67,8 @@ export interface DefaultTenantProfileConfiguration { smsEnabled: boolean; maxCreatedAlarms: number; + maxDebugModeDurationMinutes: number; + tenantServerRestLimitsConfiguration: string; customerServerRestLimitsConfiguration: string; @@ -75,6 +85,11 @@ export interface DefaultTenantProfileConfiguration { cassandraQueryTenantRateLimitsConfiguration: string; + edgeEventRateLimits?: string; + edgeEventRateLimitsPerEdge?: string; + edgeUplinkMessagesRateLimits?: string; + edgeUplinkMessagesRateLimitsPerEdge?: string; + defaultStorageTtlDays: number; alarmsTtlDays: number; rpcTtlDays: number; @@ -100,6 +115,7 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan maxUsers: 0, maxDashboards: 0, maxRuleChains: 0, + maxEdges: 0, maxResourcesInBytes: 0, maxOtaPackagesInBytes: 0, maxResourceSize: 0, @@ -114,6 +130,7 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan maxSms: 0, smsEnabled: true, maxCreatedAlarms: 0, + maxDebugModeDurationMinutes: 0, tenantServerRestLimitsConfiguration: '', customerServerRestLimitsConfiguration: '', maxWsSessionsPerTenant: 0, diff --git a/ui-ngx/src/app/shared/models/user.model.ts b/ui-ngx/src/app/shared/models/user.model.ts index a17cf040a8..773141cddb 100644 --- a/ui-ngx/src/app/shared/models/user.model.ts +++ b/ui-ngx/src/app/shared/models/user.model.ts @@ -29,7 +29,19 @@ export interface User extends BaseData, HasTenantId { authority: Authority; firstName: string; lastName: string; - additionalInfo: any; + additionalInfo: Partial; +} + +export interface UserAdditionalInfo { + userCredentialsEnabled: boolean; + userActivated: boolean; + description: string; + defaultDashboardId: string; + defaultDashboardFullscreen: boolean; + homeDashboardId: string; + homeDashboardHideToolbar: boolean; + lang: string; + [key: string]: any; } export enum ActivationMethod { diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 5015f6cb80..5950dcf911 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -30,7 +30,7 @@ import { } from '@shared/models/query/query.models'; import { PopoverPlacement } from '@shared/components/popover.models'; import { PageComponent } from '@shared/components/page.component'; -import { AfterViewInit, Directive, EventEmitter, Inject, OnInit, Type } from '@angular/core'; +import { AfterViewInit, DestroyRef, Directive, EventEmitter, inject, Inject, OnInit, Type } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { AbstractControl, UntypedFormGroup } from '@angular/forms'; @@ -46,6 +46,7 @@ import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/wid import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; import { TbFunction } from '@shared/models/js-function.models'; import { FormProperty, jsonFormSchemaToFormProperties } from '@shared/models/dynamic-form.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export enum widgetType { timeseries = 'timeseries', @@ -922,6 +923,8 @@ export abstract class WidgetSettingsComponent extends PageComponent implements settingsChangedEmitter = new EventEmitter(); settingsChanged = this.settingsChangedEmitter.asObservable(); + protected destroyRef = inject(DestroyRef); + protected constructor(@Inject(Store) protected store: Store) { super(store); } @@ -949,11 +952,15 @@ export abstract class WidgetSettingsComponent extends PageComponent implements for (const part of path) { control = control.get(part); } - control.valueChanges.subscribe(() => { + control.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateValidators(true, trigger); }); } - this.settingsForm().valueChanges.subscribe(() => { + this.settingsForm().valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.onSettingsChanged(this.prepareOutputSettings(this.settingsForm().getRawValue())); }); } diff --git a/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts b/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts index 853890ee09..8e8a0344ae 100644 --- a/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts +++ b/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts @@ -16,7 +16,7 @@ import { Pipe, PipeTransform } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { DAY, HOUR, MINUTE, SECOND } from '@shared/models/time/time.models'; +import { DAY, HOUR, MINUTE, SECOND, YEAR } from '@shared/models/time/time.models'; @Pipe({ name: 'milliSecondsToTimeString' @@ -27,19 +27,21 @@ export class MillisecondsToTimeStringPipe implements PipeTransform { } transform(milliSeconds: number, shortFormat = false, onlyFirstDigit = false): string { - const { days, hours, minutes, seconds } = this.extractTimeUnits(milliSeconds); - return this.formatTimeString(days, hours, minutes, seconds, shortFormat, onlyFirstDigit); + const { years, days, hours, minutes, seconds } = this.extractTimeUnits(milliSeconds); + return this.formatTimeString(years, days, hours, minutes, seconds, shortFormat, onlyFirstDigit); } - private extractTimeUnits(milliseconds: number): { days: number; hours: number; minutes: number; seconds: number } { - const days = Math.floor(milliseconds / DAY); + private extractTimeUnits(milliseconds: number): { years: number; days: number; hours: number; minutes: number; seconds: number } { + const years = Math.floor(milliseconds / YEAR); + const days = Math.floor((milliseconds % YEAR) / DAY); const hours = Math.floor((milliseconds % DAY) / HOUR); const minutes = Math.floor((milliseconds % HOUR) / MINUTE); const seconds = Math.floor((milliseconds % MINUTE) / SECOND); - return { days, hours, minutes, seconds }; + return { years, days, hours, minutes, seconds }; } private formatTimeString( + years: number, days: number, hours: number, minutes: number, @@ -48,6 +50,7 @@ export class MillisecondsToTimeStringPipe implements PipeTransform { onlyFirstDigit: boolean ): string { const timeUnits = [ + { value: years, key: 'years', shortKey: 'short.years' }, { value: days, key: 'days', shortKey: 'short.days' }, { value: hours, key: 'hours', shortKey: 'short.hours' }, { value: minutes, key: 'minutes', shortKey: 'short.minutes' }, @@ -59,7 +62,7 @@ export class MillisecondsToTimeStringPipe implements PipeTransform { if (value > 0) { timeString += this.translate.instant(shortFormat ? `timewindow.${shortKey}` : `timewindow.${key}`, { [key]: value }); if (onlyFirstDigit) { - return timeString; + return timeString.trim(); } } } diff --git a/ui-ngx/src/assets/help/en_US/resource/js-resource-module_fn.md b/ui-ngx/src/assets/help/en_US/resource/js-resource-module_fn.md new file mode 100644 index 0000000000..674ac35e2d --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/resource/js-resource-module_fn.md @@ -0,0 +1,52 @@ +### JavaScript Resource Module + +
+
+ +A JavaScript module is a self-contained piece of code that encapsulates a specific functionality or set of related functionalities. + +#### Use Cases +JavaScript resource modules are advantageous for reusing custom logic. They can be utilized in: +- Widget controller script. +- Data post-processing functions. +- Markdown/HTML value functions. +- Cell style functions. +- Cell content functions. +- Custom actions. + +These modules can contain any JavaScript code, facilitating the reuse of specific logic. This includes variables or functions that are exported for use in other parts of the application. +##### Examples + +You can declare variables: +```javascript +export const circle = '⬤'; +{:copy-code} +``` +```javascript +export const integerRegex = /^[-+]?\d+$/; +{:copy-code} +``` +Or define functions such as: +```javascript +export const getStatusStyles = (value) => { + let color; + if (value) { + color = 'rgb(39, 134, 34)'; + } else { + color = 'rgb(255, 0, 0)'; + } + return { + color: color, + fontSize: '18px' + }; +}; +{:copy-code} +``` +```javascript +export const formatDateToString = (date) => { + const options = { hour: '2-digit', minute: '2-digit', second: '2-digit' }; + return date.toLocaleTimeString('en-US', options); +}; +{:copy-code} +``` +**Hint**: *Remember to use the `export` keyword to make variables and functions accessible for use outside the module.* diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-bytes-expression_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-bytes-expression_fn.md new file mode 100644 index 0000000000..aa7225898a --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-bytes-expression_fn.md @@ -0,0 +1,18 @@ +### Expressions +#### Bytes converter: + +For bytes converter, expression fields can use slices format only. A slice specifies how to slice a sequence, determining the start point, and the endpoint. Here's a basic overview of slice components: + +- `start`: The starting index of the slice. It is included in the slice. If omitted, slicing starts at the beginning of the sequence. Indexing starts at 0, so the first element of the sequence is at index 0. + +- `stop`: The ending index of the slice. It is excluded from the slice, meaning the slice will end just before this index. If omitted, slicing goes through the end of the sequence. + +##### Bytes parsing examples: + + +| Message body | Slice | Output data | Description | +|:-----------------------|-----------------|--------------------------|------------------------------| +| AM123,mytype,12.2,45 | [:5] | AM123 | Extracting device name | +| AM123,mytype,12.2,45 | [:] | AM123,mytype,12.2,45 | Extracting all data | +| AM123,mytype,12.2,45 | [18:] | 45 | Extracting humidity value | +| AM123,mytype,12.2,45 | [13:17] | 12.2 | Extracting temperature value | diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/expressions_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-expression_fn.md similarity index 100% rename from ui-ngx/src/assets/help/en_US/widget/lib/gateway/expressions_fn.md rename to ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-expression_fn.md diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-expression_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-expression_fn.md new file mode 100644 index 0000000000..28387b595b --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-expression_fn.md @@ -0,0 +1,78 @@ +### Expressions +#### JSON Path: + +The expression field is used to extract data from the MQTT message. There are various available options for different parts of the messages: + +- The JSONPath format can be used to extract data from the message body. + +- The regular expression format can be used to extract data from the topic where the message will arrive. + +- Slices can only be used in the expression fields of bytes converters. + +JSONPath expressions specify the items within a JSON structure (which could be an object, array, or nested combination of both) that you want to access. These expressions can select elements from JSON data on specific criteria. Here's a basic overview of how JSONPath expressions are structured: + +- `$`: The root element of the JSON document; + +- `.`: Child operator used to select child elements. For example, $.store.book ; + +- `[]`: Child operator used to select child elements. $['store']['book'] accesses the book array within a store object; + +##### Examples: + +For example, if we want to extract the device name from the following message, we can use the expression below: + +MQTT message: + +``` +{ + "sensorModelInfo": { + "sensorName": "AM-123", + "sensorType": "myDeviceType" + }, + "data": { + "temp": 12.2, + "hum": 56, + "status": "ok" + } +} +{:copy-code} +``` + +Expression: + +`${sensorModelInfo.sensorName}` + +Converted data: + +`AM-123` + +If we want to extract all data from the message above, we can use the following expression: + +`${data}` + +Converted data: + +`{"temp": 12.2, "hum": 56, "status": "ok"}` + +Or if we want to extract specific data (for example “temperature”), you can use the following expression: + +`${data.temp}` + +And as a converted data we will get: + +`12.2` + +
+ +#### Regular expressions for topic: + +Device name or device profile can be parsed from the MQTT topic using regular expression. A regular expression, often abbreviated as regex or regexp, is a sequence of characters that forms a search pattern, primarily used for string matching and manipulation. + +##### Regular expression for topic examples: + +| Topic | Regular expression | Output data | Description | +|:---------------------------|----------------------------------|--------------------------|--------------------------------------| +| /devices/AM123/mytype/data | /devices/([^/]+)/mytype/data | AM123 | Getting device name from topic | +| /devices/AM123/mytype/data | /devices/[A-Z0-9]+/([^/]+)/data | mytype | Getting device profile from topic | + +
diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/attributes_timeseries_expressions_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-key-expression_fn.md similarity index 85% rename from ui-ngx/src/assets/help/en_US/widget/lib/gateway/attributes_timeseries_expressions_fn.md rename to ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-key-expression_fn.md index 91cf23ce26..98337dcd77 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/attributes_timeseries_expressions_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-key-expression_fn.md @@ -3,11 +3,11 @@ The expression field is used to extract data from the MQTT message. There are various available options for different parts of the messages: - - The JSONPath format can be used to extract data from the message body. +- The JSONPath format can be used to extract data from the message body. - - The regular expression format can be used to extract data from the topic where the message will arrive. +- The regular expression format can be used to extract data from the topic where the message will arrive. - - Slices can only be used in the expression fields of bytes converters. +- Slices can only be used in the expression fields of bytes converters. JSONPath expressions specify the items within a JSON structure (which could be an object, array, or nested combination of both) that you want to access. These expressions can select elements from JSON data on specific criteria. Here's a basic overview of how JSONPath expressions are structured: diff --git a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json index e4c7ddc917..695e1ad441 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json +++ b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json @@ -4602,7 +4602,6 @@ "output": "الإخراج", "test": "اختبار", "help": "مساعدة", - "reset-debug-mode": "إعادة تعيين وضع التصحيح في جميع العقد", "test-with-this-message": "{{test}} مع هذه الرسالة" }, "role": { diff --git a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json index c04c0e12cf..43a644a537 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json @@ -3651,8 +3651,7 @@ "metadata-required": "Les entrades de metadades no poden estar buides.", "output": "Sortida", "test": "Test", - "help": "Ajuda", - "reset-debug-mode": "Restablir el mode de depuració a tots els nodes" + "help": "Ajuda" }, "role": { "role": "Rol", diff --git a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json index 67b8b22005..4ae54c4d65 100644 --- a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json +++ b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json @@ -2426,8 +2426,7 @@ "metadata-required": "Záznam metadat nemůže být prázdný.", "output": "Výstup", "test": "Test", - "help": "Nápověda", - "reset-debug-mode": "Resetovat režim ladění na všech uzlech" + "help": "Nápověda" }, "timezone": { "timezone": "Časová zóna", diff --git a/ui-ngx/src/assets/locale/locale.constant-da_DK.json b/ui-ngx/src/assets/locale/locale.constant-da_DK.json index 9d08a72085..fbfeddd886 100644 --- a/ui-ngx/src/assets/locale/locale.constant-da_DK.json +++ b/ui-ngx/src/assets/locale/locale.constant-da_DK.json @@ -2642,8 +2642,7 @@ "metadata-required": "Metadataposter må ikke være tomme.", "output": "Output", "test": "Test", - "help": "Hjælp", - "reset-debug-mode": "Nulstil debug-tilstand i alle knuder" + "help": "Hjælp" }, "role": { "role": "Rolle", diff --git a/ui-ngx/src/assets/locale/locale.constant-el_GR.json b/ui-ngx/src/assets/locale/locale.constant-el_GR.json index eea0ea4377..164b5486b9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-el_GR.json +++ b/ui-ngx/src/assets/locale/locale.constant-el_GR.json @@ -1886,8 +1886,7 @@ "metadata-required": "Οι καταχωρίσεις μεταδεδομένων δεν μπορούν να είναι κενές.", "output": "Απόδοση", "test": "Τεστ", - "help": "Βοήθεια", - "reset-debug-mode": "Επαναφορά λειτουργίας εντοπισμού σφαλμάτων σε όλους τους κόμβους" + "help": "Βοήθεια" }, "role": { "role": "Ρόλος", 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 6c4614abbb..4eb933435f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -989,16 +989,16 @@ "type-timeseries-deleted": "Telemetry deleted", "type-sms-sent": "SMS sent" }, - "debug-config": { - "min": "{{number}} min", + "debug-settings": { "label": "Debug configuration", "on-failure": "Failures only (24/7)", "all-messages": "All messages ({{time}})", "failures": "Failures", - "all": "All", + "entity": "entity", + "rule-node": "rule node", "hint": { "main": "All node debug messages rate limited with:", - "main-limited": "All node debug messages will be rate-limited, with a maximum of {{msg}} messages allowed per {{sec}} seconds.", + "main-limited": "All {{entity}} debug messages will be rate-limited, with a maximum of {{msg}} messages allowed per {{time}}.", "on-failure": "Save all failure debug events without time limit.", "all-messages": "Save all debug events during time limit." } @@ -3384,7 +3384,9 @@ "module-no-members": "Module has no exported members", "module-load-error": "Module load error", "source-code": "Source code", - "source-code-load-error": "Source code load error" + "source-code-load-error": "Source code load error", + "no-js-module-text": "No JS modules found", + "no-js-module-matching": "No JS modules matching '{{module}}' were found." }, "key-val": { "key": "Key", @@ -3602,7 +3604,8 @@ "tablet-959": "Tablet (max 959px)", "max-element-number": "Max elements number", "page-name": "Page name", - "page-nam-required": "Page name is required.", + "page-name-required": "Page name is required.", + "page-name-cannot-contain-only-spaces": "Page name cannot contain only spaces.", "page-type": "Page type", "pages-types": { "dashboard": "Dashboard", @@ -3610,9 +3613,9 @@ "custom": "Custom" }, "url": "URL", - "url-pattern": "Invalid URL", + "invalid-url-format": "Invalid URL format", "path": "Path", - "path-pattern": "Path pattern", + "invalid-path-format": "Invalid path format", "custom-page": "Custom page", "edit-page": "Edit page", "edit-custom-page": "Edit custom page", @@ -4345,7 +4348,7 @@ "output": "Output", "test": "Test", "help": "Help", - "reset-debug-mode": "Reset debug mode in all nodes", + "reset-debug-settings": "Reset debug settings in all nodes", "test-with-this-message": "{{test}} with this message", "queue-hint": "Select a queue for message forwarding to another queue. 'Main' queue is used by default.", "queue-singleton-hint": "Select a queue for message forwarding in multi-instance environments. 'Main' queue is used by default." @@ -4795,6 +4798,7 @@ "sec": "{{ sec }} sec", "sec-short": "{{ sec }}s", "short": { + "years": "{ years, plural, =1 {1 year } other {# years } }", "days": "{ days, plural, =1 {1 day } other {# days } }", "hours": "{ hours, plural, =1 {1 hour } other {# hours } }", "minutes": "{{minutes}} min ", diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index 0cb9528bdd..1122c1d6fa 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -3529,7 +3529,6 @@ "output": "Salida", "test": "Test", "help": "Ayuda", - "reset-debug-mode": "Restablecer el modo de depuración en todos los nodos", "test-with-this-message": "{{test}} con este mensaje" }, "timezone": { diff --git a/ui-ngx/src/assets/locale/locale.constant-ka_GE.json b/ui-ngx/src/assets/locale/locale.constant-ka_GE.json index f645cb0f31..ae8ce2b943 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ka_GE.json +++ b/ui-ngx/src/assets/locale/locale.constant-ka_GE.json @@ -1413,8 +1413,7 @@ "metadata-required": "მეტამონაცემები ვერ იქნება ცარიელი", "output": "რეზულტატი", "test": "ტესტი", - "help": "დახმარება", - "reset-debug-mode": "Debug რეჟიმის გათიშვა ყველა ნოდისთვის" + "help": "დახმარება" }, "tenant": { "tenant": "ტენანტი", diff --git a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json index 9235fc9e95..80a2cb0830 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json +++ b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json @@ -1878,8 +1878,7 @@ "metadata-required": "메타데이터 엔트리를 입력하세요.", "output": "출력", "test": "테스트", - "help": "도움말", - "reset-debug-mode": "모든 노드에 대해 디버그 모드 초기화" + "help": "도움말" }, "timezone": { "timezone": "Timezone", diff --git a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json index c856652198..89191ea064 100644 --- a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json +++ b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json @@ -4504,7 +4504,6 @@ "output": "Output", "test": "Test", "help": "Help", - "reset-debug-mode": "Reset debug mode in all nodes", "test-with-this-message": "{{test}} with this message" }, "role": { diff --git a/ui-ngx/src/assets/locale/locale.constant-lv_LV.json b/ui-ngx/src/assets/locale/locale.constant-lv_LV.json index 0f5c946063..bba71609ec 100644 --- a/ui-ngx/src/assets/locale/locale.constant-lv_LV.json +++ b/ui-ngx/src/assets/locale/locale.constant-lv_LV.json @@ -1338,8 +1338,7 @@ "metadata-required": "Metadatu ievadi nevar būt tukši.", "output": "Izeja", "test": "Tests", - "help": "Palīdzība", - "reset-debug-mode": "Atiestatīt atkļūdošanu visās nodēs" + "help": "Palīdzība" }, "tenant": { "tenant": "Īrnieks", diff --git a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json index f9fe60f44d..7b99ad9953 100644 --- a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json +++ b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json @@ -4560,8 +4560,7 @@ "metadata-required": "Metagegevensvermeldingen mogen niet leeg zijn.", "output": "Uitvoer", "test": "Test", - "help": "Help", - "reset-debug-mode": "Foutopsporingsmodus resetten in alle rule nodes" + "help": "Help" }, "role": { "role": "Rol", diff --git a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json index 9ab89169c0..b369f061b7 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -4520,7 +4520,6 @@ "output": "Wyjście", "test": "Test", "help": "Pomoc", - "reset-debug-mode": "Zresetuj tryb debugowania we wszystkich węzłach", "test-with-this-message": "{{test}} with this message", "description": "Opis" }, diff --git a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json index 8b77be4573..bfc616e40b 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json +++ b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json @@ -1554,8 +1554,7 @@ "metadata-required": "As entradas de metadados não podem estar em branco.", "output": "Saída", "test": "Teste", - "help": "Ajuda", - "reset-debug-mode": "Redefinir modo de depuração em todos os nós" + "help": "Ajuda" }, "timezone": { "timezone": "Fuso horário", diff --git a/ui-ngx/src/assets/locale/locale.constant-ro_RO.json b/ui-ngx/src/assets/locale/locale.constant-ro_RO.json index 2eb5bc491a..1054b812a1 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ro_RO.json +++ b/ui-ngx/src/assets/locale/locale.constant-ro_RO.json @@ -1401,7 +1401,6 @@ "output": "Ieşire", "test": "Test", "help": "Ajutor", - "reset-debug-mode": "Dezactivează modul depanare în toate nodurile" }, "tenant": { "tenant": "Locatar", diff --git a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json index ff23223107..668bf8c2c9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json +++ b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json @@ -1879,8 +1879,7 @@ "metadata-required": "Vnosi metapodatkov ne smejo biti prazni.", "output": "Izdelek", "test": "Test", - "help": "Pomoč", - "reset-debug-mode": "Ponastavi način za odpravljanje napak v vseh vozliščih" + "help": "Pomoč" }, "timezone": { "timezone": "Časovni pas", diff --git a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json index e954e8f2e1..98998ab312 100644 --- a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json +++ b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json @@ -2447,8 +2447,7 @@ "metadata-required": "Meta veri girişleri boş bırakılamaz.", "output": "Çıktı", "test": "Ölçek", - "help": "Yardım et", - "reset-debug-mode": "Tüm düğümlerde hata ayıklama modunu sıfırla" + "help": "Yardım et" }, "timezone": { "timezone": "Saat dilimi", diff --git a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json index e1bf787994..acb837a587 100644 --- a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json +++ b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json @@ -1979,7 +1979,7 @@ "output": "Вихід", "test": "Тест", "help": "Допомога", - "reset-debug-mode": "Вимкнути режим налагодження у всіх правилах" + "reset-debug-settings": "Вимкнути налаштування налагодження у всіх правилах" }, "scheduler": { "scheduler": "Планувальник", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index f0dacca36d..47096fe5f2 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -4012,7 +4012,6 @@ "output": "输出", "test": "测试", "help": "帮助", - "reset-debug-mode": "重置所有节点中的调试模式", "test-with-this-message": "使用此消息进行{{test}}测试", "queue-hint": "选择一个队列将消息转发到另一个队列,默认情况下使用'Main'队列。", "queue-singleton-hint": "选择一个队列以在多实体中转发消息,默认情况下使用'Main'队列。" diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json index a100d95fa4..64b4c71f16 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json @@ -2768,8 +2768,7 @@ "metadata-required": "元資料項不能為空。", "output": "輸出", "test": "測試", - "help": "幫助", - "reset-debug-mode": "重置所有節點中的調試模式" + "help": "幫助" }, "timezone": { "timezone": "時區", diff --git a/ui-ngx/tailwind.config.js b/ui-ngx/tailwind.config.js index f459324348..e53b102710 100644 --- a/ui-ngx/tailwind.config.js +++ b/ui-ngx/tailwind.config.js @@ -103,7 +103,8 @@ module.exports = { '25': '6.25rem', '37.5': '9.375rem', '62.5': '15.625rem', - '72.5': '18.125rem' + '72.5': '18.125rem', + '147.5': '36.875rem' }, maxWidth: { '5%': '5%',