Browse Source

Add LTS 4.3.1.2 and 4.3.1.3 versioned migrations

pull/15809/head
Viacheslav Klimov 1 month ago
parent
commit
17d652d9b3
Failed to extract signature
  1. 21
      application/src/main/data/upgrade/lts/4.3.1.2/schema_update.sql
  2. 37
      application/src/main/data/upgrade/lts/4.3.1.3/schema_update.sql
  3. 29
      application/src/main/java/org/thingsboard/server/service/install/lts/V4_3_1_2Migration.java
  4. 73
      application/src/main/java/org/thingsboard/server/service/install/lts/V4_3_1_3Migration.java
  5. 96
      application/src/test/java/org/thingsboard/server/service/install/lts/V4_3_1_3MigrationTest.java

21
application/src/main/data/upgrade/lts/4.3.1.2/schema_update.sql

@ -0,0 +1,21 @@
--
-- Copyright © 2016-2026 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.
--
-- CALCULATED FIELD ADDITIONAL INFO ADDITION START
ALTER TABLE calculated_field ADD COLUMN IF NOT EXISTS additional_info varchar;
-- CALCULATED FIELD ADDITIONAL INFO ADDITION END

37
application/src/main/data/upgrade/lts/4.3.1.3/schema_update.sql

@ -0,0 +1,37 @@
--
-- Copyright © 2016-2026 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.
--
-- IOT HUB INSTALLED ITEM START
CREATE TABLE IF NOT EXISTS iot_hub_installed_item (
id UUID NOT NULL PRIMARY KEY,
created_time BIGINT NOT NULL,
tenant_id UUID NOT NULL,
item_id UUID NOT NULL,
item_version_id UUID NOT NULL,
item_name VARCHAR NOT NULL,
item_type VARCHAR NOT NULL,
version VARCHAR NOT NULL,
descriptor JSONB NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_iot_hub_installed_item_tenant_id ON iot_hub_installed_item(tenant_id);
CREATE INDEX IF NOT EXISTS idx_iot_hub_installed_item_item_type ON iot_hub_installed_item(tenant_id, item_type);
CREATE INDEX IF NOT EXISTS idx_iot_hub_installed_item_item_id ON iot_hub_installed_item(tenant_id, item_id);
-- IOT HUB INSTALLED ITEM END

29
application/src/main/java/org/thingsboard/server/service/install/lts/V4_3_1_2Migration.java

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2026 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.service.install.lts;
import org.springframework.stereotype.Component;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@TbCoreComponent
public class V4_3_1_2Migration implements LtsMigration {
@Override
public String getVersion() {
return "4.3.1.2";
}
}

73
application/src/main/java/org/thingsboard/server/service/install/lts/V4_3_1_3Migration.java

@ -0,0 +1,73 @@
/**
* Copyright © 2016-2026 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.service.install.lts;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.List;
@Slf4j
@Component
@TbCoreComponent
@RequiredArgsConstructor
public class V4_3_1_3Migration implements LtsMigration {
// Match on the bundle ALIAS, not the JSON filename stem.
// The "industrial_widgets" bundle shipped in a misspelled file (industial_widgets.json),
// but the alias inside it is correctly spelled.
private static final List<String> OBSOLETE_BUNDLE_ALIASES = List.of(
"air_quality", "indoor_environment", "industrial_widgets", "outdoor_environment");
private final WidgetsBundleService widgetsBundleService;
private final WidgetTypeService widgetTypeService;
@Override
public String getVersion() {
return "4.3.1.3";
}
@Override
public void apply() {
for (String alias : OBSOLETE_BUNDLE_ALIASES) {
deprecateTypesAndDeleteBundle(alias);
}
}
// Marks the bundle's widget types as deprecated (keeping the type entities) and deletes ONLY the bundle entity.
private void deprecateTypesAndDeleteBundle(String alias) {
WidgetsBundle bundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(TenantId.SYS_TENANT_ID, alias);
if (bundle == null) {
return; // already removed — idempotent
}
List<WidgetTypeDetails> types = widgetTypeService.findWidgetTypesDetailsByWidgetsBundleId(TenantId.SYS_TENANT_ID, bundle.getId());
for (WidgetTypeDetails type : types) {
if (!type.isDeprecated()) {
type.setDeprecated(true);
widgetTypeService.saveWidgetType(type);
}
}
widgetsBundleService.deleteWidgetsBundle(TenantId.SYS_TENANT_ID, bundle.getId());
log.info("Deprecated {} widget type(s) and removed obsolete system widget bundle: {}", types.size(), alias);
}
}

96
application/src/test/java/org/thingsboard/server/service/install/lts/V4_3_1_3MigrationTest.java

@ -0,0 +1,96 @@
/**
* Copyright © 2016-2026 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.service.install.lts;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import java.util.List;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class V4_3_1_3MigrationTest {
@Mock
private WidgetsBundleService widgetsBundleService;
@Mock
private WidgetTypeService widgetTypeService;
@InjectMocks
private V4_3_1_3Migration migration;
@Test
void versionIs4313() {
assertEquals("4.3.1.3", migration.getVersion());
}
@Test
void deprecatesTypesThenDeletesBundleEntityOnly() {
WidgetsBundle bundle = new WidgetsBundle();
bundle.setId(new WidgetsBundleId(UUID.randomUUID()));
bundle.setAlias("air_quality");
WidgetTypeDetails fresh = new WidgetTypeDetails();
fresh.setId(new WidgetTypeId(UUID.randomUUID()));
fresh.setDeprecated(false);
WidgetTypeDetails already = new WidgetTypeDetails();
already.setId(new WidgetTypeId(UUID.randomUUID()));
already.setDeprecated(true);
when(widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(TenantId.SYS_TENANT_ID, "air_quality")).thenReturn(bundle);
when(widgetTypeService.findWidgetTypesDetailsByWidgetsBundleId(TenantId.SYS_TENANT_ID, bundle.getId())).thenReturn(List.of(fresh, already));
when(widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(TenantId.SYS_TENANT_ID, "indoor_environment")).thenReturn(null);
when(widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(TenantId.SYS_TENANT_ID, "industrial_widgets")).thenReturn(null);
when(widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(TenantId.SYS_TENANT_ID, "outdoor_environment")).thenReturn(null);
migration.apply();
// only the previously non-deprecated type is re-saved, now deprecated
verify(widgetTypeService).saveWidgetType(argThat(WidgetTypeDetails::isDeprecated));
// widget types are NOT deleted
verify(widgetTypeService, never()).deleteWidgetTypesByBundleId(any(), any());
// only the bundle entity is removed
verify(widgetsBundleService).deleteWidgetsBundle(TenantId.SYS_TENANT_ID, bundle.getId());
}
@Test
void absentBundleIsNoOp() {
when(widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(any(), any())).thenReturn(null);
migration.apply();
verify(widgetTypeService, never()).saveWidgetType(any());
verify(widgetsBundleService, never()).deleteWidgetsBundle(any(), any());
}
}
Loading…
Cancel
Save