From c303d4dea3379d8d35521e7435b036288798ae32 Mon Sep 17 00:00:00 2001 From: deaflynx Date: Mon, 8 May 2023 17:54:27 +0300 Subject: [PATCH 1/4] Fix PROD-1975: ValueInputComponent change: add updateView() after closing openEditJSONDialog --- ui-ngx/src/app/shared/components/value-input.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-ngx/src/app/shared/components/value-input.component.ts b/ui-ngx/src/app/shared/components/value-input.component.ts index 946d320241..4ba7abf157 100644 --- a/ui-ngx/src/app/shared/components/value-input.component.ts +++ b/ui-ngx/src/app/shared/components/value-input.component.ts @@ -80,6 +80,7 @@ export class ValueInputComponent implements OnInit, ControlValueAccessor { if (res) { this.modelValue = res; this.inputForm.control.patchValue({value: this.modelValue}); + this.updateView(); } } ); From f1be847bfb9d8cac2efd2758f35ee8fc28866c81 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 24 May 2023 13:52:57 +0300 Subject: [PATCH 2/4] UI: Leaflet Map - add latitude/longitude validation. Improve code style. --- .../components/widget/lib/maps/leaflet-map.ts | 26 ++++++++++++------- .../components/widget/lib/maps/maps-utils.ts | 15 ++++++++++- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts index 7b63176203..64ef1a6303 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts @@ -36,7 +36,13 @@ import { Observable, of } from 'rxjs'; import { Polyline } from './polyline'; import { Polygon } from './polygon'; import { Circle } from './circle'; -import { createTooltip, entitiesParseName, isCutPolygon, isJSON } from '@home/components/widget/lib/maps/maps-utils'; +import { + createTooltip, + entitiesParseName, + isCutPolygon, + isJSON, + isValidLatLng +} from '@home/components/widget/lib/maps/maps-utils'; import { checkLngLat, createLoadingDiv } from '@home/components/widget/lib/maps/common-maps-utils'; import { WidgetContext } from '@home/models/widget-component.models'; import { @@ -148,7 +154,8 @@ export default abstract class LeafletMap { if (isDefinedAndNotNull(markerColor) && tinycolor(markerColor).isValid()) { const parsedColor = tinycolor(markerColor); return L.divIcon({ - html: `
` + + html: `
` + `
` + childCount + '
', iconSize: new L.Point(40, 40), className: 'tb-cluster-marker-container' @@ -378,7 +385,8 @@ export default abstract class LeafletMap { }); }, }); - this.map.pm.Toolbar.changeControlOrder(['tbMarker', 'tbRectangle', 'tbPolygon', 'tbCircle', 'editMode', 'dragMode', 'tbCut', 'removalMode', 'rotateMode']); + this.map.pm.Toolbar.changeControlOrder(['tbMarker', 'tbRectangle', 'tbPolygon', 'tbCircle', + 'editMode', 'dragMode', 'tbCut', 'removalMode', 'rotateMode']); } this.map.pm.setLang('en', this.translateService.instant('widgets.maps'), 'en'); @@ -643,19 +651,19 @@ export default abstract class LeafletMap { } } - extractPosition(data: FormattedData): {x: number, y: number} { + extractPosition(data: FormattedData): {x: number; y: number} { if (!data) { return null; } const lat = data[this.options.latKeyName]; const lng = data[this.options.lngKeyName]; - if (!isDefinedAndNotNull(lat) || isString(lat) || isNaN(lat) || !isDefinedAndNotNull(lng) || isString(lng) || isNaN(lng)) { + if (!isValidLatLng(lat, lng)) { return null; } return {x: lat, y: lng}; } - positionToLatLng(position: {x: number, y: number}): L.LatLng { + positionToLatLng(position: {x: number; y: number}): L.LatLng { return L.latLng(position.x, position.y) as L.LatLng; } @@ -936,7 +944,7 @@ export default abstract class LeafletMap { return; } this.saveLocation(data, this.convertToCustomFormat(e.target._latlng)).subscribe(); - } + }; private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: Partial, updateBounds = true, callback?, snappable = false): Marker { @@ -1141,7 +1149,7 @@ export default abstract class LeafletMap { } } this.saveLocation(data, this.convertPolygonToCustomFormat(coordinates)).subscribe(() => {}); - } + }; createPolygon(polyData: FormattedData, dataSources: FormattedData[], settings: Partial, updateBounds = true, snappable = false) { @@ -1218,7 +1226,7 @@ export default abstract class LeafletMap { const center = e.layer.getLatLng(); const radius = e.layer.getRadius(); this.saveLocation(data, this.convertCircleToCustomFormat(center, radius)).subscribe(() => {}); - } + }; updateCircle(circlesData: FormattedData[], updateBounds = true) { const toDelete = new Set(Array.from(this.circles.keys())); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts index 4aad829eac..81005dee07 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts @@ -20,7 +20,7 @@ import { ShowTooltipAction, WidgetToolipSettings } from './map-models'; import { Datasource, FormattedData } from '@app/shared/models/widget.models'; -import { fillDataPattern, processDataPattern, safeExecute } from '@core/utils'; +import { fillDataPattern, isDefinedAndNotNull, isString, processDataPattern, safeExecute } from '@core/utils'; import { parseWithTranslation } from '@home/components/widget/lib/maps/common-maps-utils'; export function createTooltip(target: L.Layer, @@ -110,3 +110,16 @@ export function entitiesParseName(entities: FormattedData[], labelSettings: labe } return entities; } + +export const isValidLatitude = (latitude: any): boolean => + isDefinedAndNotNull(latitude) && + !isString(latitude) && + !isNaN(latitude) && isFinite(latitude) && Math.abs(latitude) <= 90; + +export const isValidLongitude = (longitude: any): boolean => + isDefinedAndNotNull(longitude) && + !isString(longitude) && + !isNaN(longitude) && isFinite(longitude) && Math.abs(longitude) <= 180; + +export const isValidLatLng = (latitude: any, longitude: any): boolean => + isValidLatitude(latitude) && isValidLongitude(longitude); From 8311017b92cd60d0f9f162ee7df4aafffe1076de Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Wed, 24 May 2023 16:51:00 +0300 Subject: [PATCH 3/4] Fixed alarm details on alarm clear --- .../install/ThingsboardInstallService.java | 10 ++++--- .../update/DefaultCacheCleanupService.java | 3 +- .../server/dao/sql/alarm/JpaAlarmDao.java | 2 +- .../sql/schema-views-and-functions.sql | 6 +++- .../server/dao/sql/alarm/JpaAlarmDaoTest.java | 30 ++++++++++++++++++- 5 files changed, 43 insertions(+), 8 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index ea13de3779..b8ab38b14e 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -249,15 +249,17 @@ public class ThingsboardInstallService { case "3.4.4": log.info("Upgrading ThingsBoard from version 3.4.4 to 3.5.0 ..."); databaseEntitiesUpgradeService.upgradeDatabase("3.4.4"); - entityDatabaseSchemaService.createOrUpdateViewsAndFunctions(); - entityDatabaseSchemaService.createOrUpdateDeviceInfoView(persistToTelemetry); - log.info("Updating system data..."); - systemDataLoaderService.updateSystemWidgets(); if (!getEnv("SKIP_DEFAULT_NOTIFICATION_CONFIGS_CREATION", false)) { systemDataLoaderService.createDefaultNotificationConfigs(); } else { log.info("Skipping default notification configs creation"); } + case "3.5.0": + log.info("Upgrading ThingsBoard from version 3.5.0 to 3.5.1 ..."); + entityDatabaseSchemaService.createOrUpdateViewsAndFunctions(); + entityDatabaseSchemaService.createOrUpdateDeviceInfoView(persistToTelemetry); + log.info("Updating system data..."); + systemDataLoaderService.updateSystemWidgets(); installScripts.loadSystemLwm2mResources(); break; //TODO update CacheCleanupService on the next version upgrade diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultCacheCleanupService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultCacheCleanupService.java index 1e54d8d48f..278fc0adf5 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultCacheCleanupService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultCacheCleanupService.java @@ -84,7 +84,8 @@ public class DefaultCacheCleanupService implements CacheCleanupService { break; case "3.4.4": log.info("Clearing cache to upgrade from version 3.4.4 to 3.5.0"); - clearCacheByName("deviceProfiles"); + clearAll(); + break; default: //Do nothing, since cache cleanup is optional. } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java index d9e79dd594..8dfdaf8a3e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java @@ -348,7 +348,7 @@ public class JpaAlarmDao extends JpaAbstractDao implements A @Override public AlarmApiCallResult clearAlarm(TenantId tenantId, AlarmId id, long clearTs, JsonNode details) { - return toAlarmApiResult(alarmRepository.clearAlarm(tenantId.getId(), id.getId(), clearTs, getDetailsAsString(details))); + return toAlarmApiResult(alarmRepository.clearAlarm(tenantId.getId(), id.getId(), clearTs, details != null ? getDetailsAsString(details) : null)); } @Override diff --git a/dao/src/main/resources/sql/schema-views-and-functions.sql b/dao/src/main/resources/sql/schema-views-and-functions.sql index 2de757be54..eaa09d78cc 100644 --- a/dao/src/main/resources/sql/schema-views-and-functions.sql +++ b/dao/src/main/resources/sql/schema-views-and-functions.sql @@ -226,7 +226,11 @@ BEGIN END IF; IF NOT(existing.cleared) THEN cleared = TRUE; - UPDATE alarm a SET cleared = true, clear_ts = a_ts, additional_info = a_details WHERE a.id = a_id AND a.tenant_id = t_id; + IF a_details IS NULL THEN + UPDATE alarm a SET cleared = true, clear_ts = a_ts WHERE a.id = a_id AND a.tenant_id = t_id; + ELSE + UPDATE alarm a SET cleared = true, clear_ts = a_ts, additional_info = a_details WHERE a.id = a_id AND a.tenant_id = t_id; + END IF; END IF; SELECT * INTO result FROM alarm_info a WHERE a.id = a_id AND a.tenant_id = t_id; RETURN json_build_object('success', true, 'cleared', cleared, 'alarm', row_to_json(result))::text; diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java index ea35933d4d..25231f822f 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java @@ -202,7 +202,34 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest { @Test public void testClearAlarmProcedure() { UUID tenantId = UUID.randomUUID(); - ; + UUID originator1Id = UUID.fromString("d4b68f41-3e96-11e7-a884-898080180d6b"); + UUID alarm1Id = UUID.fromString("d4b68f43-3e96-11e7-a884-898080180d6b"); + Alarm alarm = saveAlarm(alarm1Id, tenantId, originator1Id, "TEST_ALARM"); + long clearTs = System.currentTimeMillis(); + var details = JacksonUtil.newObjectNode().put("test", 123); + AlarmApiCallResult result = alarmDao.clearAlarm(alarm.getTenantId(), alarm.getId(), clearTs, details); + AlarmInfo afterSave = alarmDao.findAlarmInfoById(alarm.getTenantId(), alarm.getUuidId()); + assertNotNull(result); + assertTrue(result.isSuccessful()); + assertTrue(result.isCleared()); + assertNotNull(result.getAlarm()); + assertEquals(afterSave, result.getAlarm()); + assertEquals(clearTs, result.getAlarm().getClearTs()); + assertTrue(result.getAlarm().isCleared()); + assertEquals(details, result.getAlarm().getDetails()); + result = alarmDao.clearAlarm(alarm.getTenantId(), alarm.getId(), clearTs + 1, JacksonUtil.newObjectNode()); + assertNotNull(result); + assertNotNull(result.getAlarm()); + assertEquals(afterSave, result.getAlarm()); + assertTrue(result.isSuccessful()); + assertFalse(result.isCleared()); + assertEquals(clearTs, result.getAlarm().getClearTs()); + assertTrue(result.getAlarm().isCleared()); + } + + @Test + public void testClearAlarmWithoutDetailsProcedure() { + UUID tenantId = UUID.randomUUID(); UUID originator1Id = UUID.fromString("d4b68f41-3e96-11e7-a884-898080180d6b"); UUID alarm1Id = UUID.fromString("d4b68f43-3e96-11e7-a884-898080180d6b"); Alarm alarm = saveAlarm(alarm1Id, tenantId, originator1Id, "TEST_ALARM"); @@ -216,6 +243,7 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest { assertEquals(afterSave, result.getAlarm()); assertEquals(clearTs, result.getAlarm().getClearTs()); assertTrue(result.getAlarm().isCleared()); + assertEquals(alarm.getDetails(), result.getAlarm().getDetails()); result = alarmDao.clearAlarm(alarm.getTenantId(), alarm.getId(), clearTs + 1, JacksonUtil.newObjectNode()); assertNotNull(result); assertNotNull(result.getAlarm()); From 138c0e4f8fd6763002e71f320211fc183f018ca6 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Wed, 24 May 2023 17:05:23 +0300 Subject: [PATCH 4/4] Simplify upgrage procedure --- .../server/install/ThingsboardInstallService.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index b8ab38b14e..04f0cc78c8 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -256,17 +256,16 @@ public class ThingsboardInstallService { } case "3.5.0": log.info("Upgrading ThingsBoard from version 3.5.0 to 3.5.1 ..."); - entityDatabaseSchemaService.createOrUpdateViewsAndFunctions(); - entityDatabaseSchemaService.createOrUpdateDeviceInfoView(persistToTelemetry); - log.info("Updating system data..."); - systemDataLoaderService.updateSystemWidgets(); - installScripts.loadSystemLwm2mResources(); + //TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache break; - //TODO update CacheCleanupService on the next version upgrade default: throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion); - } + entityDatabaseSchemaService.createOrUpdateViewsAndFunctions(); + entityDatabaseSchemaService.createOrUpdateDeviceInfoView(persistToTelemetry); + log.info("Updating system data..."); + systemDataLoaderService.updateSystemWidgets(); + installScripts.loadSystemLwm2mResources(); } log.info("Upgrade finished successfully!");