Version control: fix ids replacement in dashboard config
This commit is contained in:
		
							parent
							
								
									94dbb1a682
								
							
						
					
					
						commit
						4114c96571
					
				@ -48,8 +48,8 @@ public abstract class BaseEntityExportService<I extends EntityId, E extends Expo
 | 
			
		||||
 | 
			
		||||
    public abstract Set<EntityType> getSupportedEntityTypes();
 | 
			
		||||
 | 
			
		||||
    protected void replaceUuidsRecursively(EntitiesExportCtx<?> ctx, JsonNode node, Set<String> skipFieldsSet, Pattern includedFieldsPattern) {
 | 
			
		||||
        JacksonUtil.replaceUuidsRecursively(node, skipFieldsSet, includedFieldsPattern, uuid -> getExternalIdOrElseInternalByUuid(ctx, uuid));
 | 
			
		||||
    protected void replaceUuidsRecursively(EntitiesExportCtx<?> ctx, JsonNode node, Set<String> skippedRootFields, Pattern includedFieldsPattern) {
 | 
			
		||||
        JacksonUtil.replaceUuidsRecursively(node, skippedRootFields, includedFieldsPattern, uuid -> getExternalIdOrElseInternalByUuid(ctx, uuid), true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected Stream<UUID> toExternalIds(Collection<UUID> internalIds, Function<UUID, EntityId> entityIdCreator,
 | 
			
		||||
 | 
			
		||||
@ -26,8 +26,11 @@ import org.thingsboard.server.common.data.sync.ie.EntityExportData;
 | 
			
		||||
import org.thingsboard.server.queue.util.TbCoreComponent;
 | 
			
		||||
import org.thingsboard.server.service.sync.vc.data.EntitiesExportCtx;
 | 
			
		||||
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import static org.thingsboard.server.service.sync.ie.importing.impl.DashboardImportService.WIDGET_CONFIG_PROCESSED_FIELDS_PATTERN;
 | 
			
		||||
 | 
			
		||||
@Service
 | 
			
		||||
@TbCoreComponent
 | 
			
		||||
public class DashboardExportService extends BaseEntityExportService<DashboardId, Dashboard, EntityExportData<Dashboard>> {
 | 
			
		||||
@ -43,7 +46,7 @@ public class DashboardExportService extends BaseEntityExportService<DashboardId,
 | 
			
		||||
            replaceUuidsRecursively(ctx, entityAlias, Set.of("id"), null);
 | 
			
		||||
        }
 | 
			
		||||
        for (JsonNode widgetConfig : dashboard.getWidgetsConfig()) {
 | 
			
		||||
            replaceUuidsRecursively(ctx, JacksonUtil.getSafely(widgetConfig, "config", "actions"), Set.of("id"), null);
 | 
			
		||||
            replaceUuidsRecursively(ctx, JacksonUtil.getSafely(widgetConfig, "config", "actions"), Collections.emptySet(), WIDGET_CONFIG_PROCESSED_FIELDS_PATTERN);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -388,11 +388,11 @@ public abstract class BaseEntityImportService<I extends EntityId, E extends Expo
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void replaceIdsRecursively(EntitiesImportCtx ctx, IdProvider idProvider, JsonNode json,
 | 
			
		||||
                                         Set<String> skipFieldsSet, Pattern includedFieldsPattern,
 | 
			
		||||
                                         Set<String> skippedRootFields, Pattern includedFieldsPattern,
 | 
			
		||||
                                         LinkedHashSet<EntityType> hints) {
 | 
			
		||||
        JacksonUtil.replaceUuidsRecursively(json, skipFieldsSet, includedFieldsPattern,
 | 
			
		||||
        JacksonUtil.replaceUuidsRecursively(json, skippedRootFields, includedFieldsPattern,
 | 
			
		||||
                uuid -> idProvider.getInternalIdByUuid(uuid, ctx.isFinalImportAttempt(), hints)
 | 
			
		||||
                        .map(EntityId::getId).orElse(uuid));
 | 
			
		||||
                        .map(EntityId::getId).orElse(uuid), true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,7 @@ import java.util.HashSet;
 | 
			
		||||
import java.util.LinkedHashSet;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
@Service
 | 
			
		||||
@ -44,6 +45,7 @@ import java.util.stream.Collectors;
 | 
			
		||||
public class DashboardImportService extends BaseEntityImportService<DashboardId, Dashboard, EntityExportData<Dashboard>> {
 | 
			
		||||
 | 
			
		||||
    private static final LinkedHashSet<EntityType> HINTS = new LinkedHashSet<>(Arrays.asList(EntityType.DASHBOARD, EntityType.DEVICE, EntityType.ASSET));
 | 
			
		||||
    public static final Pattern WIDGET_CONFIG_PROCESSED_FIELDS_PATTERN = Pattern.compile(".*Id.*");
 | 
			
		||||
 | 
			
		||||
    private final DashboardService dashboardService;
 | 
			
		||||
 | 
			
		||||
@ -68,7 +70,7 @@ public class DashboardImportService extends BaseEntityImportService<DashboardId,
 | 
			
		||||
            replaceIdsRecursively(ctx, idProvider, entityAlias, Set.of("id"), null, HINTS);
 | 
			
		||||
        }
 | 
			
		||||
        for (JsonNode widgetConfig : dashboard.getWidgetsConfig()) {
 | 
			
		||||
            replaceIdsRecursively(ctx, idProvider, JacksonUtil.getSafely(widgetConfig, "config", "actions"), Set.of("id"), null, HINTS);
 | 
			
		||||
            replaceIdsRecursively(ctx, idProvider, JacksonUtil.getSafely(widgetConfig, "config", "actions"), Collections.emptySet(), WIDGET_CONFIG_PROCESSED_FIELDS_PATTERN, HINTS);
 | 
			
		||||
        }
 | 
			
		||||
        return dashboard;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -257,6 +257,7 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest {
 | 
			
		||||
        Asset asset1 = createAsset(tenantId1, null, assetProfile.getId(), "Asset 1");
 | 
			
		||||
        Asset asset2 = createAsset(tenantId1, null, assetProfile.getId(), "Asset 2");
 | 
			
		||||
        Dashboard dashboard = createDashboard(tenantId1, null, "Dashboard 1");
 | 
			
		||||
        Dashboard otherDashboard = createDashboard(tenantId1, null, "Dashboard 2");
 | 
			
		||||
        DeviceProfile existingDeviceProfile = createDeviceProfile(tenantId2, null, null, "Existing");
 | 
			
		||||
 | 
			
		||||
        String aliasId = "23c4185d-1497-9457-30b2-6d91e69a5b2c";
 | 
			
		||||
@ -265,20 +266,43 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest {
 | 
			
		||||
                "\"" + aliasId + "\": {\n" +
 | 
			
		||||
                "\"alias\": \"assets\",\n" +
 | 
			
		||||
                "\"filter\": {\n" +
 | 
			
		||||
                "\"entityList\": [\n" +
 | 
			
		||||
                "\"" + asset1.getId().toString() + "\",\n" +
 | 
			
		||||
                "\"" + asset2.getId().toString() + "\",\n" +
 | 
			
		||||
                "\"" + tenantId1.getId().toString() + "\",\n" +
 | 
			
		||||
                "\"" + existingDeviceProfile.getId().toString() + "\",\n" +
 | 
			
		||||
                "\"" + unknownUuid + "\"\n" +
 | 
			
		||||
                "],\n" +
 | 
			
		||||
                "\"resolveMultiple\": true\n" +
 | 
			
		||||
                "   \"entityList\": [\n" +
 | 
			
		||||
                "   \"" + asset1.getId() + "\",\n" +
 | 
			
		||||
                "   \"" + asset2.getId() + "\",\n" +
 | 
			
		||||
                "   \"" + tenantId1.getId() + "\",\n" +
 | 
			
		||||
                "   \"" + existingDeviceProfile.getId() + "\",\n" +
 | 
			
		||||
                "   \"" + unknownUuid + "\"\n" +
 | 
			
		||||
                "   ],\n" +
 | 
			
		||||
                "   \"id\":\"" + asset1.getId() + "\",\n" +
 | 
			
		||||
                "   \"resolveMultiple\": true\n" +
 | 
			
		||||
                "},\n" +
 | 
			
		||||
                "\"id\": \"" + aliasId + "\"\n" +
 | 
			
		||||
                "}\n" +
 | 
			
		||||
                "}";
 | 
			
		||||
        String widgetId = "ea8f34a0-264a-f11f-cde3-05201bb4ff4b";
 | 
			
		||||
        String actionId = "4a8e6efa-3e68-fa59-7feb-d83366130cae";
 | 
			
		||||
        String widgets = "{\n" +
 | 
			
		||||
                "  \"" + widgetId + "\": {\n" +
 | 
			
		||||
                "    \"config\": {\n" +
 | 
			
		||||
                "      \"actions\": {\n" +
 | 
			
		||||
                "        \"rowClick\": [\n" +
 | 
			
		||||
                "          {\n" +
 | 
			
		||||
                "            \"name\": \"go to dashboard\",\n" +
 | 
			
		||||
                "            \"targetDashboardId\": \"" + otherDashboard.getId() + "\",\n" +
 | 
			
		||||
                "            \"id\": \"" + actionId + "\"\n" +
 | 
			
		||||
                "          }\n" +
 | 
			
		||||
                "        ]\n" +
 | 
			
		||||
                "      }\n" +
 | 
			
		||||
                "    },\n" +
 | 
			
		||||
                "    \"row\": 0,\n" +
 | 
			
		||||
                "    \"col\": 0,\n" +
 | 
			
		||||
                "    \"id\": \"" + widgetId + "\"\n" +
 | 
			
		||||
                "  }\n" +
 | 
			
		||||
                "}";
 | 
			
		||||
 | 
			
		||||
        ObjectNode dashboardConfiguration = JacksonUtil.newObjectNode();
 | 
			
		||||
        dashboardConfiguration.set("entityAliases", JacksonUtil.toJsonNode(entityAliases));
 | 
			
		||||
        dashboardConfiguration.set("widgets", JacksonUtil.toJsonNode(widgets));
 | 
			
		||||
        dashboardConfiguration.set("description", new TextNode("hallo"));
 | 
			
		||||
        dashboard.setConfiguration(dashboardConfiguration);
 | 
			
		||||
        dashboard = dashboardService.saveDashboard(dashboard);
 | 
			
		||||
@ -288,10 +312,12 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest {
 | 
			
		||||
        EntityExportData<Asset> asset1ExportData = exportEntity(tenantAdmin1, asset1.getId());
 | 
			
		||||
        EntityExportData<Asset> asset2ExportData = exportEntity(tenantAdmin1, asset2.getId());
 | 
			
		||||
        EntityExportData<Dashboard> dashboardExportData = exportEntity(tenantAdmin1, dashboard.getId());
 | 
			
		||||
        EntityExportData<Dashboard> otherDashboardExportData = exportEntity(tenantAdmin1, otherDashboard.getId());
 | 
			
		||||
 | 
			
		||||
        AssetProfile importedProfile = importEntity(tenantAdmin2, profileExportData).getSavedEntity();
 | 
			
		||||
        Asset importedAsset1 = importEntity(tenantAdmin2, asset1ExportData).getSavedEntity();
 | 
			
		||||
        Asset importedAsset2 = importEntity(tenantAdmin2, asset2ExportData).getSavedEntity();
 | 
			
		||||
        Dashboard importedOtherDashboard = importEntity(tenantAdmin2, otherDashboardExportData).getSavedEntity();
 | 
			
		||||
        Dashboard importedDashboard = importEntity(tenantAdmin2, dashboardExportData).getSavedEntity();
 | 
			
		||||
 | 
			
		||||
        Map.Entry<String, JsonNode> entityAlias = importedDashboard.getConfiguration().get("entityAliases").fields().next();
 | 
			
		||||
@ -311,6 +337,17 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest {
 | 
			
		||||
                .isEqualTo(existingDeviceProfile.getId().toString());
 | 
			
		||||
        assertThat(aliasEntitiesIds).element(4).as("unresolved uuid was replaced with tenant id")
 | 
			
		||||
                .isEqualTo(tenantId2.toString());
 | 
			
		||||
        assertThat(entityAlias.getValue().get("filter").get("id").asText()).as("external asset 1 was replaced with imported one")
 | 
			
		||||
                .isEqualTo(importedAsset1.getId().toString());
 | 
			
		||||
 | 
			
		||||
        ObjectNode widgetConfig = importedDashboard.getWidgetsConfig().get(0);
 | 
			
		||||
        assertThat(widgetConfig.get("id").asText()).as("widget id is not replaced")
 | 
			
		||||
                .isEqualTo(widgetId);
 | 
			
		||||
        JsonNode actionConfig = widgetConfig.get("config").get("actions").get("rowClick").get(0);
 | 
			
		||||
        assertThat(actionConfig.get("id").asText()).as("action id is not replaced")
 | 
			
		||||
                .isEqualTo(actionId);
 | 
			
		||||
        assertThat(actionConfig.get("targetDashboardId").asText()).as("dashboard id is replaced with imported one")
 | 
			
		||||
                .isEqualTo(importedOtherDashboard.getId().toString());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,7 @@ import com.fasterxml.jackson.databind.json.JsonMapper;
 | 
			
		||||
import com.fasterxml.jackson.databind.node.ArrayNode;
 | 
			
		||||
import com.fasterxml.jackson.databind.node.ObjectNode;
 | 
			
		||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
 | 
			
		||||
import com.google.common.collect.Lists;
 | 
			
		||||
import org.thingsboard.server.common.data.kv.DataType;
 | 
			
		||||
import org.thingsboard.server.common.data.kv.KvEntry;
 | 
			
		||||
 | 
			
		||||
@ -35,7 +36,6 @@ import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.Reader;
 | 
			
		||||
import java.io.Writer;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
@ -228,27 +228,24 @@ public class JacksonUtil {
 | 
			
		||||
        return node;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void replaceUuidsRecursively(JsonNode node, Set<String> skipFieldsSet, Pattern includedFieldsPattern, UnaryOperator<UUID> replacer) {
 | 
			
		||||
    public static void replaceUuidsRecursively(JsonNode node, Set<String> skippedRootFields, Pattern includedFieldsPattern, UnaryOperator<UUID> replacer, boolean root) {
 | 
			
		||||
        if (node == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (node.isObject()) {
 | 
			
		||||
            ObjectNode objectNode = (ObjectNode) node;
 | 
			
		||||
            List<String> fieldNames = new ArrayList<>(objectNode.size());
 | 
			
		||||
            objectNode.fieldNames().forEachRemaining(fieldNames::add);
 | 
			
		||||
            List<String> fieldNames = Lists.newArrayList(objectNode.fieldNames());
 | 
			
		||||
            for (String fieldName : fieldNames) {
 | 
			
		||||
                if (skipFieldsSet.contains(fieldName)) {
 | 
			
		||||
                if (root && skippedRootFields.contains(fieldName)) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                if (includedFieldsPattern != null) {
 | 
			
		||||
                    if (!RegexUtils.matches(fieldName, includedFieldsPattern)) {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                var child = objectNode.get(fieldName);
 | 
			
		||||
                if (child.isObject() || child.isArray()) {
 | 
			
		||||
                    replaceUuidsRecursively(child, skipFieldsSet, includedFieldsPattern, replacer);
 | 
			
		||||
                    replaceUuidsRecursively(child, skippedRootFields, includedFieldsPattern, replacer, false);
 | 
			
		||||
                } else if (child.isTextual()) {
 | 
			
		||||
                    if (includedFieldsPattern != null && !RegexUtils.matches(fieldName, includedFieldsPattern)) {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    String text = child.asText();
 | 
			
		||||
                    String newText = RegexUtils.replace(text, RegexUtils.UUID_PATTERN, uuid -> replacer.apply(UUID.fromString(uuid)).toString());
 | 
			
		||||
                    if (!text.equals(newText)) {
 | 
			
		||||
@ -261,7 +258,7 @@ public class JacksonUtil {
 | 
			
		||||
            for (int i = 0; i < array.size(); i++) {
 | 
			
		||||
                JsonNode arrayElement = array.get(i);
 | 
			
		||||
                if (arrayElement.isObject() || arrayElement.isArray()) {
 | 
			
		||||
                    replaceUuidsRecursively(arrayElement, skipFieldsSet, includedFieldsPattern, replacer);
 | 
			
		||||
                    replaceUuidsRecursively(arrayElement, skippedRootFields, includedFieldsPattern, replacer, false);
 | 
			
		||||
                } else if (arrayElement.isTextual()) {
 | 
			
		||||
                    String text = arrayElement.asText();
 | 
			
		||||
                    String newText = RegexUtils.replace(text, RegexUtils.UUID_PATTERN, uuid -> replacer.apply(UUID.fromString(uuid)).toString());
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user