diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index e79bb9c0ae..04ea69d419 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -39,6 +39,8 @@ import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityViewInfo; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantInfo; import org.thingsboard.server.common.data.TenantProfile; @@ -59,6 +61,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; @@ -77,7 +80,6 @@ import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleNode; -import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.common.msg.TbMsg; @@ -99,6 +101,7 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.resource.TbResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantProfileService; @@ -226,6 +229,9 @@ public abstract class BaseController { @Autowired protected PartitionService partitionService; + @Autowired + protected TbResourceService resourceService; + @Autowired protected TbQueueProducerProvider producerProvider; @@ -461,6 +467,9 @@ public abstract class BaseController { case WIDGET_TYPE: checkWidgetTypeId(new WidgetTypeId(entityId.getId()), operation); return; + case TB_RESOURCE: + checkResourceId(new TbResourceId(entityId.getId()), operation); + return; default: throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType()); } @@ -668,6 +677,30 @@ public abstract class BaseController { return ruleNode; } + TbResource checkResourceId(TbResourceId resourceId, Operation operation) throws ThingsboardException { + try { + validateId(resourceId, "Incorrect resourceId " + resourceId); + TbResource resource = resourceService.findResourceById(getCurrentUser().getTenantId(), resourceId); + checkNotNull(resource); + accessControlService.checkPermission(getCurrentUser(), Resource.TB_RESOURCE, operation, resourceId, resource); + return resource; + } catch (Exception e) { + throw handleException(e, false); + } + } + + TbResourceInfo checkResourceInfoId(TbResourceId resourceId, Operation operation) throws ThingsboardException { + try { + validateId(resourceId, "Incorrect resourceId " + resourceId); + TbResourceInfo resourceInfo = resourceService.findResourceInfoById(getCurrentUser().getTenantId(), resourceId); + checkNotNull(resourceInfo); + accessControlService.checkPermission(getCurrentUser(), Resource.TB_RESOURCE, operation, resourceId, resourceInfo); + return resourceInfo; + } catch (Exception e) { + throw handleException(e, false); + } + } + @SuppressWarnings("unchecked") protected I emptyId(EntityType entityType) { return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); @@ -884,8 +917,8 @@ public abstract class BaseController { protected void processDashboardIdFromAdditionalInfo(ObjectNode additionalInfo, String requiredFields) throws ThingsboardException { String dashboardId = additionalInfo.has(requiredFields) ? additionalInfo.get(requiredFields).asText() : null; - if(dashboardId != null && !dashboardId.equals("null")) { - if(dashboardService.findDashboardById(getTenantId(), new DashboardId(UUID.fromString(dashboardId))) == null) { + if (dashboardId != null && !dashboardId.equals("null")) { + if (dashboardService.findDashboardById(getTenantId(), new DashboardId(UUID.fromString(dashboardId))) == null) { additionalInfo.remove(requiredFields); } } diff --git a/application/src/main/java/org/thingsboard/server/controller/ResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java similarity index 51% rename from application/src/main/java/org/thingsboard/server/controller/ResourceController.java rename to application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index 1d4a31c1c2..4336ef85fe 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -18,20 +18,23 @@ package org.thingsboard.server.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -import org.thingsboard.server.common.data.Resource; -import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.resource.ResourceService; +import org.thingsboard.server.dao.resource.TbResourceService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.security.permission.Resource; import java.util.List; @@ -39,21 +42,50 @@ import java.util.List; @RestController @TbCoreComponent @RequestMapping("/api") -public class ResourceController extends BaseController { +public class TbResourceController extends BaseController { - private final ResourceService resourceService; + public static final String RESOURCE_ID = "resourceId"; - public ResourceController(ResourceService resourceService) { + private final TbResourceService resourceService; + + public TbResourceController(TbResourceService resourceService) { this.resourceService = resourceService; } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/resource/info/{resourceId}", method = RequestMethod.GET) + @ResponseBody + public TbResourceInfo getResourceInfoById(@PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { + checkParameter(RESOURCE_ID, strResourceId); + try { + TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); + return checkResourceInfoId(resourceId, Operation.READ); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/resource/{resourceId}", method = RequestMethod.GET) + @ResponseBody + public TbResource getResourceById(@PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { + checkParameter(RESOURCE_ID, strResourceId); + try { + TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); + return checkResourceId(resourceId, Operation.READ); + } catch (Exception e) { + throw handleException(e); + } + } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/resource", method = RequestMethod.POST) @ResponseBody - public Resource saveResource(Resource resource) throws ThingsboardException { + public TbResource saveResource(@RequestBody TbResource resource) throws ThingsboardException { try { resource.setTenantId(getTenantId()); - Resource savedResource = checkNotNull(resourceService.saveResource(resource)); + checkEntity(resource.getId(), resource, Resource.TB_RESOURCE); + TbResource savedResource = checkNotNull(resourceService.saveResource(resource)); tbClusterService.onResourceChange(savedResource, null); return savedResource; } catch (Exception e) { @@ -64,15 +96,14 @@ public class ResourceController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/resource", method = RequestMethod.GET) @ResponseBody - public PageData getResources(@RequestParam(required = false) boolean system, - @RequestParam int pageSize, - @RequestParam int page, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String sortProperty, - @RequestParam(required = false) String sortOrder) throws ThingsboardException { + public PageData getResources(@RequestParam int pageSize, + @RequestParam int page, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String sortProperty, + @RequestParam(required = false) String sortOrder) throws ThingsboardException { try { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(resourceService.findResourcesByTenantId(system ? TenantId.SYS_TENANT_ID : getTenantId(), pageLink)); + return checkNotNull(resourceService.findResourcesByTenantId(getTenantId(), pageLink)); } catch (Exception e) { throw handleException(e); } @@ -82,10 +113,10 @@ public class ResourceController extends BaseController { @RequestMapping(value = "/resource/lwm2m/page", method = RequestMethod.GET) @ResponseBody public List getLwm2mListObjectsPage(@RequestParam int pageSize, - @RequestParam int page, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String sortProperty, - @RequestParam(required = false) String sortOrder) throws ThingsboardException { + @RequestParam int page, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String sortProperty, + @RequestParam(required = false) String sortOrder) throws ThingsboardException { try { PageLink pageLink = new PageLink(pageSize, page, textSearch); return checkNotNull(resourceService.findLwM2mObjectPage(getTenantId(), sortProperty, sortOrder, pageLink)); @@ -95,7 +126,7 @@ public class ResourceController extends BaseController { } @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/resource/lwm2m", method = RequestMethod.GET) + @RequestMapping(value = "/resource/lwm2m", method = RequestMethod.GET) @ResponseBody public List getLwm2mListObjects(@RequestParam String sortOrder, @RequestParam String sortProperty, @@ -108,14 +139,15 @@ public class ResourceController extends BaseController { } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource/{resourceType}/{resourceId}", method = RequestMethod.DELETE) + @RequestMapping(value = "/resource/{resourceId}", method = RequestMethod.DELETE) @ResponseBody - public void deleteResource(@PathVariable("resourceType") ResourceType resourceType, - @PathVariable("resourceId") String resourceId) throws ThingsboardException { + public void deleteResource(@PathVariable("resourceId") String strResourceId) throws ThingsboardException { + checkParameter("resourceId", strResourceId); try { - Resource resource = checkNotNull(resourceService.getResource(getTenantId(), resourceType, resourceId)); - resourceService.deleteResource(getTenantId(), resourceType, resourceId); - tbClusterService.onResourceDeleted(resource, null); + TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); + TbResource tbResource = checkResourceId(resourceId, Operation.DELETE); + resourceService.deleteResource(getTenantId(), resourceId); + tbClusterService.onResourceDeleted(tbResource, null); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java index 3a59397f12..64f4847abf 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java +++ b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java @@ -22,8 +22,8 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.Dashboard; -import org.thingsboard.server.common.data.Resource; import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -35,7 +35,7 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; -import org.thingsboard.server.dao.resource.ResourceService; +import org.thingsboard.server.dao.resource.TbResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; @@ -95,7 +95,7 @@ public class InstallScripts { private OAuth2ConfigTemplateService oAuth2TemplateService; @Autowired - private ResourceService resourceService; + private TbResourceService resourceService; public Path getTenantRuleChainsDir() { return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR); @@ -204,13 +204,10 @@ public class InstallScripts { path -> { try { byte[] fileBytes = Files.readAllBytes(path); - String source = new String(fileBytes); - Resource resource = new Resource(); + TbResource resource = new TbResource(); resource.setTenantId(TenantId.SYS_TENANT_ID); resource.setResourceType(ResourceType.LWM2M_MODEL); - resource.setResourceId(getValueByTag(source, "ObjectID") + "_" + getValueByTag(source, "ObjectVersion")); - resource.setTextSearch(resource.getResourceId() + ":" + getValueByTag(source, "Name")); - resource.setValue(Base64.getEncoder().encodeToString(fileBytes)); + resource.setData(Base64.getEncoder().encodeToString(fileBytes)); resourceService.saveResource(resource); } catch (Exception e) { throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", path.toString())); @@ -219,28 +216,6 @@ public class InstallScripts { ); } } - - Path jksPath = Paths.get(getDataDir(), CREDENTIALS_DIR, "serverKeyStore.jks"); - try { - Resource resource = new Resource(); - resource.setTenantId(TenantId.SYS_TENANT_ID); - resource.setResourceType(ResourceType.JKS); - resource.setResourceId(jksPath.getFileName().toString()); - resource.setTextSearch(jksPath.getFileName().toString()); - resource.setValue(Base64.getEncoder().encodeToString(Files.readAllBytes(jksPath))); - resourceService.saveResource(resource); - } catch (Exception e) { - log.error("Unable to load lwm2m serverKeyStore [{}]", jksPath.toString()); - throw new RuntimeException("Unable to load lwm2m serverKeyStore", e); - } - } - - private String getValueByTag(String source, String tag) { - int lenTag = ("<" + tag + ">").length(); - int indStart = source.indexOf("<" + tag + ">"); - int indEnd = source.indexOf(""); - return (indStart > 0 && indEnd > 0) ? source.substring(indStart + lenTag, indEnd) : null; - } public void loadDashboards(TenantId tenantId, CustomerId customerId) throws Exception { diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index f6e2c76cbe..cc6a335a66 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -450,14 +450,17 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { log.info("Updating schema ..."); try { - conn.createStatement().execute("CREATE TABLE IF NOT EXISTS resource (" + - " tenant_id uuid NOT NULL," + - " resource_type varchar(32) NOT NULL," + - " resource_id varchar(255) NOT NULL," + - " text_search varchar(255)," + - " resource_value varchar," + - " CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id)" + - " );"); + conn.createStatement().execute("CREATE TABLE IF NOT EXISTS resource ( " + + "id uuid NOT NULL CONSTRAINT resource_pkey PRIMARY KEY, " + + "created_time bigint NOT NULL, " + + "tenant_id uuid NOT NULL, " + + "title varchar(255) NOT NULL, " + + "resource_type varchar(32) NOT NULL, " + + "resource_key varchar(255) NOT NULL, " + + "search_text varchar(255), " + + "data varchar, " + + "CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key)" + + ");"); conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3003000;"); installScripts.loadSystemLwm2mResources(); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java index 16938b7fbf..1b684863de 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java @@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasName; -import org.thingsboard.server.common.data.Resource; +import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.DeviceId; @@ -249,28 +249,27 @@ public class DefaultTbClusterService implements TbClusterService { } @Override - public void onResourceChange(Resource resource, TbQueueCallback callback) { + public void onResourceChange(TbResource resource, TbQueueCallback callback) { TenantId tenantId = resource.getTenantId(); - log.trace("[{}][{}][{}] Processing change resource", tenantId, resource.getResourceType(), resource.getResourceId()); + log.trace("[{}][{}][{}] Processing change resource", tenantId, resource.getResourceType(), resource.getResourceKey()); TransportProtos.ResourceUpdateMsg resourceUpdateMsg = TransportProtos.ResourceUpdateMsg.newBuilder() .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) .setResourceType(resource.getResourceType().name()) - .setResourceId(resource.getResourceId()) + .setResourceKey(resource.getResourceKey()) .build(); ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setResourceUpdateMsg(resourceUpdateMsg).build(); broadcast(transportMsg, callback); } @Override - public void onResourceDeleted(Resource resource, TbQueueCallback callback) { - TenantId tenantId = resource.getTenantId(); - log.trace("[{}][{}][{}] Processing delete resource", tenantId, resource.getResourceType(), resource.getResourceId()); + public void onResourceDeleted(TbResource resource, TbQueueCallback callback) { + log.trace("[{}] Processing delete resource", resource); TransportProtos.ResourceDeleteMsg resourceUpdateMsg = TransportProtos.ResourceDeleteMsg.newBuilder() - .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) - .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) + .setTenantIdMSB(resource.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(resource.getTenantId().getId().getLeastSignificantBits()) .setResourceType(resource.getResourceType().name()) - .setResourceId(resource.getResourceId()) + .setResourceKey(resource.getResourceKey()) .build(); ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setResourceDeleteMsg(resourceUpdateMsg).build(); broadcast(transportMsg, callback); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/TbClusterService.java index a2391a73e4..bf2d6691e5 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/TbClusterService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/TbClusterService.java @@ -19,7 +19,7 @@ import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.Resource; +import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.EntityId; @@ -73,7 +73,7 @@ public interface TbClusterService { void onDeviceDeleted(Device device, TbQueueCallback callback); - void onResourceChange(Resource resource, TbQueueCallback callback); + void onResourceChange(TbResource resource, TbQueueCallback callback); - void onResourceDeleted(Resource resource, TbQueueCallback callback); + void onResourceDeleted(TbResource resource, TbQueueCallback callback); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index 75ece6226e..ba53ceea98 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -36,7 +36,8 @@ public enum Resource { OAUTH2_CONFIGURATION_TEMPLATE(), TENANT_PROFILE(EntityType.TENANT_PROFILE), DEVICE_PROFILE(EntityType.DEVICE_PROFILE), - API_USAGE_STATE(EntityType.API_USAGE_STATE); + API_USAGE_STATE(EntityType.API_USAGE_STATE), + TB_RESOURCE(EntityType.TB_RESOURCE); private final EntityType entityType; diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java index c00bf2ea6c..f703102e18 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java @@ -38,6 +38,7 @@ public class SysAdminPermissions extends AbstractPermissions { put(Resource.OAUTH2_CONFIGURATION_INFO, PermissionChecker.allowAllPermissionChecker); put(Resource.OAUTH2_CONFIGURATION_TEMPLATE, PermissionChecker.allowAllPermissionChecker); put(Resource.TENANT_PROFILE, PermissionChecker.allowAllPermissionChecker); + put(Resource.TB_RESOURCE, systemEntityPermissionChecker); } private static final PermissionChecker systemEntityPermissionChecker = new PermissionChecker() { diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java index f56e1e0554..c36a70821f 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java @@ -41,6 +41,7 @@ public class TenantAdminPermissions extends AbstractPermissions { put(Resource.WIDGET_TYPE, widgetsPermissionChecker); put(Resource.DEVICE_PROFILE, tenantEntityPermissionChecker); put(Resource.API_USAGE_STATE, tenantEntityPermissionChecker); + put(Resource.TB_RESOURCE, tbResourcePermissionChecker); } public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() { @@ -101,4 +102,19 @@ public class TenantAdminPermissions extends AbstractPermissions { } }; + + private static final PermissionChecker tbResourcePermissionChecker = new PermissionChecker() { + + @Override + public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { + if (entity.getTenantId() == null || entity.getTenantId().isNullUid()) { + return operation == Operation.READ; + } + if (!user.getTenantId().equals(entity.getTenantId())) { + return false; + } + return true; + } + + }; } diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index 9110bf07ba..9613d708d2 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -31,7 +31,7 @@ import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.Resource; +import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; @@ -56,7 +56,7 @@ import org.thingsboard.server.dao.device.provision.ProvisionFailedException; import org.thingsboard.server.dao.device.provision.ProvisionRequest; import org.thingsboard.server.dao.device.provision.ProvisionResponse; import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.dao.resource.ResourceService; +import org.thingsboard.server.dao.resource.TbResourceService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; @@ -108,7 +108,7 @@ public class DefaultTransportApiService implements TransportApiService { private final TbClusterService tbClusterService; private final DataDecodingEncodingService dataDecodingEncodingService; private final DeviceProvisionService deviceProvisionService; - private final ResourceService resourceService; + private final TbResourceService resourceService; private final ConcurrentMap deviceCreationLocks = new ConcurrentHashMap<>(); @@ -117,7 +117,7 @@ public class DefaultTransportApiService implements TransportApiService { RelationService relationService, DeviceCredentialsService deviceCredentialsService, DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService, TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService, - DeviceProvisionService deviceProvisionService, ResourceService resourceService) { + DeviceProvisionService deviceProvisionService, TbResourceService resourceService) { this.deviceProfileCache = deviceProfileCache; this.tenantProfileCache = tenantProfileCache; this.apiUsageStateService = apiUsageStateService; @@ -365,12 +365,12 @@ public class DefaultTransportApiService implements TransportApiService { private ListenableFuture handle(GetResourceRequestMsg requestMsg) { TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); ResourceType resourceType = ResourceType.valueOf(requestMsg.getResourceType()); - String resourceId = requestMsg.getResourceId(); + String resourceKey = requestMsg.getResourceKey(); TransportProtos.GetResourceResponseMsg.Builder builder = TransportProtos.GetResourceResponseMsg.newBuilder(); - Resource resource = resourceService.getResource(tenantId, resourceType, resourceId); + TbResource resource = resourceService.getResource(tenantId, resourceType, resourceKey); if (resource == null && !tenantId.equals(TenantId.SYS_TENANT_ID)) { - resource = resourceService.getResource(TenantId.SYS_TENANT_ID, resourceType, resourceId); + resource = resourceService.getResource(TenantId.SYS_TENANT_ID, resourceType, resourceKey); } if (resource != null) { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/TbResourceService.java similarity index 65% rename from common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java rename to common/dao-api/src/main/java/org/thingsboard/server/dao/resource/TbResourceService.java index 9f522d57b3..e6d09c0f4f 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/TbResourceService.java @@ -15,24 +15,30 @@ */ package org.thingsboard.server.dao.resource; -import org.thingsboard.server.common.data.Resource; +import org.eclipse.leshan.core.model.InvalidDDFFileException; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import java.io.IOException; import java.util.List; -public interface ResourceService { - Resource saveResource(Resource resource); +public interface TbResourceService { + TbResource saveResource(TbResource resource) throws InvalidDDFFileException, IOException; - Resource getResource(TenantId tenantId, ResourceType resourceType, String resourceId); + TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceId); - PageData findResourcesByTenantId(TenantId tenantId, PageLink pageLink); + TbResource findResourceById(TenantId tenantId, TbResourceId resourceId); - List findAllByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType); + TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId); + + PageData findResourcesByTenantId(TenantId tenantId, PageLink pageLink); List findLwM2mObject(TenantId tenantId, String sortOrder, @@ -44,7 +50,7 @@ public interface ResourceService { String sortOrder, PageLink pageLink); - void deleteResource(TenantId tenantId, ResourceType resourceType, String resourceId); + void deleteResource(TenantId tenantId, TbResourceId resourceId); void deleteResourcesByTenantId(TenantId tenantId); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index 53677e60e5..f289f802d8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; * @author Andrew Shvayka */ public enum EntityType { - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE; + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE, TB_RESOURCE; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Resource.java b/common/data/src/main/java/org/thingsboard/server/common/data/Resource.java deleted file mode 100644 index 351a450fa2..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Resource.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright © 2016-2021 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.common.data; - -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.id.TenantId; - -import java.io.Serializable; - -@Slf4j -@Data -public class Resource implements HasTenantId, Serializable { - - private static final long serialVersionUID = 7379609705527272306L; - - private TenantId tenantId; - private ResourceType resourceType; - private String resourceId; - private String textSearch; - private String value; - - @Override - public String toString() { - final StringBuilder res = new StringBuilder("Resource{"); - res.append("tenantId=").append(tenantId); - res.append(", resourceType='").append(resourceType).append('\''); - res.append(", resourceId='").append(resourceId).append('\''); - res.append(", textSearch='").append(textSearch).append('\''); - res.append('}'); - return res.toString(); - } -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java new file mode 100644 index 0000000000..57d74d95cf --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java @@ -0,0 +1,69 @@ +/** + * Copyright © 2016-2021 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.common.data; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.id.TbResourceId; + +@Slf4j +@Data +@EqualsAndHashCode(callSuper = true) +public class TbResource extends TbResourceInfo { + + private static final long serialVersionUID = 7379609705527272306L; + + private String data; + + public TbResource() { + super(); + } + + public TbResource(TbResourceId id) { + super(id); + } + + public TbResource(TbResourceInfo resourceInfo) { + super(resourceInfo); + } + + public TbResource(TbResource resource) { + super(resource); + this.data = resource.getData(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Resource [tenantId="); + builder.append(getTenantId()); + builder.append(", id="); + builder.append(getUuidId()); + builder.append(", createdTime="); + builder.append(createdTime); + builder.append(", title="); + builder.append(getTitle()); + builder.append(", resourceType="); + builder.append(getResourceType()); + builder.append(", resourceKey="); + builder.append(getResourceKey()); + builder.append(", data="); + builder.append(data); + builder.append("]"); + return builder.toString(); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java new file mode 100644 index 0000000000..eda6a5ffc9 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java @@ -0,0 +1,74 @@ +/** + * Copyright © 2016-2021 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.common.data; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; + +@Slf4j +@Data +@EqualsAndHashCode(callSuper = true) +public class TbResourceInfo extends SearchTextBased implements HasTenantId { + + private TenantId tenantId; + private String title; + private ResourceType resourceType; + private String resourceKey; + private String searchText; + + public TbResourceInfo() { + super(); + } + + public TbResourceInfo(TbResourceId id) { + super(id); + } + + public TbResourceInfo(TbResourceInfo resourceInfo) { + super(resourceInfo); + this.tenantId = resourceInfo.getTenantId(); + this.title = resourceInfo.getTitle(); + this.resourceType = resourceInfo.getResourceType(); + this.resourceKey = resourceInfo.resourceKey; + } + + @Override + public String getSearchText() { + return searchText; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("ResourceInfo [tenantId="); + builder.append(tenantId); + builder.append(", id="); + builder.append(getUuidId()); + builder.append(", createdTime="); + builder.append(createdTime); + builder.append(", title="); + builder.append(title); + builder.append(", resourceType="); + builder.append(resourceType); + builder.append(", resourceKey="); + builder.append(resourceKey); + builder.append("]"); + return builder.toString(); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index 321d85ac84..8e52a5b2a7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -68,6 +68,8 @@ public class EntityIdFactory { return new TenantProfileId(uuid); case API_USAGE_STATE: return new ApiUsageStateId(uuid); + case TB_RESOURCE: + return new TbResourceId(uuid); } throw new IllegalArgumentException("EntityType " + type + " is not supported!"); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/TbResourceId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/TbResourceId.java new file mode 100644 index 0000000000..566b62db66 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/TbResourceId.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2021 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.common.data.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.EntityType; + +import java.util.UUID; + +public class TbResourceId extends UUIDBased implements EntityId { + + private static final long serialVersionUID = 1L; + + @JsonCreator + public TbResourceId(@JsonProperty("id") UUID id) { + super(id); + } + + @JsonIgnore + @Override + public EntityType getEntityType() { + return EntityType.TB_RESOURCE; + } +} diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto index f3d00b889d..7e9ff076f2 100644 --- a/common/queue/src/main/proto/queue.proto +++ b/common/queue/src/main/proto/queue.proto @@ -205,7 +205,7 @@ message GetResourceRequestMsg { int64 tenantIdMSB = 1; int64 tenantIdLSB = 2; string resourceType = 3; - string resourceId = 4; + string resourceKey = 4; } message GetResourceResponseMsg { @@ -257,14 +257,14 @@ message ResourceUpdateMsg { int64 tenantIdMSB = 1; int64 tenantIdLSB = 2; string resourceType = 3; - string resourceId = 4; + string resourceKey = 4; } message ResourceDeleteMsg { int64 tenantIdMSB = 1; int64 tenantIdLSB = 2; string resourceType = 3; - string resourceId = 4; + string resourceKey = 4; } message SessionCloseNotificationProto { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java index 112e0b4384..20f44d597b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java @@ -124,7 +124,7 @@ public class LwM2mVersionedModelProvider implements LwM2mModelProvider { this.tenantId, LWM2M_MODEL, key). - getValue(); + getData(); return xmlB64 != null && !xmlB64.isEmpty() ? lwM2mTransportContextServer.parseFromXmlToObjectModel( Base64.getDecoder().decode(xmlB64), diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportResourceCache.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportResourceCache.java index 34e95189fa..5f793511c6 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportResourceCache.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportResourceCache.java @@ -15,13 +15,13 @@ */ package org.thingsboard.server.common.transport; -import org.thingsboard.server.common.data.Resource; +import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.id.TenantId; public interface TransportResourceCache { - Resource get(TenantId tenantId, ResourceType resourceType, String resourceId); + TbResource get(TenantId tenantId, ResourceType resourceType, String resourceId); void update(TenantId tenantId, ResourceType resourceType, String resourceI); diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportResourceCache.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportResourceCache.java index 5160479d1e..17d898508a 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportResourceCache.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportResourceCache.java @@ -19,7 +19,7 @@ import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.Resource; +import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.transport.TransportResourceCache; @@ -42,8 +42,8 @@ import java.util.concurrent.locks.ReentrantLock; public class DefaultTransportResourceCache implements TransportResourceCache { private final Lock resourceFetchLock = new ReentrantLock(); - private final ConcurrentMap resources = new ConcurrentHashMap<>(); - private final Set keys = ConcurrentHashMap.newKeySet(); + private final ConcurrentMap resources = new ConcurrentHashMap<>(); + private final Set keys = ConcurrentHashMap.newKeySet(); private final DataDecodingEncodingService dataDecodingEncodingService; private final TransportService transportService; @@ -53,26 +53,26 @@ public class DefaultTransportResourceCache implements TransportResourceCache { } @Override - public Resource get(TenantId tenantId, ResourceType resourceType, String resourceId) { - ResourceKey resourceKey = new ResourceKey(tenantId, resourceType, resourceId); - Resource resource; + public TbResource get(TenantId tenantId, ResourceType resourceType, String resourceKey) { + ResourceCompositeKey compositeKey = new ResourceCompositeKey(tenantId, resourceType, resourceKey); + TbResource resource; - if (keys.contains(resourceKey)) { - resource = resources.get(resourceKey); + if (keys.contains(compositeKey)) { + resource = resources.get(compositeKey); if (resource == null) { - resource = resources.get(resourceKey.getSystemKey()); + resource = resources.get(compositeKey.getSystemKey()); } } else { resourceFetchLock.lock(); try { - if (keys.contains(resourceKey)) { - resource = resources.get(resourceKey); + if (keys.contains(compositeKey)) { + resource = resources.get(compositeKey); if (resource == null) { - resource = resources.get(resourceKey.getSystemKey()); + resource = resources.get(compositeKey.getSystemKey()); } } else { - resource = fetchResource(resourceKey); - keys.add(resourceKey); + resource = fetchResource(compositeKey); + keys.add(compositeKey); } } finally { resourceFetchLock.unlock(); @@ -82,20 +82,20 @@ public class DefaultTransportResourceCache implements TransportResourceCache { return resource; } - private Resource fetchResource(ResourceKey resourceKey) { - UUID tenantId = resourceKey.getTenantId().getId(); + private TbResource fetchResource(ResourceCompositeKey compositeKey) { + UUID tenantId = compositeKey.getTenantId().getId(); TransportProtos.GetResourceRequestMsg.Builder builder = TransportProtos.GetResourceRequestMsg.newBuilder(); builder .setTenantIdLSB(tenantId.getLeastSignificantBits()) .setTenantIdMSB(tenantId.getMostSignificantBits()) - .setResourceType(resourceKey.resourceType.name()) - .setResourceId(resourceKey.resourceId); + .setResourceType(compositeKey.resourceType.name()) + .setResourceKey(compositeKey.resourceKey); TransportProtos.GetResourceResponseMsg responseMsg = transportService.getResource(builder.build()); - Optional optionalResource = dataDecodingEncodingService.decode(responseMsg.getResource().toByteArray()); + Optional optionalResource = dataDecodingEncodingService.decode(responseMsg.getResource().toByteArray()); if (optionalResource.isPresent()) { - Resource resource = optionalResource.get(); - resources.put(new ResourceKey(resource.getTenantId(), resource.getResourceType(), resource.getResourceId()), resource); + TbResource resource = optionalResource.get(); + resources.put(new ResourceCompositeKey(resource.getTenantId(), resource.getResourceType(), resource.getResourceKey()), resource); return resource; } @@ -103,28 +103,28 @@ public class DefaultTransportResourceCache implements TransportResourceCache { } @Override - public void update(TenantId tenantId, ResourceType resourceType, String resourceId) { - ResourceKey resourceKey = new ResourceKey(tenantId, resourceType, resourceId); - if (keys.contains(resourceKey) || resources.containsKey(resourceKey)) { - fetchResource(resourceKey); + public void update(TenantId tenantId, ResourceType resourceType, String resourceKey) { + ResourceCompositeKey compositeKey = new ResourceCompositeKey(tenantId, resourceType, resourceKey); + if (keys.contains(compositeKey) || resources.containsKey(compositeKey)) { + fetchResource(compositeKey); } } @Override - public void evict(TenantId tenantId, ResourceType resourceType, String resourceId) { - ResourceKey resourceKey = new ResourceKey(tenantId, resourceType, resourceId); - keys.remove(resourceKey); - resources.remove(resourceKey); + public void evict(TenantId tenantId, ResourceType resourceType, String resourceKey) { + ResourceCompositeKey compositeKey = new ResourceCompositeKey(tenantId, resourceType, resourceKey); + keys.remove(compositeKey); + resources.remove(compositeKey); } @Data - private static class ResourceKey { + private static class ResourceCompositeKey { private final TenantId tenantId; private final ResourceType resourceType; - private final String resourceId; + private final String resourceKey; - public ResourceKey getSystemKey() { - return new ResourceKey(TenantId.SYS_TENANT_ID, resourceType, resourceId); + public ResourceCompositeKey getSystemKey() { + return new ResourceCompositeKey(TenantId.SYS_TENANT_ID, resourceType, resourceKey); } } } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index 6a8526f543..5882790684 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -708,13 +708,13 @@ public class DefaultTransportService implements TransportService { TransportProtos.ResourceUpdateMsg msg = toSessionMsg.getResourceUpdateMsg(); TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); ResourceType resourceType = ResourceType.valueOf(msg.getResourceType()); - String resourceId = msg.getResourceId(); + String resourceId = msg.getResourceKey(); transportResourceCache.update(tenantId, resourceType, resourceId); } else if (toSessionMsg.hasResourceDeleteMsg()) { TransportProtos.ResourceDeleteMsg msg = toSessionMsg.getResourceDeleteMsg(); TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); ResourceType resourceType = ResourceType.valueOf(msg.getResourceType()); - String resourceId = msg.getResourceId(); + String resourceId = msg.getResourceKey(); transportResourceCache.evict(tenantId, resourceType, resourceId); } else { //TODO: should we notify the device actor about missed session? diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index bd8b8ea4d8..0afdcd760a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -462,9 +462,9 @@ public class ModelConstants { public static final String RESOURCE_TABLE_NAME = "resource"; public static final String RESOURCE_TENANT_ID_COLUMN = TENANT_ID_COLUMN; public static final String RESOURCE_TYPE_COLUMN = "resource_type"; - public static final String RESOURCE_ID_COLUMN = "resource_id"; - public static final String TEXT_SEARCH_COLUMN = "text_search"; - public static final String RESOURCE_VALUE_COLUMN = "resource_value"; + public static final String RESOURCE_KEY_COLUMN = "resource_key"; + public static final String RESOURCE_TITLE_COLUMN = TITLE_PROPERTY; + public static final String RESOURCE_DATA_COLUMN = "data"; /** diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ResourceCompositeKey.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ResourceCompositeKey.java deleted file mode 100644 index 6592cf757d..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ResourceCompositeKey.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright © 2016-2021 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.dao.model.sql; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.Resource; - -import javax.persistence.Transient; -import java.io.Serializable; -import java.util.UUID; - -@NoArgsConstructor -@AllArgsConstructor -@Data -public class ResourceCompositeKey implements Serializable { - - @Transient - private static final long serialVersionUID = -3789469030818742769L; - - private UUID tenantId; - private String resourceType; - private String resourceId; - - public ResourceCompositeKey(Resource resource) { - this.tenantId = resource.getTenantId().getId(); - this.resourceType = resource.getResourceType().name(); - this.resourceId = resource.getResourceId(); - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java new file mode 100644 index 0000000000..816700fe03 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java @@ -0,0 +1,95 @@ +/** + * Copyright © 2016-2021 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.dao.model.sql; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.SearchTextEntity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DATA_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TITLE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = RESOURCE_TABLE_NAME) +public class TbResourceEntity extends BaseSqlEntity implements SearchTextEntity { + + @Column(name = RESOURCE_TENANT_ID_COLUMN, columnDefinition = "uuid") + private UUID tenantId; + + @Column(name = RESOURCE_TITLE_COLUMN) + private String title; + + @Column(name = RESOURCE_TYPE_COLUMN) + private String resourceType; + + @Column(name = RESOURCE_KEY_COLUMN) + private String resourceKey; + + @Column(name = SEARCH_TEXT_PROPERTY) + private String searchText; + + @Column(name = RESOURCE_DATA_COLUMN) + private String data; + + public TbResourceEntity() { + } + + public TbResourceEntity(TbResource resource) { + this.setUuid(resource.getId().getId()); + this.tenantId = resource.getTenantId().getId(); + this.title = resource.getTitle(); + this.resourceType = resource.getResourceType().name(); + this.resourceKey = resource.getResourceKey(); + this.searchText = resource.getSearchText(); + this.data = resource.getData(); + } + + @Override + public TbResource toData() { + TbResource resource = new TbResource(); + resource.setId(new TbResourceId(id)); + resource.setCreatedTime(createdTime); + resource.setTenantId(new TenantId(tenantId)); + resource.setTitle(title); + resource.setResourceType(ResourceType.valueOf(resourceType)); + resource.setResourceKey(resourceKey); + resource.setSearchText(searchText); + resource.setData(data); + return resource; + } + + @Override + public String getSearchTextSource() { + return title; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ResourceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java similarity index 55% rename from dao/src/main/java/org/thingsboard/server/dao/model/sql/ResourceEntity.java rename to dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java index 9f403259e4..c91a5dd9ae 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ResourceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java @@ -16,68 +16,75 @@ package org.thingsboard.server.dao.model.sql; import lombok.Data; -import org.thingsboard.server.common.data.Resource; +import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.model.ToData; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.SearchTextEntity; import javax.persistence.Column; import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.IdClass; import javax.persistence.Table; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TITLE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TYPE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_VALUE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.TEXT_SEARCH_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @Data +@EqualsAndHashCode(callSuper = true) @Entity @Table(name = RESOURCE_TABLE_NAME) -@IdClass(ResourceCompositeKey.class) -public class ResourceEntity implements ToData { +public class TbResourceInfoEntity extends BaseSqlEntity implements SearchTextEntity { - @Id @Column(name = RESOURCE_TENANT_ID_COLUMN, columnDefinition = "uuid") private UUID tenantId; - @Id + @Column(name = RESOURCE_TITLE_COLUMN) + private String title; + @Column(name = RESOURCE_TYPE_COLUMN) private String resourceType; - @Id - @Column(name = RESOURCE_ID_COLUMN) - private String resourceId; - - @Column(name = TEXT_SEARCH_COLUMN) - private String textSearch; + @Column(name = RESOURCE_KEY_COLUMN) + private String resourceKey; - @Column(name = RESOURCE_VALUE_COLUMN) - private String value; + @Column(name = SEARCH_TEXT_PROPERTY) + private String searchText; - public ResourceEntity() { + public TbResourceInfoEntity() { } - public ResourceEntity(Resource resource) { + public TbResourceInfoEntity(TbResourceInfo resource) { + this.setUuid(resource.getId().getId()); + this.setCreatedTime(resource.getCreatedTime()); this.tenantId = resource.getTenantId().getId(); + this.title = resource.getTitle(); this.resourceType = resource.getResourceType().name(); - this.resourceId = resource.getResourceId(); - this.textSearch = resource.getTextSearch(); - this.value = resource.getValue(); + this.resourceKey = resource.getResourceKey(); + this.searchText = resource.getSearchText(); } @Override - public Resource toData() { - Resource resource = new Resource(); + public TbResourceInfo toData() { + TbResourceInfo resource = new TbResourceInfo(); + resource.setId(new TbResourceId(id)); + resource.setCreatedTime(createdTime); resource.setTenantId(new TenantId(tenantId)); + resource.setTitle(title); resource.setResourceType(ResourceType.valueOf(resourceType)); - resource.setResourceId(resourceId); - resource.setTextSearch(textSearch); - resource.setValue(value); + resource.setResourceKey(resourceKey); + resource.setSearchText(searchText); return resource; } + + @Override + public String getSearchTextSource() { + return title; + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseTbResourceService.java similarity index 55% rename from dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java rename to dao/src/main/java/org/thingsboard/server/dao/resource/BaseTbResourceService.java index a84d20fda9..104b6604e1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseTbResourceService.java @@ -21,8 +21,10 @@ import org.eclipse.leshan.core.model.DefaultDDFFileValidator; import org.eclipse.leshan.core.model.InvalidDDFFileException; import org.eclipse.leshan.core.model.ObjectModel; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.Resource; import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.lwm2m.LwM2mInstance; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; @@ -30,6 +32,8 @@ import org.thingsboard.server.common.data.lwm2m.LwM2mResource; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.PaginatedRemover; +import org.thingsboard.server.dao.service.Validator; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -44,61 +48,88 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Service @Slf4j -public class BaseResourceService implements ResourceService { +public class BaseTbResourceService implements TbResourceService { - private final ResourceDao resourceDao; + public static final String INCORRECT_RESOURCE_ID = "Incorrect resourceId "; + private final TbResourceDao resourceDao; + private final TbResourceInfoDao resourceInfoDao; + private final DDFFileParser ddfFileParser; - public BaseResourceService(ResourceDao resourceDao) { + public BaseTbResourceService(TbResourceDao resourceDao, TbResourceInfoDao resourceInfoDao) { this.resourceDao = resourceDao; + this.resourceInfoDao = resourceInfoDao; + this.ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator()); } @Override - public Resource saveResource(Resource resource) { + public TbResource saveResource(TbResource resource) throws InvalidDDFFileException, IOException { log.trace("Executing saveResource [{}]", resource); + + if (resource.getId() == null && ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) { + List objectModels = + ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); + if (!objectModels.isEmpty()) { + ObjectModel objectModel = objectModels.get(0); + + String resourceKey = objectModel.id + "_" + objectModel.getVersion(); + String name = objectModel.name; + resource.setResourceKey(resourceKey); + resource.setTitle(name); + resource.setSearchText(resourceKey + ":" + name); + } + } + validate(resource); + return resourceDao.saveResource(resource); } @Override - public Resource getResource(TenantId tenantId, ResourceType resourceType, String resourceId) { - log.trace("Executing getResource [{}] [{}] [{}]", tenantId, resourceType, resourceId); - validate(tenantId, resourceType, resourceId); - return resourceDao.getResource(tenantId, resourceType, resourceId); + public TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceKey) { + log.trace("Executing getResource [{}] [{}] [{}]", tenantId, resourceType, resourceKey); + validate(tenantId, resourceType, resourceKey); + return resourceDao.getResource(tenantId, resourceType, resourceKey); } @Override - public void deleteResource(TenantId tenantId, ResourceType resourceType, String resourceId) { - log.trace("Executing deleteResource [{}] [{}] [{}]", tenantId, resourceType, resourceId); - validate(tenantId, resourceType, resourceId); - resourceDao.deleteResource(tenantId, resourceType, resourceId); + public TbResource findResourceById(TenantId tenantId, TbResourceId resourceId) { + log.trace("Executing findResourceById [{}] [{}]", tenantId, resourceId); + Validator.validateId(resourceId, INCORRECT_RESOURCE_ID + resourceId); + return resourceDao.findById(tenantId, resourceId.getId()); } @Override - public PageData findResourcesByTenantId(TenantId tenantId, PageLink pageLink) { - log.trace("Executing findByTenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - return resourceDao.findAllByTenantId(tenantId, pageLink); + public TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId) { + log.trace("Executing findResourceInfoById [{}] [{}]", tenantId, resourceId); + Validator.validateId(resourceId, INCORRECT_RESOURCE_ID + resourceId); + return resourceInfoDao.findById(tenantId, resourceId.getId()); } + @Override + public void deleteResource(TenantId tenantId, TbResourceId resourceId) { + log.trace("Executing deleteResource [{}] [{}]", tenantId, resourceId); + Validator.validateId(resourceId, INCORRECT_RESOURCE_ID + resourceId); + resourceDao.removeById(tenantId, resourceId.getId()); + } @Override - public List findAllByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType) { + public PageData findResourcesByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findByTenantId [{}]", tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - return resourceDao.findAllByTenantIdAndResourceType(tenantId, resourceType); + return resourceInfoDao.findTbResourcesByTenantId(tenantId.getId(), pageLink); } @Override public List findLwM2mObjectPage(TenantId tenantId, String sortProperty, String sortOrder, PageLink pageLink) { log.trace("Executing findByTenantId [{}]", tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - PageData resourcePageData = resourceDao.findResourcesByTenantIdAndResourceType( - tenantId, - ResourceType.LWM2M_MODEL, pageLink); - return resourcePageData.getData().stream() - .map(this::toLwM2mObject) - .sorted(getComparator(sortProperty, sortOrder)) - .collect(Collectors.toList()); + PageData resourcePageData = resourceDao.findResourcesByTenantIdAndResourceType( + tenantId, + ResourceType.LWM2M_MODEL, pageLink); + return resourcePageData.getData().stream() + .map(this::toLwM2mObject) + .sorted(getComparator(sortProperty, sortOrder)) + .collect(Collectors.toList()); } @Override @@ -107,9 +138,9 @@ public class BaseResourceService implements ResourceService { String[] objectIds) { log.trace("Executing findByTenantId [{}]", tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - List resources = resourceDao.findResourcesByTenantIdAndResourceType(tenantId, ResourceType.LWM2M_MODEL, - objectIds, - null); + List resources = resourceDao.findResourcesByTenantIdAndResourceType(tenantId, ResourceType.LWM2M_MODEL, + objectIds, + null); return resources.stream() .map(this::toLwM2mObject) .sorted(getComparator(sortProperty, sortOrder)) @@ -118,21 +149,21 @@ public class BaseResourceService implements ResourceService { @Override public void deleteResourcesByTenantId(TenantId tenantId) { - log.trace("Executing deleteDevicesByTenantId, tenantId [{}]", tenantId); + log.trace("Executing deleteResourcesByTenantId, tenantId [{}]", tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - resourceDao.removeAllByTenantId(tenantId); + tenantResourcesRemover.removeEntities(tenantId, tenantId); } - protected void validate(Resource resource) { + protected void validate(TbResource resource) { if (resource == null) { throw new DataValidationException("Resource should be specified!"); } - if (resource.getValue() == null) { + if (resource.getData() == null) { throw new DataValidationException("Resource value should be specified!"); } - validate(resource.getTenantId(), resource.getResourceType(), resource.getResourceId()); + validate(resource.getTenantId(), resource.getResourceType(), resource.getResourceKey()); if (resource.getResourceType().equals(ResourceType.LWM2M_MODEL) && this.toLwM2mObject(resource) == null) { - throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", resource.getTextSearch())); + throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", resource.getSearchText())); } } @@ -146,18 +177,18 @@ public class BaseResourceService implements ResourceService { validateId(tenantId, "Incorrect tenantId "); } - private LwM2mObject toLwM2mObject(Resource resource) { + private LwM2mObject toLwM2mObject(TbResource resource) { try { DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator()); List objectModels = - ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getValue())), resource.getTextSearch()); + ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); if (objectModels.size() == 0) { return null; } else { ObjectModel obj = objectModels.get(0); LwM2mObject lwM2mObject = new LwM2mObject(); lwM2mObject.setId(obj.id); - lwM2mObject.setKeyId(resource.getResourceId()); + lwM2mObject.setKeyId(resource.getResourceKey()); lwM2mObject.setName(obj.name); lwM2mObject.setMultiple(obj.multiple); lwM2mObject.setMandatory(obj.mandatory); @@ -170,12 +201,12 @@ public class BaseResourceService implements ResourceService { resources.add(lwM2mResource); } }); - instance.setResources(resources.stream().toArray(LwM2mResource[]::new)); + instance.setResources(resources.toArray(LwM2mResource[]::new)); lwM2mObject.setInstances(new LwM2mInstance[]{instance}); return lwM2mObject; } } catch (IOException | InvalidDDFFileException e) { - log.error("Could not parse the XML of objectModel with name [{}]", resource.getTextSearch(), e); + log.error("Could not parse the XML of objectModel with name [{}]", resource.getSearchText(), e); return null; } } @@ -190,4 +221,17 @@ public class BaseResourceService implements ResourceService { return "DESC".equals(sortOrder) ? comparator.reversed() : comparator; } + private PaginatedRemover tenantResourcesRemover = + new PaginatedRemover<>() { + + @Override + protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { + return resourceDao.findAllByTenantId(id, pageLink); + } + + @Override + protected void removeEntity(TenantId tenantId, TbResource entity) { + deleteResource(tenantId, new TbResourceId(entity.getUuidId())); + } + }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/ResourceDao.java b/dao/src/main/java/org/thingsboard/server/dao/resource/ResourceDao.java deleted file mode 100644 index 82ea27dc2c..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/ResourceDao.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright © 2016-2021 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.dao.resource; - -import org.thingsboard.server.common.data.Resource; -import org.thingsboard.server.common.data.ResourceType; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; - -import java.util.List; - -public interface ResourceDao { - - Resource saveResource(Resource resource); - - Resource getResource(TenantId tenantId, ResourceType resourceType, String resourceId); - - void deleteResource(TenantId tenantId, ResourceType resourceType, String resourceId); - - PageData findAllByTenantId(TenantId tenantId, PageLink pageLink); - - PageData findResourcesByTenantIdAndResourceType(TenantId tenantId, - ResourceType resourceType, - PageLink pageLink); - - List findAllByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType); - - List findResourcesByTenantIdAndResourceType(TenantId tenantId, - ResourceType resourceType, - String[] objectIds, - String searchText); - void removeAllByTenantId(TenantId tenantId); -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java new file mode 100644 index 0000000000..538b99aa32 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2021 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.dao.resource; + +import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.Dao; + +import java.util.List; + +public interface TbResourceDao extends Dao { + + TbResource saveResource(TbResource resource); + + TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceId); + + PageData findAllByTenantId(TenantId tenantId, PageLink pageLink); + + PageData findResourcesByTenantIdAndResourceType(TenantId tenantId, + ResourceType resourceType, + PageLink pageLink); + + List findResourcesByTenantIdAndResourceType(TenantId tenantId, + ResourceType resourceType, + String[] objectIds, + String searchText); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceInfoDao.java new file mode 100644 index 0000000000..d0c83b0b46 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceInfoDao.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2021 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.dao.resource; + +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.Dao; + +import java.util.UUID; + +public interface TbResourceInfoDao extends Dao { + + PageData findTbResourcesByTenantId(UUID tenantId, PageLink pageLink); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/ResourceDaoImpl.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java similarity index 56% rename from dao/src/main/java/org/thingsboard/server/dao/sql/resource/ResourceDaoImpl.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java index 27d335e737..e437894a02 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/ResourceDaoImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java @@ -16,72 +16,64 @@ package org.thingsboard.server.dao.sql.resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -import org.thingsboard.server.common.data.Resource; import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.model.sql.ResourceCompositeKey; -import org.thingsboard.server.dao.model.sql.ResourceEntity; -import org.thingsboard.server.dao.resource.ResourceDao; +import org.thingsboard.server.dao.model.sql.TbResourceEntity; +import org.thingsboard.server.dao.resource.TbResourceDao; +import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import java.util.List; import java.util.Objects; +import java.util.UUID; @Slf4j @Component -public class ResourceDaoImpl implements ResourceDao { +public class JpaTbResourceDao extends JpaAbstractSearchTextDao implements TbResourceDao { - private final ResourceRepository resourceRepository; + private final TbResourceRepository resourceRepository; - public ResourceDaoImpl(ResourceRepository resourceRepository) { + public JpaTbResourceDao(TbResourceRepository resourceRepository) { this.resourceRepository = resourceRepository; } @Override - @Transactional - public Resource saveResource(Resource resource) { - return DaoUtil.getData(resourceRepository.save(new ResourceEntity(resource))); + protected Class getEntityClass() { + return TbResourceEntity.class; } @Override - public Resource getResource(TenantId tenantId, ResourceType resourceType, String resourceId) { - ResourceCompositeKey key = new ResourceCompositeKey(); - key.setTenantId(tenantId.getId()); - key.setResourceType(resourceType.name()); - key.setResourceId(resourceId); - - return DaoUtil.getData(resourceRepository.findById(key)); + protected CrudRepository getCrudRepository() { + return resourceRepository; } @Override @Transactional - public void deleteResource(TenantId tenantId, ResourceType resourceType, String resourceId) { - ResourceCompositeKey key = new ResourceCompositeKey(); - key.setTenantId(tenantId.getId()); - key.setResourceType(resourceType.name()); - key.setResourceId(resourceId); - - resourceRepository.deleteById(key); + public TbResource saveResource(TbResource resource) { + return DaoUtil.getData(resourceRepository.save(new TbResourceEntity(resource))); } @Override - public PageData findAllByTenantId(TenantId tenantId, PageLink pageLink) { - return DaoUtil.toPageData(resourceRepository.findAllByTenantId(tenantId.getId(), DaoUtil.toPageable(pageLink))); + public TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceKey) { + + return DaoUtil.getData(resourceRepository.findByTenantIdAndResourceTypeAndResourceKey(tenantId.getId(), resourceType.name(), resourceKey)); } @Override - public List findAllByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType) { - return DaoUtil.convertDataList(resourceRepository.findAllByTenantIdAndResourceType(tenantId.getId(), resourceType.name())); + public PageData findAllByTenantId(TenantId tenantId, PageLink pageLink) { + return DaoUtil.toPageData(resourceRepository.findAllByTenantId(tenantId.getId(), DaoUtil.toPageable(pageLink))); } @Override - public PageData findResourcesByTenantIdAndResourceType(TenantId tenantId, - ResourceType resourceType, - PageLink pageLink) { + public PageData findResourcesByTenantIdAndResourceType(TenantId tenantId, + ResourceType resourceType, + PageLink pageLink) { return DaoUtil.toPageData(resourceRepository.findResourcesPage( tenantId.getId(), TenantId.SYS_TENANT_ID.getId(), @@ -92,9 +84,9 @@ public class ResourceDaoImpl implements ResourceDao { } @Override - public List findResourcesByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType, - String[] objectIds, - String searchText) { + public List findResourcesByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType, + String[] objectIds, + String searchText) { return objectIds == null ? DaoUtil.convertDataList(resourceRepository.findResources( tenantId.getId(), @@ -107,8 +99,4 @@ public class ResourceDaoImpl implements ResourceDao { resourceType.name(), objectIds)); } - @Override - public void removeAllByTenantId(TenantId tenantId) { - resourceRepository.removeAllByTenantId(tenantId.getId()); - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java new file mode 100644 index 0000000000..9759cf9e02 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java @@ -0,0 +1,59 @@ +/** + * Copyright © 2016-2021 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.dao.sql.resource; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.TbResourceInfoEntity; +import org.thingsboard.server.dao.resource.TbResourceInfoDao; +import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; + +import java.util.Objects; +import java.util.UUID; + +@Slf4j +@Component +public class JpaTbResourceInfoDao extends JpaAbstractSearchTextDao implements TbResourceInfoDao { + + @Autowired + private TbResourceInfoRepository resourceInfoRepository; + + @Override + protected Class getEntityClass() { + return TbResourceInfoEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return resourceInfoRepository; + } + + @Override + public PageData findTbResourcesByTenantId(UUID tenantId, PageLink pageLink) { + return DaoUtil.toPageData(resourceInfoRepository + .findByTenantId( + tenantId, + TenantId.NULL_UUID, + Objects.toString(pageLink.getTextSearch(), ""), + DaoUtil.toPageable(pageLink))); } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java new file mode 100644 index 0000000000..5df36ce285 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2021 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.dao.sql.resource; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; +import org.thingsboard.server.dao.model.sql.TbResourceInfoEntity; + +import java.util.UUID; + +public interface TbResourceInfoRepository extends CrudRepository { + + @Query("SELECT tr FROM TbResourceInfoEntity tr WHERE tr.tenantId = :tenantId " + + "AND LOWER(tr.searchText) LIKE LOWER(CONCAT(:searchText, '%'))" + + "AND (tr.tenantId = :tenantId " + + "OR (tr.tenantId = :systemAdminId " + + "AND NOT EXISTS " + + "(SELECT sr FROM TbResourceEntity sr " + + "WHERE sr.tenantId = :tenantId " + + "AND tr.resourceType = sr.resourceType " + + "AND tr.resourceKey = sr.resourceKey)))") + Page findByTenantId(@Param("tenantId") UUID tenantId, + @Param("systemAdminId") UUID sysadminId, + @Param("searchText") String searchText, + Pageable pageable); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/ResourceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java similarity index 55% rename from dao/src/main/java/org/thingsboard/server/dao/sql/resource/ResourceRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java index df1e08fde2..488192eb13 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/ResourceRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java @@ -20,65 +20,63 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; -import org.thingsboard.server.dao.model.sql.ResourceCompositeKey; -import org.thingsboard.server.dao.model.sql.ResourceEntity; +import org.thingsboard.server.dao.model.sql.TbResourceEntity; import java.util.List; import java.util.UUID; -public interface ResourceRepository extends CrudRepository { +public interface TbResourceRepository extends CrudRepository { + TbResourceEntity findByTenantIdAndResourceTypeAndResourceKey(UUID tenantId, String resourceType, String resourceKey); - Page findAllByTenantId(UUID tenantId, Pageable pageable); + Page findAllByTenantId(UUID tenantId, Pageable pageable); - @Query("SELECT tr FROM ResourceEntity tr " + + @Query("SELECT tr FROM TbResourceEntity tr " + "WHERE tr.resourceType = :resourceType " + - "AND LOWER(tr.textSearch) LIKE LOWER(CONCAT('%', :searchText, '%')) " + + "AND LOWER(tr.searchText) LIKE LOWER(CONCAT('%', :searchText, '%')) " + "AND (tr.tenantId = :tenantId " + "OR (tr.tenantId = :systemAdminId " + "AND NOT EXISTS " + - "(SELECT sr FROM ResourceEntity sr " + + "(SELECT sr FROM TbResourceEntity sr " + "WHERE sr.tenantId = :tenantId " + "AND sr.resourceType = :resourceType " + - "AND tr.resourceId = sr.resourceId)))") - Page findResourcesPage( + "AND tr.resourceKey = sr.resourceKey)))") + Page findResourcesPage( @Param("tenantId") UUID tenantId, @Param("systemAdminId") UUID sysAdminId, @Param("resourceType") String resourceType, @Param("searchText") String search, Pageable pageable); - List findAllByTenantIdAndResourceType(UUID tenantId, String resourceType); - void removeAllByTenantId(UUID tenantId); - @Query("SELECT tr FROM ResourceEntity tr " + + @Query("SELECT tr FROM TbResourceEntity tr " + "WHERE tr.resourceType = :resourceType " + - "AND LOWER(tr.textSearch) LIKE LOWER(CONCAT('%', :searchText, '%')) " + + "AND LOWER(tr.searchText) LIKE LOWER(CONCAT('%', :searchText, '%')) " + "AND (tr.tenantId = :tenantId " + "OR (tr.tenantId = :systemAdminId " + "AND NOT EXISTS " + - "(SELECT sr FROM ResourceEntity sr " + + "(SELECT sr FROM TbResourceEntity sr " + "WHERE sr.tenantId = :tenantId " + "AND sr.resourceType = :resourceType " + - "AND tr.resourceId = sr.resourceId)))") - List findResources(@Param("tenantId") UUID tenantId, - @Param("systemAdminId") UUID sysAdminId, - @Param("resourceType") String resourceType, - @Param("searchText") String search); + "AND tr.resourceKey = sr.resourceKey)))") + List findResources(@Param("tenantId") UUID tenantId, + @Param("systemAdminId") UUID sysAdminId, + @Param("resourceType") String resourceType, + @Param("searchText") String search); - @Query("SELECT tr FROM ResourceEntity tr " + + @Query("SELECT tr FROM TbResourceEntity tr " + "WHERE tr.resourceType = :resourceType " + - "AND tr.resourceId in (:resourceIds) " + + "AND tr.resourceKey in (:resourceIds) " + "AND (tr.tenantId = :tenantId " + "OR (tr.tenantId = :systemAdminId " + "AND NOT EXISTS " + - "(SELECT sr FROM ResourceEntity sr " + + "(SELECT sr FROM TbResourceEntity sr " + "WHERE sr.tenantId = :tenantId " + "AND sr.resourceType = :resourceType " + - "AND tr.resourceId = sr.resourceId)))") - List findResourcesByIds(@Param("tenantId") UUID tenantId, - @Param("systemAdminId") UUID sysAdminId, - @Param("resourceType") String resourceType, - @Param("resourceIds") String[] objectIds); + "AND tr.resourceKey = sr.resourceKey)))") + List findResourcesByIds(@Param("tenantId") UUID tenantId, + @Param("systemAdminId") UUID sysAdminId, + @Param("resourceType") String resourceType, + @Param("resourceIds") String[] objectIds); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 3016474f27..96a6d14c42 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -35,7 +35,7 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.resource.ResourceService; +import org.thingsboard.server.dao.resource.TbResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -90,7 +90,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe private RuleChainService ruleChainService; @Autowired - private ResourceService resourceService; + private TbResourceService resourceService; @Override public Tenant findTenantById(TenantId tenantId) { diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 76cdf46d1c..404c6a20c4 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -426,10 +426,13 @@ CREATE TABLE IF NOT EXISTS api_usage_state ( ); CREATE TABLE IF NOT EXISTS resource ( + id uuid NOT NULL CONSTRAINT resource_pkey PRIMARY KEY, + created_time bigint NOT NULL, tenant_id uuid NOT NULL, + title varchar(255) NOT NULL, resource_type varchar(32) NOT NULL, - resource_id varchar(255) NOT NULL, - text_search varchar(255), - resource_value varchar, - CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id) + resource_key varchar(255) NOT NULL, + search_text varchar(255), + data varchar, + CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key) ); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 143e58c479..006866edd8 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -452,12 +452,15 @@ CREATE TABLE IF NOT EXISTS api_usage_state ( ); CREATE TABLE IF NOT EXISTS resource ( + id uuid NOT NULL CONSTRAINT resource_pkey PRIMARY KEY, + created_time bigint NOT NULL, tenant_id uuid NOT NULL, + title varchar(255) NOT NULL, resource_type varchar(32) NOT NULL, - resource_id varchar(255) NOT NULL, - text_search varchar(255), - resource_value varchar, - CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id) + resource_key varchar(255) NOT NULL, + search_text varchar(255), + data varchar, + CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key) ); CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint)