Merge pull request #9233 from thingsboard/fix/vc
Version control: fix ids replacement in dashboard config
This commit is contained in:
commit
5583446340
@ -48,8 +48,8 @@ public abstract class BaseEntityExportService<I extends EntityId, E extends Expo
|
|||||||
|
|
||||||
public abstract Set<EntityType> getSupportedEntityTypes();
|
public abstract Set<EntityType> getSupportedEntityTypes();
|
||||||
|
|
||||||
protected void replaceUuidsRecursively(EntitiesExportCtx<?> ctx, JsonNode node, Set<String> skipFieldsSet, Pattern includedFieldsPattern) {
|
protected void replaceUuidsRecursively(EntitiesExportCtx<?> ctx, JsonNode node, Set<String> skippedRootFields, Pattern includedFieldsPattern) {
|
||||||
JacksonUtil.replaceUuidsRecursively(node, skipFieldsSet, includedFieldsPattern, uuid -> getExternalIdOrElseInternalByUuid(ctx, uuid));
|
JacksonUtil.replaceUuidsRecursively(node, skippedRootFields, includedFieldsPattern, uuid -> getExternalIdOrElseInternalByUuid(ctx, uuid), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Stream<UUID> toExternalIds(Collection<UUID> internalIds, Function<UUID, EntityId> entityIdCreator,
|
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.queue.util.TbCoreComponent;
|
||||||
import org.thingsboard.server.service.sync.vc.data.EntitiesExportCtx;
|
import org.thingsboard.server.service.sync.vc.data.EntitiesExportCtx;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.thingsboard.server.service.sync.ie.importing.impl.DashboardImportService.WIDGET_CONFIG_PROCESSED_FIELDS_PATTERN;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@TbCoreComponent
|
@TbCoreComponent
|
||||||
public class DashboardExportService extends BaseEntityExportService<DashboardId, Dashboard, EntityExportData<Dashboard>> {
|
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);
|
replaceUuidsRecursively(ctx, entityAlias, Set.of("id"), null);
|
||||||
}
|
}
|
||||||
for (JsonNode widgetConfig : dashboard.getWidgetsConfig()) {
|
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,
|
protected void replaceIdsRecursively(EntitiesImportCtx ctx, IdProvider idProvider, JsonNode json,
|
||||||
Set<String> skipFieldsSet, Pattern includedFieldsPattern,
|
Set<String> skippedRootFields, Pattern includedFieldsPattern,
|
||||||
LinkedHashSet<EntityType> hints) {
|
LinkedHashSet<EntityType> hints) {
|
||||||
JacksonUtil.replaceUuidsRecursively(json, skipFieldsSet, includedFieldsPattern,
|
JacksonUtil.replaceUuidsRecursively(json, skippedRootFields, includedFieldsPattern,
|
||||||
uuid -> idProvider.getInternalIdByUuid(uuid, ctx.isFinalImportAttempt(), hints)
|
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.LinkedHashSet;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@ -44,6 +45,7 @@ import java.util.stream.Collectors;
|
|||||||
public class DashboardImportService extends BaseEntityImportService<DashboardId, Dashboard, EntityExportData<Dashboard>> {
|
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));
|
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;
|
private final DashboardService dashboardService;
|
||||||
|
|
||||||
@ -68,7 +70,7 @@ public class DashboardImportService extends BaseEntityImportService<DashboardId,
|
|||||||
replaceIdsRecursively(ctx, idProvider, entityAlias, Set.of("id"), null, HINTS);
|
replaceIdsRecursively(ctx, idProvider, entityAlias, Set.of("id"), null, HINTS);
|
||||||
}
|
}
|
||||||
for (JsonNode widgetConfig : dashboard.getWidgetsConfig()) {
|
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;
|
return dashboard;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -257,6 +257,7 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest {
|
|||||||
Asset asset1 = createAsset(tenantId1, null, assetProfile.getId(), "Asset 1");
|
Asset asset1 = createAsset(tenantId1, null, assetProfile.getId(), "Asset 1");
|
||||||
Asset asset2 = createAsset(tenantId1, null, assetProfile.getId(), "Asset 2");
|
Asset asset2 = createAsset(tenantId1, null, assetProfile.getId(), "Asset 2");
|
||||||
Dashboard dashboard = createDashboard(tenantId1, null, "Dashboard 1");
|
Dashboard dashboard = createDashboard(tenantId1, null, "Dashboard 1");
|
||||||
|
Dashboard otherDashboard = createDashboard(tenantId1, null, "Dashboard 2");
|
||||||
DeviceProfile existingDeviceProfile = createDeviceProfile(tenantId2, null, null, "Existing");
|
DeviceProfile existingDeviceProfile = createDeviceProfile(tenantId2, null, null, "Existing");
|
||||||
|
|
||||||
String aliasId = "23c4185d-1497-9457-30b2-6d91e69a5b2c";
|
String aliasId = "23c4185d-1497-9457-30b2-6d91e69a5b2c";
|
||||||
@ -266,19 +267,42 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest {
|
|||||||
"\"alias\": \"assets\",\n" +
|
"\"alias\": \"assets\",\n" +
|
||||||
"\"filter\": {\n" +
|
"\"filter\": {\n" +
|
||||||
" \"entityList\": [\n" +
|
" \"entityList\": [\n" +
|
||||||
"\"" + asset1.getId().toString() + "\",\n" +
|
" \"" + asset1.getId() + "\",\n" +
|
||||||
"\"" + asset2.getId().toString() + "\",\n" +
|
" \"" + asset2.getId() + "\",\n" +
|
||||||
"\"" + tenantId1.getId().toString() + "\",\n" +
|
" \"" + tenantId1.getId() + "\",\n" +
|
||||||
"\"" + existingDeviceProfile.getId().toString() + "\",\n" +
|
" \"" + existingDeviceProfile.getId() + "\",\n" +
|
||||||
" \"" + unknownUuid + "\"\n" +
|
" \"" + unknownUuid + "\"\n" +
|
||||||
" ],\n" +
|
" ],\n" +
|
||||||
|
" \"id\":\"" + asset1.getId() + "\",\n" +
|
||||||
" \"resolveMultiple\": true\n" +
|
" \"resolveMultiple\": true\n" +
|
||||||
"},\n" +
|
"},\n" +
|
||||||
"\"id\": \"" + aliasId + "\"\n" +
|
"\"id\": \"" + aliasId + "\"\n" +
|
||||||
"}\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();
|
ObjectNode dashboardConfiguration = JacksonUtil.newObjectNode();
|
||||||
dashboardConfiguration.set("entityAliases", JacksonUtil.toJsonNode(entityAliases));
|
dashboardConfiguration.set("entityAliases", JacksonUtil.toJsonNode(entityAliases));
|
||||||
|
dashboardConfiguration.set("widgets", JacksonUtil.toJsonNode(widgets));
|
||||||
dashboardConfiguration.set("description", new TextNode("hallo"));
|
dashboardConfiguration.set("description", new TextNode("hallo"));
|
||||||
dashboard.setConfiguration(dashboardConfiguration);
|
dashboard.setConfiguration(dashboardConfiguration);
|
||||||
dashboard = dashboardService.saveDashboard(dashboard);
|
dashboard = dashboardService.saveDashboard(dashboard);
|
||||||
@ -288,10 +312,12 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest {
|
|||||||
EntityExportData<Asset> asset1ExportData = exportEntity(tenantAdmin1, asset1.getId());
|
EntityExportData<Asset> asset1ExportData = exportEntity(tenantAdmin1, asset1.getId());
|
||||||
EntityExportData<Asset> asset2ExportData = exportEntity(tenantAdmin1, asset2.getId());
|
EntityExportData<Asset> asset2ExportData = exportEntity(tenantAdmin1, asset2.getId());
|
||||||
EntityExportData<Dashboard> dashboardExportData = exportEntity(tenantAdmin1, dashboard.getId());
|
EntityExportData<Dashboard> dashboardExportData = exportEntity(tenantAdmin1, dashboard.getId());
|
||||||
|
EntityExportData<Dashboard> otherDashboardExportData = exportEntity(tenantAdmin1, otherDashboard.getId());
|
||||||
|
|
||||||
AssetProfile importedProfile = importEntity(tenantAdmin2, profileExportData).getSavedEntity();
|
AssetProfile importedProfile = importEntity(tenantAdmin2, profileExportData).getSavedEntity();
|
||||||
Asset importedAsset1 = importEntity(tenantAdmin2, asset1ExportData).getSavedEntity();
|
Asset importedAsset1 = importEntity(tenantAdmin2, asset1ExportData).getSavedEntity();
|
||||||
Asset importedAsset2 = importEntity(tenantAdmin2, asset2ExportData).getSavedEntity();
|
Asset importedAsset2 = importEntity(tenantAdmin2, asset2ExportData).getSavedEntity();
|
||||||
|
Dashboard importedOtherDashboard = importEntity(tenantAdmin2, otherDashboardExportData).getSavedEntity();
|
||||||
Dashboard importedDashboard = importEntity(tenantAdmin2, dashboardExportData).getSavedEntity();
|
Dashboard importedDashboard = importEntity(tenantAdmin2, dashboardExportData).getSavedEntity();
|
||||||
|
|
||||||
Map.Entry<String, JsonNode> entityAlias = importedDashboard.getConfiguration().get("entityAliases").fields().next();
|
Map.Entry<String, JsonNode> entityAlias = importedDashboard.getConfiguration().get("entityAliases").fields().next();
|
||||||
@ -311,6 +337,17 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest {
|
|||||||
.isEqualTo(existingDeviceProfile.getId().toString());
|
.isEqualTo(existingDeviceProfile.getId().toString());
|
||||||
assertThat(aliasEntitiesIds).element(4).as("unresolved uuid was replaced with tenant id")
|
assertThat(aliasEntitiesIds).element(4).as("unresolved uuid was replaced with tenant id")
|
||||||
.isEqualTo(tenantId2.toString());
|
.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.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
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.DataType;
|
||||||
import org.thingsboard.server.common.data.kv.KvEntry;
|
import org.thingsboard.server.common.data.kv.KvEntry;
|
||||||
|
|
||||||
@ -35,7 +36,6 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -228,27 +228,24 @@ public class JacksonUtil {
|
|||||||
return node;
|
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) {
|
if (node == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (node.isObject()) {
|
if (node.isObject()) {
|
||||||
ObjectNode objectNode = (ObjectNode) node;
|
ObjectNode objectNode = (ObjectNode) node;
|
||||||
List<String> fieldNames = new ArrayList<>(objectNode.size());
|
List<String> fieldNames = Lists.newArrayList(objectNode.fieldNames());
|
||||||
objectNode.fieldNames().forEachRemaining(fieldNames::add);
|
|
||||||
for (String fieldName : fieldNames) {
|
for (String fieldName : fieldNames) {
|
||||||
if (skipFieldsSet.contains(fieldName)) {
|
if (root && skippedRootFields.contains(fieldName)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (includedFieldsPattern != null) {
|
|
||||||
if (!RegexUtils.matches(fieldName, includedFieldsPattern)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var child = objectNode.get(fieldName);
|
var child = objectNode.get(fieldName);
|
||||||
if (child.isObject() || child.isArray()) {
|
if (child.isObject() || child.isArray()) {
|
||||||
replaceUuidsRecursively(child, skipFieldsSet, includedFieldsPattern, replacer);
|
replaceUuidsRecursively(child, skippedRootFields, includedFieldsPattern, replacer, false);
|
||||||
} else if (child.isTextual()) {
|
} else if (child.isTextual()) {
|
||||||
|
if (includedFieldsPattern != null && !RegexUtils.matches(fieldName, includedFieldsPattern)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
String text = child.asText();
|
String text = child.asText();
|
||||||
String newText = RegexUtils.replace(text, RegexUtils.UUID_PATTERN, uuid -> replacer.apply(UUID.fromString(uuid)).toString());
|
String newText = RegexUtils.replace(text, RegexUtils.UUID_PATTERN, uuid -> replacer.apply(UUID.fromString(uuid)).toString());
|
||||||
if (!text.equals(newText)) {
|
if (!text.equals(newText)) {
|
||||||
@ -261,7 +258,7 @@ public class JacksonUtil {
|
|||||||
for (int i = 0; i < array.size(); i++) {
|
for (int i = 0; i < array.size(); i++) {
|
||||||
JsonNode arrayElement = array.get(i);
|
JsonNode arrayElement = array.get(i);
|
||||||
if (arrayElement.isObject() || arrayElement.isArray()) {
|
if (arrayElement.isObject() || arrayElement.isArray()) {
|
||||||
replaceUuidsRecursively(arrayElement, skipFieldsSet, includedFieldsPattern, replacer);
|
replaceUuidsRecursively(arrayElement, skippedRootFields, includedFieldsPattern, replacer, false);
|
||||||
} else if (arrayElement.isTextual()) {
|
} else if (arrayElement.isTextual()) {
|
||||||
String text = arrayElement.asText();
|
String text = arrayElement.asText();
|
||||||
String newText = RegexUtils.replace(text, RegexUtils.UUID_PATTERN, uuid -> replacer.apply(UUID.fromString(uuid)).toString());
|
String newText = RegexUtils.replace(text, RegexUtils.UUID_PATTERN, uuid -> replacer.apply(UUID.fromString(uuid)).toString());
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user