diff --git a/application/src/main/java/org/thingsboard/server/controller/ResourceController.java b/application/src/main/java/org/thingsboard/server/controller/ResourceController.java new file mode 100644 index 0000000000..b09e4e23b2 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/ResourceController.java @@ -0,0 +1,82 @@ +/** + * 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.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.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.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.transport.resource.Resource; +import org.thingsboard.server.common.data.transport.resource.ResourceType; +import org.thingsboard.server.dao.resource.ResourceService; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.List; + +@Slf4j +@RestController +@TbCoreComponent +@RequestMapping("/api") +public class ResourceController extends BaseController { + + private final ResourceService resourceService; + + public ResourceController(ResourceService resourceService) { + this.resourceService = resourceService; + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/resource", method = RequestMethod.POST) + @ResponseBody + public Resource saveResource(Resource resource) throws ThingsboardException { + try { + resource.setTenantId(getCurrentUser().getTenantId()); + return checkNotNull(resourceService.saveResource(resource)); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/resource", method = RequestMethod.GET) + @ResponseBody + public List getResources(@RequestParam(required = false) boolean system) throws ThingsboardException { + try { + return checkNotNull(resourceService.findByTenantId(system ? TenantId.SYS_TENANT_ID : getCurrentUser().getTenantId())); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/resource/{resourceType}/{resourceId}", method = RequestMethod.DELETE) + @ResponseBody + public boolean deleteResource(@PathVariable("resourceType") ResourceType resourceType, + @PathVariable("resourceId") String resourceId) throws ThingsboardException { + try { + return resourceService.deleteResource(getCurrentUser().getTenantId(), resourceType, resourceId); + } catch (Exception e) { + throw handleException(e); + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 8b4a5b77a3..63eea2a7fc 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -194,6 +194,12 @@ public class ThingsboardInstallService { log.info("Updating system data..."); systemDataLoaderService.updateSystemWidgets(); break; + case "3.2.2": + log.info("Upgrading ThingsBoard from version 3.2.2 to 3.3.0 ..."); + databaseEntitiesUpgradeService.upgradeDatabase("3.2.2"); + + log.info("Updating system data..."); + break; default: throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion); @@ -226,6 +232,7 @@ public class ThingsboardInstallService { systemDataLoaderService.createAdminSettings(); systemDataLoaderService.loadSystemWidgets(); systemDataLoaderService.createOAuth2Templates(); + systemDataLoaderService.loadSystemLwm2mResources(); // systemDataLoaderService.loadSystemPlugins(); // systemDataLoaderService.loadSystemRules(); diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java index 9028fe279e..6f257a367e 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java @@ -445,6 +445,11 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { installScripts.loadSystemWidgets(); } + @Override + public void loadSystemLwm2mResources() throws Exception { + installScripts.loadSystemLwm2mResources(); + } + private User createUser(Authority authority, TenantId tenantId, CustomerId customerId, 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 47f8529c11..5029a520e3 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 @@ -29,10 +29,13 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.transport.resource.Resource; +import org.thingsboard.server.common.data.transport.resource.ResourceType; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; +import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; @@ -42,6 +45,7 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Base64; import java.util.Optional; import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper; @@ -66,8 +70,11 @@ public class InstallScripts { public static final String WIDGET_BUNDLES_DIR = "widget_bundles"; public static final String OAUTH2_CONFIG_TEMPLATES_DIR = "oauth2_config_templates"; public static final String DASHBOARDS_DIR = "dashboards"; + public static final String MODELS_DIR = "models"; + public static final String CREDENTIALS_DIR = "credentials"; public static final String JSON_EXT = ".json"; + public static final String XML_EXT = ".xml"; @Value("${install.data_dir:}") private String dataDir; @@ -87,6 +94,9 @@ public class InstallScripts { @Autowired private OAuth2ConfigTemplateService oAuth2TemplateService; + @Autowired + private ResourceService resourceService; + public Path getTenantRuleChainsDir() { return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR); } @@ -186,6 +196,40 @@ public class InstallScripts { } } + public void loadSystemLwm2mResources() throws Exception { + Path modelsDir = Paths.get(getDataDir(), MODELS_DIR); + try (DirectoryStream dirStream = Files.newDirectoryStream(modelsDir, path -> path.toString().endsWith(XML_EXT))) { + dirStream.forEach( + path -> { + try { + Resource resource = new Resource(); + resource.setTenantId(TenantId.SYS_TENANT_ID); + resource.setResourceType(ResourceType.LWM2M_MODEL); + resource.setResourceId(path.getFileName().toString()); + resource.setValue(Files.readString(path)); + resourceService.saveResource(resource); + } catch (Exception e) { + log.error("Unable to load lwm2m model [{}]", path.toString()); + throw new RuntimeException("Unable to load lwm2m model", e); + } + } + ); + } + + Path jksPath = Paths.get(getDataDir(), CREDENTIALS_DIR, "serverKeyStore.jks"); + try { + Resource resource = new Resource(); + resource.setTenantId(TenantId.SYS_TENANT_ID); + resource.setResourceType(ResourceType.LWM2M_KEY_STORE); + resource.setResourceId(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 l2m2m serverKeyStore", e); + } + } + public void loadDashboards(TenantId tenantId, CustomerId customerId) throws Exception { Path dashboardsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR); try (DirectoryStream dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) { 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 c3495d960b..a56cc7ca25 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 @@ -434,6 +434,25 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService log.info("Schema updated."); } break; + case "3.2.2": + 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," + + " resource_value varchar," + + " CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id)" + + " );"); + + conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3003000;"); + } catch (Exception e) { + log.error("Failed updating schema!!!", e); + } + log.info("Schema updated."); + } + break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java index 73e2b6ea57..a6f33f476f 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java @@ -33,4 +33,6 @@ public interface SystemDataLoaderService { void deleteSystemWidgetBundle(String bundleAlias) throws Exception; + void loadSystemLwm2mResources() throws Exception; + } 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/ResourceService.java new file mode 100644 index 0000000000..99cf1ef87b --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java @@ -0,0 +1,33 @@ +/** + * 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.id.TenantId; +import org.thingsboard.server.common.data.transport.resource.Resource; +import org.thingsboard.server.common.data.transport.resource.ResourceType; + +import java.util.List; + + +public interface ResourceService { + Resource saveResource(Resource resource); + + Resource getResource(TenantId tenantId, ResourceType resourceType, String resourceId); + + boolean deleteResource(TenantId tenantId, ResourceType resourceType, String resourceId); + + List findByTenantId(TenantId tenantId); +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/resource/Resource.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/resource/Resource.java new file mode 100644 index 0000000000..c29b704b04 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/resource/Resource.java @@ -0,0 +1,37 @@ +/** + * 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.transport.resource; + +import lombok.Data; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +public class Resource implements HasTenantId { + private TenantId tenantId; + private ResourceType resourceType; + private String resourceId; + private String value; + + @Override + public String toString() { + return "Resource{" + + "tenantId=" + tenantId + + ", resourceType=" + resourceType + + ", resourceId='" + resourceId + '\'' + + '}'; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/resource/ResourceType.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/resource/ResourceType.java new file mode 100644 index 0000000000..2e12bbdda2 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/resource/ResourceType.java @@ -0,0 +1,20 @@ +/** + * 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.transport.resource; + +public enum ResourceType { + LWM2M_MODEL, LWM2M_KEY_STORE +} 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 fc6a2bed25..3f11a4ed00 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 @@ -453,6 +453,15 @@ public class ModelConstants { public static final String API_USAGE_STATE_EMAIL_EXEC_COLUMN = "email_exec"; public static final String API_USAGE_STATE_SMS_EXEC_COLUMN = "sms_exec"; + /** + * Resource constants. + */ + 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 RESOURCE_VALUE_COLUMN = "resource_value"; + /** * Cassandra attributes and timeseries constants. */ 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 new file mode 100644 index 0000000000..828f1f4341 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ResourceCompositeKey.java @@ -0,0 +1,45 @@ +/** + * 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.relation.EntityRelation; +import org.thingsboard.server.common.data.transport.resource.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/ResourceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ResourceEntity.java new file mode 100644 index 0000000000..a877afa027 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ResourceEntity.java @@ -0,0 +1,77 @@ +/** + * 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 org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.transport.resource.Resource; +import org.thingsboard.server.common.data.transport.resource.ResourceType; +import org.thingsboard.server.dao.model.ToData; + +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_TABLE_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_VALUE_COLUMN; + +@Data +@Entity +@Table(name = RESOURCE_TABLE_NAME) +@IdClass(ResourceCompositeKey.class) +public class ResourceEntity implements ToData { + + @Id + @Column(name = RESOURCE_TENANT_ID_COLUMN, columnDefinition = "uuid") + private UUID tenantId; + + @Id + @Column(name = RESOURCE_TYPE_COLUMN) + private String resourceType; + + @Id + @Column(name = RESOURCE_ID_COLUMN) + private String resourceId; + + @Column(name = RESOURCE_VALUE_COLUMN) + private String value; + + public ResourceEntity() { + } + + public ResourceEntity(Resource resource) { + this.tenantId = resource.getTenantId().getId(); + this.resourceType = resource.getResourceType().name(); + this.resourceId = resource.getResourceId(); + this.value = resource.getValue(); + } + + @Override + public Resource toData() { + Resource resource = new Resource(); + resource.setTenantId(new TenantId(tenantId)); + resource.setResourceType(ResourceType.valueOf(resourceType)); + resource.setResourceId(resourceId); + resource.setValue(value); + return resource; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java new file mode 100644 index 0000000000..84ce22eef1 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -0,0 +1,90 @@ +/** + * 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 lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.transport.resource.Resource; +import org.thingsboard.server.common.data.transport.resource.ResourceType; +import org.thingsboard.server.dao.exception.DataValidationException; + +import java.util.List; + +@Service +@Slf4j +public class BaseResourceService implements ResourceService { + + private final ResourceDao resourceDao; + + public BaseResourceService(ResourceDao resourceDao) { + this.resourceDao = resourceDao; + } + + @Override + public Resource saveResource(Resource resource) { + log.trace("Executing saveResource [{}]", resource); + 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); + } + + @Override + public boolean deleteResource(TenantId tenantId, ResourceType resourceType, String resourceId) { + log.trace("Executing deleteResource [{}] [{}] [{}]", tenantId, resourceType, resourceId); + validate(tenantId, resourceType, resourceId); + return resourceDao.deleteResource(tenantId, resourceType, resourceId); + } + + @Override + public List findByTenantId(TenantId tenantId) { + log.trace("Executing findByTenantId [{}]", tenantId); + return resourceDao.findAllByTenantId(tenantId); + } + + protected void validate(Resource resource) { + if (resource == null) { + throw new DataValidationException("Resource should be specified!"); + } + + if (resource.getValue() == null) { + throw new DataValidationException("Resource value should be specified!"); + } + validate(resource.getTenantId(), resource.getResourceType(), resource.getResourceId()); + } + + protected void validate(TenantId tenantId, ResourceType resourceType, String resourceId) { + if (resourceType == null) { + throw new DataValidationException("Resource type should be specified!"); + } + if (resourceId == null) { + throw new DataValidationException("Resource id should be specified!"); + } + validate(tenantId); + } + + protected void validate(TenantId tenantId) { + if (tenantId == null) { + throw new DataValidationException("Tenant id should be specified!"); + } + } +} 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 new file mode 100644 index 0000000000..f722423412 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/ResourceDao.java @@ -0,0 +1,33 @@ +/** + * 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.id.TenantId; +import org.thingsboard.server.common.data.transport.resource.Resource; +import org.thingsboard.server.common.data.transport.resource.ResourceType; + +import java.util.List; + +public interface ResourceDao { + + Resource saveResource(Resource resource); + + Resource getResource(TenantId tenantId, ResourceType resourceType, String resourceId); + + boolean deleteResource(TenantId tenantId, ResourceType resourceType, String resourceId); + + List findAllByTenantId(TenantId tenantId); +} 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/ResourceDaoImpl.java new file mode 100644 index 0000000000..b11c14546a --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/ResourceDaoImpl.java @@ -0,0 +1,73 @@ +/** + * 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.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.transport.resource.Resource; +import org.thingsboard.server.common.data.transport.resource.ResourceType; +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 java.util.List; + +@Slf4j +@Component +public class ResourceDaoImpl implements ResourceDao { + + private final ResourceRepository resourceRepository; + + public ResourceDaoImpl(ResourceRepository resourceRepository) { + this.resourceRepository = resourceRepository; + } + + @Override + @Transactional + public Resource saveResource(Resource resource) { + return DaoUtil.getData(resourceRepository.save(new ResourceEntity(resource))); + } + + @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)); + } + + @Override + @Transactional + public boolean 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); + return resourceRepository.existsById(key); + } + + @Override + public List findAllByTenantId(TenantId tenantId) { + return DaoUtil.convertDataList(resourceRepository.findAllByTenantId(tenantId)); + } +} 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/ResourceRepository.java new file mode 100644 index 0000000000..d2d92b8b55 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/ResourceRepository.java @@ -0,0 +1,28 @@ +/** + * 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.repository.CrudRepository; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.model.sql.ResourceCompositeKey; +import org.thingsboard.server.dao.model.sql.ResourceEntity; + +import java.util.List; + +public interface ResourceRepository extends CrudRepository { + + List findAllByTenantId(TenantId tenantId); +} diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 72e00ae85a..3bf5ce29c8 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -447,6 +447,14 @@ CREATE TABLE IF NOT EXISTS api_usage_state ( CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id) ); +CREATE TABLE IF NOT EXISTS resource ( + tenant_id uuid NOT NULL, + resource_type varchar(32) NOT NULL, + resource_id varchar(255) NOT NULL, + resource_value varchar, + CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id) +); + CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint) LANGUAGE plpgsql AS $$