@ -57,6 +57,7 @@ import org.thingsboard.server.dao.service.Validator;
import org.thingsboard.server.dao.service.validator.ResourceDataValidator ;
import java.nio.charset.StandardCharsets ;
import java.util.ArrayList ;
import java.util.Base64 ;
import java.util.Collection ;
import java.util.Collections ;
@ -401,18 +402,33 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
}
@Override
public boolean updateResourcesUsage ( Dashboard dashboard ) {
public boolean updateResourcesUsage ( TenantId tenantId , Dashboard dashboard ) {
if ( dashboard . getConfiguration ( ) = = null ) {
return false ;
}
Map < String , String > links = getResourcesLinks ( dashboard . getResources ( ) ) ;
return updateResourcesUsage ( dashboard . getTenantId ( ) , dashboard . getConfiguration ( ) , DASHBOARD_RESOURCES_MAPPING , links ) ;
return updateResourcesUsage ( tenantId , List . of ( dashboard . getConfiguration ( ) ) , List . of ( DASHBOARD_RESOURCES_MAPPING ) , links ) ;
}
@Override
public boolean updateResourcesUsage ( WidgetTypeDetails widgetTypeDetails ) {
public boolean updateResourcesUsage ( TenantId tenantId , WidgetTypeDetails widgetTypeDetails ) {
Map < String , String > links = getResourcesLinks ( widgetTypeDetails . getResources ( ) ) ;
boolean updated = updateResourcesUsage ( widgetTypeDetails . getTenantId ( ) , widgetTypeDetails . getDescriptor ( ) , WIDGET_RESOURCES_MAPPING , links ) ;
List < JsonNode > jsonNodes = new ArrayList < > ( 2 ) ;
List < Map < String , String > > mappings = new ArrayList < > ( 2 ) ;
if ( widgetTypeDetails . getDescriptor ( ) ! = null ) {
jsonNodes . add ( widgetTypeDetails . getDescriptor ( ) ) ;
mappings . add ( WIDGET_RESOURCES_MAPPING ) ;
}
JsonNode defaultConfig = widgetTypeDetails . getDefaultConfig ( ) ;
if ( defaultConfig ! = null ) {
updated | = updateResourcesUsage ( widgetTypeDetails . getTenantId ( ) , defaultConfig , WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING , links ) ;
jsonNodes . add ( defaultConfig ) ;
mappings . add ( WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING ) ;
}
boolean updated = updateResourcesUsage ( tenantId , jsonNodes , mappings , links ) ;
if ( defaultConfig ! = null ) {
widgetTypeDetails . setDefaultConfig ( defaultConfig ) ;
}
return updated ;
@ -433,8 +449,9 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
return links ;
}
private boolean updateResourcesUsage ( TenantId tenantId , JsonNode jsonNode , Map < String , String > mapping , Map < String , String > links ) {
return processResources ( jsonNode , mapping , value - > {
private boolean updateResourcesUsage ( TenantId tenantId , List < JsonNode > jsonNodes , List < Map < String , String > > mappings , Map < String , String > links ) {
log . trace ( "[{}] updateResourcesUsage (new links: {}) for {}" , tenantId , links , jsonNodes ) ;
return processResources ( jsonNodes , mappings , value - > {
String link = getResourceLink ( value ) ;
if ( link ! = null ) {
String newLink = links . get ( link ) ;
@ -462,23 +479,31 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
}
@Override
public Collection < TbResourceInfo > getUsedResources ( Dashboard dashboard ) {
return getUsedResources ( dashboard . getTenantId ( ) , dashboard . getConfiguration ( ) , DASHBOARD_RESOURCES_MAPPING ) . values ( ) ;
public Collection < TbResourceInfo > getUsedResources ( TenantId tenantId , Dashboard dashboard ) {
return getUsedResources ( tenantId , List . of ( dashboard . getConfiguration ( ) ) , List . of ( DASHBOARD_RESOURCES_MAPPING ) ) . values ( ) ;
}
@Override
public Collection < TbResourceInfo > getUsedResources ( WidgetTypeDetails widgetTypeDetails ) {
Map < TbResourceId , TbResourceInfo > resources = getUsedResources ( widgetTypeDetails . getTenantId ( ) , widgetTypeDetails . getDescriptor ( ) , WIDGET_RESOURCES_MAPPING ) ;
public Collection < TbResourceInfo > getUsedResources ( TenantId tenantId , WidgetTypeDetails widgetTypeDetails ) {
List < JsonNode > jsonNodes = new ArrayList < > ( 2 ) ;
List < Map < String , String > > mappings = new ArrayList < > ( 2 ) ;
jsonNodes . add ( widgetTypeDetails . getDescriptor ( ) ) ;
mappings . add ( WIDGET_RESOURCES_MAPPING ) ;
JsonNode defaultConfig = widgetTypeDetails . getDefaultConfig ( ) ;
if ( defaultConfig ! = null ) {
resources . putAll ( getUsedResources ( widgetTypeDetails . getTenantId ( ) , defaultConfig , WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING ) ) ;
jsonNodes . add ( defaultConfig ) ;
mappings . add ( WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING ) ;
}
return resources . values ( ) ;
return getUsedResources ( tenantId , jsonNodes , mappings ) . values ( ) ;
}
private Map < TbResourceId , TbResourceInfo > getUsedResources ( TenantId tenantId , JsonNode jsonNode , Map < String , String > mapping ) {
private Map < TbResourceId , TbResourceInfo > getUsedResources ( TenantId tenantId , List < JsonNode > jsonNodes , List < Map < String , String > > mappings ) {
Map < TbResourceId , TbResourceInfo > resources = new HashMap < > ( ) ;
processResources ( jsonNode , mapping , value - > {
log . trace ( "[{}] getUsedResources for {}" , tenantId , jsonNodes ) ;
processResources ( jsonNodes , mappings , value - > {
String link = getResourceLink ( value ) ;
if ( link = = null ) {
return value ;
@ -517,32 +542,57 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
}
}
private boolean processResources ( JsonNode jsonNode , Map < String , String > mapping , UnaryOperator < String > processor ) {
private boolean processResources ( List < JsonNode > jsonNodes , List < Map < String , String > > mappings , UnaryOperator < String > processor ) {
AtomicBoolean updated = new AtomicBoolean ( false ) ;
JacksonUtil . replaceByMapping ( jsonNode , mapping , Collections . emptyMap ( ) , ( name , urlNode ) - > {
String value = null ;
if ( urlNode . isTextual ( ) ) { // link is in the right place
value = urlNode . asText ( ) ;
} else {
JsonNode id = urlNode . get ( "id" ) ; // old structure is used
if ( id ! = null & & id . isTextual ( ) ) {
value = id . asText ( ) ;
}
}
if ( StringUtils . isNotBlank ( value ) ) {
value = processor . apply ( value ) ;
} else {
value = "" ;
}
for ( int i = 0 ; i < jsonNodes . size ( ) ; i + + ) {
JsonNode jsonNode = jsonNodes . get ( i ) ;
// processing by mappings first
if ( i < = mappings . size ( ) - 1 ) {
JacksonUtil . replaceByMapping ( jsonNode , mappings . get ( i ) , Collections . emptyMap ( ) , ( name , urlNode ) - > {
String value = null ;
if ( urlNode . isTextual ( ) ) { // link is in the right place
value = urlNode . asText ( ) ;
} else {
JsonNode id = urlNode . get ( "id" ) ; // old structure is used
if ( id ! = null & & id . isTextual ( ) ) {
value = id . asText ( ) ;
}
}
JsonNode newValue = new TextNode ( value ) ;
if ( ! newValue . toString ( ) . equals ( urlNode . toString ( ) ) ) {
updated . set ( true ) ;
log . trace ( "Replaced '{}' with '{}'" , urlNode , newValue ) ;
if ( StringUtils . isNotBlank ( value ) ) {
value = processor . apply ( value ) ;
} else {
value = "" ;
}
JsonNode newValue = new TextNode ( value ) ;
if ( ! newValue . toString ( ) . equals ( urlNode . toString ( ) ) ) {
updated . set ( true ) ;
log . trace ( "Replaced by mapping '{}' ({}) with '{}'" , value , name , newValue ) ;
}
return newValue ;
} ) ;
}
return newValue ;
} ) ;
// processing all
JacksonUtil . replaceAll ( jsonNode , "" , ( name , value ) - > {
if ( ! StringUtils . startsWith ( value , DataConstants . TB_RESOURCE_PREFIX + "/api/resource/" ) ) {
return value ;
}
String newValue = processor . apply ( value ) ;
if ( StringUtils . equals ( value , newValue ) ) {
return value ;
} else {
updated . set ( true ) ;
log . trace ( "Replaced '{}' ({}) with '{}'" , value , name , newValue ) ;
return newValue ;
}
} ) ;
}
return updated . get ( ) ;
}
@ -555,7 +605,7 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
importResources ( dashboard . getTenantId ( ) , dashboard . getResources ( ) ) ;
}
imageService . updateImagesUsage ( dashboard ) ;
updateResourcesUsage ( dashboard ) ;
updateResourcesUsage ( dashboard . getTenantId ( ) , dashboard ) ;
data = JacksonUtil . writeValueAsBytes ( dashboard ) ;
}