Commit e2dd5b96aef5aca4c6a21012a5bbbda9b489169d

Authored by Yevhen Bondarenko
Committed by GitHub
1 parent 3f4714c2

added resource dao support (#4213)

* Version set to 3.3.0-SNAPSHOT

* added resource dao

* added resource support in transport lvl (get resources and "update", "delete" notifications)

* refactoring

* added resource table to hsql schema

* added check for models dir in InstallScripts

* added pageLink support to getResources
Showing 29 changed files with 801 additions and 9 deletions
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.controller;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.security.access.prepost.PreAuthorize;
  20 +import org.springframework.web.bind.annotation.PathVariable;
  21 +import org.springframework.web.bind.annotation.RequestMapping;
  22 +import org.springframework.web.bind.annotation.RequestMethod;
  23 +import org.springframework.web.bind.annotation.RequestParam;
  24 +import org.springframework.web.bind.annotation.ResponseBody;
  25 +import org.springframework.web.bind.annotation.RestController;
  26 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  27 +import org.thingsboard.server.common.data.id.TenantId;
  28 +import org.thingsboard.server.common.data.page.PageData;
  29 +import org.thingsboard.server.common.data.page.PageLink;
  30 +import org.thingsboard.server.common.data.transport.resource.Resource;
  31 +import org.thingsboard.server.common.data.transport.resource.ResourceType;
  32 +import org.thingsboard.server.dao.resource.ResourceService;
  33 +import org.thingsboard.server.queue.util.TbCoreComponent;
  34 +
  35 +@Slf4j
  36 +@RestController
  37 +@TbCoreComponent
  38 +@RequestMapping("/api")
  39 +public class ResourceController extends BaseController {
  40 +
  41 + private final ResourceService resourceService;
  42 +
  43 + public ResourceController(ResourceService resourceService) {
  44 + this.resourceService = resourceService;
  45 + }
  46 +
  47 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
  48 + @RequestMapping(value = "/resource", method = RequestMethod.POST)
  49 + @ResponseBody
  50 + public Resource saveResource(Resource resource) throws ThingsboardException {
  51 + try {
  52 + resource.setTenantId(getTenantId());
  53 + Resource savedResource = checkNotNull(resourceService.saveResource(resource));
  54 + tbClusterService.onResourceChange(savedResource, null);
  55 + return savedResource;
  56 + } catch (Exception e) {
  57 + throw handleException(e);
  58 + }
  59 + }
  60 +
  61 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
  62 + @RequestMapping(value = "/resource", method = RequestMethod.GET)
  63 + @ResponseBody
  64 + public PageData<Resource> getResources(@RequestParam(required = false) boolean system,
  65 + @RequestParam int pageSize,
  66 + @RequestParam int page,
  67 + @RequestParam(required = false) String sortProperty,
  68 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  69 + try {
  70 + PageLink pageLink = createPageLink(pageSize, page, null, sortProperty, sortOrder);
  71 + return checkNotNull(resourceService.findResourcesByTenantId(system ? TenantId.SYS_TENANT_ID : getTenantId(), pageLink));
  72 + } catch (Exception e) {
  73 + throw handleException(e);
  74 + }
  75 + }
  76 +
  77 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
  78 + @RequestMapping(value = "/resource/{resourceType}/{resourceId}", method = RequestMethod.DELETE)
  79 + @ResponseBody
  80 + public void deleteResource(@PathVariable("resourceType") ResourceType resourceType,
  81 + @PathVariable("resourceId") String resourceId) throws ThingsboardException {
  82 + try {
  83 + Resource resource = checkNotNull(resourceService.getResource(getTenantId(), resourceType, resourceId));
  84 + resourceService.deleteResource(getTenantId(), resourceType, resourceId);
  85 + tbClusterService.onResourceDeleted(resource, null);
  86 + } catch (Exception e) {
  87 + throw handleException(e);
  88 + }
  89 + }
  90 +}
... ...
... ... @@ -194,6 +194,12 @@ public class ThingsboardInstallService {
194 194 log.info("Updating system data...");
195 195 systemDataLoaderService.updateSystemWidgets();
196 196 break;
  197 + case "3.2.2":
  198 + log.info("Upgrading ThingsBoard from version 3.2.2 to 3.3.0 ...");
  199 + databaseEntitiesUpgradeService.upgradeDatabase("3.2.2");
  200 +
  201 + log.info("Updating system data...");
  202 + break;
197 203 default:
198 204 throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion);
199 205
... ... @@ -226,6 +232,7 @@ public class ThingsboardInstallService {
226 232 systemDataLoaderService.createAdminSettings();
227 233 systemDataLoaderService.loadSystemWidgets();
228 234 systemDataLoaderService.createOAuth2Templates();
  235 + systemDataLoaderService.loadSystemLwm2mResources();
229 236 // systemDataLoaderService.loadSystemPlugins();
230 237 // systemDataLoaderService.loadSystemRules();
231 238
... ...
... ... @@ -445,6 +445,11 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
445 445 installScripts.loadSystemWidgets();
446 446 }
447 447
  448 + @Override
  449 + public void loadSystemLwm2mResources() throws Exception {
  450 + installScripts.loadSystemLwm2mResources();
  451 + }
  452 +
448 453 private User createUser(Authority authority,
449 454 TenantId tenantId,
450 455 CustomerId customerId,
... ...
... ... @@ -24,15 +24,17 @@ import org.springframework.util.StringUtils;
24 24 import org.thingsboard.server.common.data.Dashboard;
25 25 import org.thingsboard.server.common.data.id.CustomerId;
26 26 import org.thingsboard.server.common.data.id.EntityId;
27   -import org.thingsboard.server.common.data.id.RuleChainId;
28 27 import org.thingsboard.server.common.data.id.TenantId;
29 28 import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate;
30 29 import org.thingsboard.server.common.data.rule.RuleChain;
31 30 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
  31 +import org.thingsboard.server.common.data.transport.resource.Resource;
  32 +import org.thingsboard.server.common.data.transport.resource.ResourceType;
32 33 import org.thingsboard.server.common.data.widget.WidgetType;
33 34 import org.thingsboard.server.common.data.widget.WidgetsBundle;
34 35 import org.thingsboard.server.dao.dashboard.DashboardService;
35 36 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
  37 +import org.thingsboard.server.dao.resource.ResourceService;
36 38 import org.thingsboard.server.dao.rule.RuleChainService;
37 39 import org.thingsboard.server.dao.widget.WidgetTypeService;
38 40 import org.thingsboard.server.dao.widget.WidgetsBundleService;
... ... @@ -42,6 +44,7 @@ import java.nio.file.DirectoryStream;
42 44 import java.nio.file.Files;
43 45 import java.nio.file.Path;
44 46 import java.nio.file.Paths;
  47 +import java.util.Base64;
45 48 import java.util.Optional;
46 49
47 50 import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper;
... ... @@ -66,8 +69,11 @@ public class InstallScripts {
66 69 public static final String WIDGET_BUNDLES_DIR = "widget_bundles";
67 70 public static final String OAUTH2_CONFIG_TEMPLATES_DIR = "oauth2_config_templates";
68 71 public static final String DASHBOARDS_DIR = "dashboards";
  72 + public static final String MODELS_DIR = "models";
  73 + public static final String CREDENTIALS_DIR = "credentials";
69 74
70 75 public static final String JSON_EXT = ".json";
  76 + public static final String XML_EXT = ".xml";
71 77
72 78 @Value("${install.data_dir:}")
73 79 private String dataDir;
... ... @@ -87,6 +93,9 @@ public class InstallScripts {
87 93 @Autowired
88 94 private OAuth2ConfigTemplateService oAuth2TemplateService;
89 95
  96 + @Autowired
  97 + private ResourceService resourceService;
  98 +
90 99 public Path getTenantRuleChainsDir() {
91 100 return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR);
92 101 }
... ... @@ -186,6 +195,42 @@ public class InstallScripts {
186 195 }
187 196 }
188 197
  198 + public void loadSystemLwm2mResources() throws Exception {
  199 + Path modelsDir = Paths.get(getDataDir(), MODELS_DIR);
  200 + if (Files.isDirectory(modelsDir)) {
  201 + try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(modelsDir, path -> path.toString().endsWith(XML_EXT))) {
  202 + dirStream.forEach(
  203 + path -> {
  204 + try {
  205 + Resource resource = new Resource();
  206 + resource.setTenantId(TenantId.SYS_TENANT_ID);
  207 + resource.setResourceType(ResourceType.LWM2M_MODEL);
  208 + resource.setResourceId(path.getFileName().toString());
  209 + resource.setValue(Base64.getEncoder().encodeToString(Files.readAllBytes(path)));
  210 + resourceService.saveResource(resource);
  211 + } catch (Exception e) {
  212 + log.error("Unable to load lwm2m model [{}]", path.toString());
  213 + throw new RuntimeException("Unable to load lwm2m model", e);
  214 + }
  215 + }
  216 + );
  217 + }
  218 + }
  219 +
  220 + Path jksPath = Paths.get(getDataDir(), CREDENTIALS_DIR, "serverKeyStore.jks");
  221 + try {
  222 + Resource resource = new Resource();
  223 + resource.setTenantId(TenantId.SYS_TENANT_ID);
  224 + resource.setResourceType(ResourceType.JKS);
  225 + resource.setResourceId(jksPath.getFileName().toString());
  226 + resource.setValue(Base64.getEncoder().encodeToString(Files.readAllBytes(jksPath)));
  227 + resourceService.saveResource(resource);
  228 + } catch (Exception e) {
  229 + log.error("Unable to load lwm2m serverKeyStore [{}]", jksPath.toString());
  230 + throw new RuntimeException("Unable to load l2m2m serverKeyStore", e);
  231 + }
  232 + }
  233 +
189 234 public void loadDashboards(TenantId tenantId, CustomerId customerId) throws Exception {
190 235 Path dashboardsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR);
191 236 try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) {
... ... @@ -208,7 +253,6 @@ public class InstallScripts {
208 253 }
209 254 }
210 255
211   -
212 256 public void loadDemoRuleChains(TenantId tenantId) throws Exception {
213 257 try {
214 258 createDefaultRuleChains(tenantId);
... ...
... ... @@ -434,6 +434,25 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
434 434 log.info("Schema updated.");
435 435 }
436 436 break;
  437 + case "3.2.2":
  438 + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
  439 + log.info("Updating schema ...");
  440 + try {
  441 + conn.createStatement().execute("CREATE TABLE IF NOT EXISTS resource (" +
  442 + " tenant_id uuid NOT NULL," +
  443 + " resource_type varchar(32) NOT NULL," +
  444 + " resource_id varchar(255) NOT NULL," +
  445 + " resource_value varchar," +
  446 + " CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id)" +
  447 + " );");
  448 +
  449 + conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3003000;");
  450 + } catch (Exception e) {
  451 + log.error("Failed updating schema!!!", e);
  452 + }
  453 + log.info("Schema updated.");
  454 + }
  455 + break;
437 456 default:
438 457 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
439 458 }
... ...
... ... @@ -33,4 +33,6 @@ public interface SystemDataLoaderService {
33 33
34 34 void deleteSystemWidgetBundle(String bundleAlias) throws Exception;
35 35
  36 + void loadSystemLwm2mResources() throws Exception;
  37 +
36 38 }
... ...
... ... @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.page.PageLink;
33 33 import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigBootstrap;
34 34 import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigServer;
35 35 import org.thingsboard.server.dao.service.Validator;
  36 +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
36 37 import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
37 38
38 39 import java.math.BigInteger;
... ...
... ... @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.id.EntityId;
34 34 import org.thingsboard.server.common.data.id.RuleChainId;
35 35 import org.thingsboard.server.common.data.id.TenantId;
36 36 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  37 +import org.thingsboard.server.common.data.transport.resource.Resource;
37 38 import org.thingsboard.server.common.msg.TbMsg;
38 39 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
39 40 import org.thingsboard.server.common.msg.queue.ServiceType;
... ... @@ -247,6 +248,34 @@ public class DefaultTbClusterService implements TbClusterService {
247 248 onEntityDelete(entity.getTenantId(), entity.getId(), entity.getName(), callback);
248 249 }
249 250
  251 + @Override
  252 + public void onResourceChange(Resource resource, TbQueueCallback callback) {
  253 + TenantId tenantId = resource.getTenantId();
  254 + log.trace("[{}][{}][{}] Processing change resource", tenantId, resource.getResourceType(), resource.getResourceId());
  255 + TransportProtos.ResourceUpdateMsg resourceUpdateMsg = TransportProtos.ResourceUpdateMsg.newBuilder()
  256 + .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
  257 + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
  258 + .setResourceType(resource.getResourceType().name())
  259 + .setResourceId(resource.getResourceId())
  260 + .build();
  261 + ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setResourceUpdateMsg(resourceUpdateMsg).build();
  262 + broadcast(transportMsg, callback);
  263 + }
  264 +
  265 + @Override
  266 + public void onResourceDeleted(Resource resource, TbQueueCallback callback) {
  267 + TenantId tenantId = resource.getTenantId();
  268 + log.trace("[{}][{}][{}] Processing delete resource", tenantId, resource.getResourceType(), resource.getResourceId());
  269 + TransportProtos.ResourceDeleteMsg resourceUpdateMsg = TransportProtos.ResourceDeleteMsg.newBuilder()
  270 + .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
  271 + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
  272 + .setResourceType(resource.getResourceType().name())
  273 + .setResourceId(resource.getResourceId())
  274 + .build();
  275 + ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setResourceDeleteMsg(resourceUpdateMsg).build();
  276 + broadcast(transportMsg, callback);
  277 + }
  278 +
250 279 public <T> void onEntityChange(TenantId tenantId, EntityId entityid, T entity, TbQueueCallback callback) {
251 280 String entityName = (entity instanceof HasName) ? ((HasName) entity).getName() : entity.getClass().getName();
252 281 log.trace("[{}][{}][{}] Processing [{}] change event", tenantId, entityid.getEntityType(), entityid.getId(), entityName);
... ...
... ... @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.TenantProfile;
24 24 import org.thingsboard.server.common.data.id.EntityId;
25 25 import org.thingsboard.server.common.data.id.TenantId;
26 26 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  27 +import org.thingsboard.server.common.data.transport.resource.Resource;
27 28 import org.thingsboard.server.common.msg.TbMsg;
28 29 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
29 30 import org.thingsboard.server.gen.transport.TransportProtos;
... ... @@ -71,4 +72,8 @@ public interface TbClusterService {
71 72 void onDeviceChange(Device device, TbQueueCallback callback);
72 73
73 74 void onDeviceDeleted(Device device, TbQueueCallback callback);
  75 +
  76 + void onResourceChange(Resource resource, TbQueueCallback callback);
  77 +
  78 + void onResourceDeleted(Resource resource, TbQueueCallback callback);
74 79 }
... ...
... ... @@ -25,6 +25,7 @@ import com.google.protobuf.ByteString;
25 25 import lombok.extern.slf4j.Slf4j;
26 26 import org.springframework.stereotype.Service;
27 27 import org.springframework.util.StringUtils;
  28 +import org.thingsboard.common.util.JacksonUtil;
28 29 import org.thingsboard.server.common.data.ApiUsageState;
29 30 import org.thingsboard.server.common.data.DataConstants;
30 31 import org.thingsboard.server.common.data.Device;
... ... @@ -38,9 +39,12 @@ import org.thingsboard.server.common.data.id.CustomerId;
38 39 import org.thingsboard.server.common.data.id.DeviceId;
39 40 import org.thingsboard.server.common.data.id.DeviceProfileId;
40 41 import org.thingsboard.server.common.data.id.TenantId;
  42 +import org.thingsboard.server.common.data.page.PageLink;
41 43 import org.thingsboard.server.common.data.relation.EntityRelation;
42 44 import org.thingsboard.server.common.data.security.DeviceCredentials;
43 45 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
  46 +import org.thingsboard.server.common.data.transport.resource.Resource;
  47 +import org.thingsboard.server.common.data.transport.resource.ResourceType;
44 48 import org.thingsboard.server.common.msg.EncryptionUtil;
45 49 import org.thingsboard.server.common.msg.TbMsg;
46 50 import org.thingsboard.server.common.msg.TbMsgDataType;
... ... @@ -53,14 +57,15 @@ import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
53 57 import org.thingsboard.server.dao.device.provision.ProvisionRequest;
54 58 import org.thingsboard.server.dao.device.provision.ProvisionResponse;
55 59 import org.thingsboard.server.dao.relation.RelationService;
  60 +import org.thingsboard.server.dao.resource.ResourceService;
56 61 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
57   -import org.thingsboard.common.util.JacksonUtil;
58 62 import org.thingsboard.server.gen.transport.TransportProtos;
59 63 import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
60 64 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg;
61 65 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg;
62 66 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
63 67 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
  68 +import org.thingsboard.server.gen.transport.TransportProtos.GetResourcesRequestMsg;
64 69 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg;
65 70 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionResponseStatus;
66 71 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
... ... @@ -77,11 +82,14 @@ import org.thingsboard.server.service.profile.TbDeviceProfileCache;
77 82 import org.thingsboard.server.service.queue.TbClusterService;
78 83 import org.thingsboard.server.service.state.DeviceStateService;
79 84
  85 +import java.util.Collections;
  86 +import java.util.List;
80 87 import java.util.UUID;
81 88 import java.util.concurrent.ConcurrentHashMap;
82 89 import java.util.concurrent.ConcurrentMap;
83 90 import java.util.concurrent.locks.Lock;
84 91 import java.util.concurrent.locks.ReentrantLock;
  92 +import java.util.stream.Collectors;
85 93
86 94 /**
87 95 * Created by ashvayka on 05.10.18.
... ... @@ -104,6 +112,7 @@ public class DefaultTransportApiService implements TransportApiService {
104 112 private final TbClusterService tbClusterService;
105 113 private final DataDecodingEncodingService dataDecodingEncodingService;
106 114 private final DeviceProvisionService deviceProvisionService;
  115 + private final ResourceService resourceService;
107 116
108 117 private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>();
109 118
... ... @@ -112,7 +121,7 @@ public class DefaultTransportApiService implements TransportApiService {
112 121 RelationService relationService, DeviceCredentialsService deviceCredentialsService,
113 122 DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService,
114 123 TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService,
115   - DeviceProvisionService deviceProvisionService) {
  124 + DeviceProvisionService deviceProvisionService, ResourceService resourceService) {
116 125 this.deviceProfileCache = deviceProfileCache;
117 126 this.tenantProfileCache = tenantProfileCache;
118 127 this.apiUsageStateService = apiUsageStateService;
... ... @@ -124,6 +133,7 @@ public class DefaultTransportApiService implements TransportApiService {
124 133 this.tbClusterService = tbClusterService;
125 134 this.dataDecodingEncodingService = dataDecodingEncodingService;
126 135 this.deviceProvisionService = deviceProvisionService;
  136 + this.resourceService = resourceService;
127 137 }
128 138
129 139 @Override
... ... @@ -157,6 +167,9 @@ public class DefaultTransportApiService implements TransportApiService {
157 167 } else if (transportApiRequestMsg.hasProvisionDeviceRequestMsg()) {
158 168 return Futures.transform(handle(transportApiRequestMsg.getProvisionDeviceRequestMsg()),
159 169 value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  170 + } else if (transportApiRequestMsg.hasResourcesRequestMsg()) {
  171 + return Futures.transform(handle(transportApiRequestMsg.getResourcesRequestMsg()),
  172 + value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
160 173 }
161 174 return Futures.transform(getEmptyTransportApiResponseFuture(),
162 175 value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
... ... @@ -315,9 +328,9 @@ public class DefaultTransportApiService implements TransportApiService {
315 328 return TransportApiResponseMsg.newBuilder().setProvisionDeviceResponseMsg(TransportProtos.ProvisionDeviceResponseMsg.newBuilder().setStatus(status).build()).build();
316 329 }
317 330 TransportProtos.ProvisionDeviceResponseMsg.Builder provisionResponse = TransportProtos.ProvisionDeviceResponseMsg.newBuilder()
318   - .setCredentialsType(TransportProtos.CredentialsType.valueOf(deviceCredentials.getCredentialsType().name()))
319   - .setStatus(status);
320   - switch (deviceCredentials.getCredentialsType()){
  331 + .setCredentialsType(TransportProtos.CredentialsType.valueOf(deviceCredentials.getCredentialsType().name()))
  332 + .setStatus(status);
  333 + switch (deviceCredentials.getCredentialsType()) {
321 334 case ACCESS_TOKEN:
322 335 provisionResponse.setCredentialsValue(deviceCredentials.getCredentialsId());
323 336 break;
... ... @@ -353,6 +366,40 @@ public class DefaultTransportApiService implements TransportApiService {
353 366 return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setEntityProfileResponseMsg(builder).build());
354 367 }
355 368
  369 + private ListenableFuture<TransportApiResponseMsg> handle(GetResourcesRequestMsg requestMsg) {
  370 + TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()));
  371 + TransportProtos.GetResourcesResponseMsg.Builder builder = TransportProtos.GetResourcesResponseMsg.newBuilder();
  372 + String resourceType = requestMsg.getResourceType();
  373 + String resourceId = requestMsg.getResourceId();
  374 +
  375 + List<TransportProtos.ResourceMsg> resources;
  376 +
  377 + if (resourceType != null && resourceId != null) {
  378 + resources = Collections.singletonList(toProto(
  379 + resourceService.getResource(tenantId, ResourceType.valueOf(resourceType), resourceId)));
  380 + } else {
  381 + //TODO: add page link params to request proto if need or remove this
  382 + resources = resourceService.findResourcesByTenantId(tenantId, new PageLink(100))
  383 + .getData()
  384 + .stream()
  385 + .map(this::toProto)
  386 + .collect(Collectors.toList());
  387 + }
  388 +
  389 + builder.addAllResources(resources);
  390 + return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setResourcesResponseMsg(builder).build());
  391 + }
  392 +
  393 + private TransportProtos.ResourceMsg toProto(Resource resource) {
  394 + return TransportProtos.ResourceMsg.newBuilder()
  395 + .setTenantIdMSB(resource.getTenantId().getId().getMostSignificantBits())
  396 + .setTenantIdLSB(resource.getTenantId().getId().getLeastSignificantBits())
  397 + .setResourceType(resource.getResourceType().name())
  398 + .setResourceId(resource.getResourceId())
  399 + .setValue(resource.getValue())
  400 + .build();
  401 + }
  402 +
356 403 private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId, DeviceCredentials credentials) {
357 404 return Futures.transform(deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, deviceId), device -> {
358 405 if (device == null) {
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.resource;
  17 +
  18 +import org.thingsboard.server.common.data.id.TenantId;
  19 +import org.thingsboard.server.common.data.page.PageData;
  20 +import org.thingsboard.server.common.data.page.PageLink;
  21 +import org.thingsboard.server.common.data.transport.resource.Resource;
  22 +import org.thingsboard.server.common.data.transport.resource.ResourceType;
  23 +
  24 +
  25 +public interface ResourceService {
  26 + Resource saveResource(Resource resource);
  27 +
  28 + Resource getResource(TenantId tenantId, ResourceType resourceType, String resourceId);
  29 +
  30 + PageData<Resource> findResourcesByTenantId(TenantId tenantId, PageLink pageLink);
  31 +
  32 + void deleteResource(TenantId tenantId, ResourceType resourceType, String resourceId);
  33 +
  34 + void deleteResourcesByTenantId(TenantId tenantId);
  35 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.resource;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.HasTenantId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +
  22 +@Data
  23 +public class Resource implements HasTenantId {
  24 + private TenantId tenantId;
  25 + private ResourceType resourceType;
  26 + private String resourceId;
  27 + private String value;
  28 +
  29 + @Override
  30 + public String toString() {
  31 + return "Resource{" +
  32 + "tenantId=" + tenantId +
  33 + ", resourceType=" + resourceType +
  34 + ", resourceId='" + resourceId + '\'' +
  35 + '}';
  36 + }
  37 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.resource;
  17 +
  18 +public enum ResourceType {
  19 + LWM2M_MODEL, JKS, PKCS_12
  20 +}
... ...
... ... @@ -21,6 +21,6 @@ import java.lang.annotation.Retention;
21 21 import java.lang.annotation.RetentionPolicy;
22 22
23 23 @Retention(RetentionPolicy.RUNTIME)
24   -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
  24 +@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
25 25 public @interface TbLwM2mTransportComponent {
26 26 }
... ...
... ... @@ -201,6 +201,25 @@ message LwM2MResponseMsg {
201 201 LwM2MRegistrationResponseMsg registrationMsg = 1;
202 202 }
203 203
  204 +message ResourceMsg {
  205 + int64 tenantIdMSB = 1;
  206 + int64 tenantIdLSB = 2;
  207 + string resourceType = 3;
  208 + string resourceId = 4;
  209 + string value = 5;
  210 +}
  211 +
  212 +message GetResourcesRequestMsg {
  213 + int64 tenantIdMSB = 1;
  214 + int64 tenantIdLSB = 2;
  215 + string resourceType = 3;
  216 + string resourceId = 4;
  217 +}
  218 +
  219 +message GetResourcesResponseMsg {
  220 + repeated ResourceMsg resources = 1;
  221 +}
  222 +
204 223 message ValidateDeviceLwM2MCredentialsRequestMsg {
205 224 string credentialsId = 1;
206 225 }
... ... @@ -242,6 +261,20 @@ message EntityDeleteMsg {
242 261 int64 entityIdLSB = 3;
243 262 }
244 263
  264 +message ResourceUpdateMsg {
  265 + int64 tenantIdMSB = 1;
  266 + int64 tenantIdLSB = 2;
  267 + string resourceType = 3;
  268 + string resourceId = 4;
  269 +}
  270 +
  271 +message ResourceDeleteMsg {
  272 + int64 tenantIdMSB = 1;
  273 + int64 tenantIdLSB = 2;
  274 + string resourceType = 3;
  275 + string resourceId = 4;
  276 +}
  277 +
245 278 message SessionCloseNotificationProto {
246 279 string message = 1;
247 280 }
... ... @@ -525,6 +558,7 @@ message TransportApiRequestMsg {
525 558 ValidateBasicMqttCredRequestMsg validateBasicMqttCredRequestMsg = 6;
526 559 ProvisionDeviceRequestMsg provisionDeviceRequestMsg = 7;
527 560 ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8;
  561 + GetResourcesRequestMsg resourcesRequestMsg = 9;
528 562 }
529 563
530 564 /* Response from ThingsBoard Core Service to Transport Service */
... ... @@ -534,6 +568,7 @@ message TransportApiResponseMsg {
534 568 GetEntityProfileResponseMsg entityProfileResponseMsg = 3;
535 569 ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 4;
536 570 LwM2MResponseMsg lwM2MResponseMsg = 6;
  571 + GetResourcesResponseMsg resourcesResponseMsg = 7;
537 572 }
538 573
539 574 /* Messages that are handled by ThingsBoard Core Service */
... ... @@ -578,6 +613,8 @@ message ToTransportMsg {
578 613 EntityDeleteMsg entityDeleteMsg = 9;
579 614 ProvisionDeviceResponseMsg provisionResponse = 10;
580 615 ToTransportUpdateCredentialsProto toTransportUpdateCredentialsNotification = 11;
  616 + ResourceUpdateMsg resourceUpdateMsg = 12;
  617 + ResourceDeleteMsg resourceDeleteMsg = 13;
581 618 }
582 619
583 620 message UsageStatsKVProto{
... ...
... ... @@ -25,6 +25,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestM
25 25 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg;
26 26 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg;
27 27 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
  28 +import org.thingsboard.server.gen.transport.TransportProtos.GetResourcesRequestMsg;
  29 +import org.thingsboard.server.gen.transport.TransportProtos.GetResourcesResponseMsg;
28 30 import org.thingsboard.server.gen.transport.TransportProtos.LwM2MRequestMsg;
29 31 import org.thingsboard.server.gen.transport.TransportProtos.LwM2MResponseMsg;
30 32 import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
... ... @@ -51,6 +53,8 @@ public interface TransportService {
51 53
52 54 GetEntityProfileResponseMsg getEntityProfile(GetEntityProfileRequestMsg msg);
53 55
  56 + GetResourcesResponseMsg getResources(GetResourcesRequestMsg msg);
  57 +
54 58 void process(DeviceTransportType transportType, ValidateDeviceTokenRequestMsg msg,
55 59 TransportServiceCallback<ValidateDeviceCredentialsResponse> callback);
56 60
... ...
... ... @@ -255,6 +255,18 @@ public class DefaultTransportService implements TransportService {
255 255 }
256 256
257 257 @Override
  258 + public TransportProtos.GetResourcesResponseMsg getResources(TransportProtos.GetResourcesRequestMsg msg) {
  259 + TbProtoQueueMsg<TransportProtos.TransportApiRequestMsg> protoMsg =
  260 + new TbProtoQueueMsg<>(UUID.randomUUID(), TransportProtos.TransportApiRequestMsg.newBuilder().setResourcesRequestMsg(msg).build());
  261 + try {
  262 + TbProtoQueueMsg<TransportApiResponseMsg> response = transportApiRequestTemplate.send(protoMsg).get();
  263 + return response.getValue().getResourcesResponseMsg();
  264 + } catch (InterruptedException | ExecutionException e) {
  265 + throw new RuntimeException(e);
  266 + }
  267 + }
  268 +
  269 + @Override
258 270 public void process(DeviceTransportType transportType, TransportProtos.ValidateDeviceTokenRequestMsg msg,
259 271 TransportServiceCallback<ValidateDeviceCredentialsResponse> callback) {
260 272 log.trace("Processing msg: {}", msg);
... ... @@ -688,6 +700,10 @@ public class DefaultTransportService implements TransportService {
688 700 } else if (EntityType.DEVICE.equals(entityType)) {
689 701 rateLimitService.remove(new DeviceId(entityUuid));
690 702 }
  703 + } else if (toSessionMsg.hasResourceUpdateMsg()) {
  704 + //TODO: update resource cache
  705 + } else if (toSessionMsg.hasResourceDeleteMsg()) {
  706 + //TODO: remove resource from cache
691 707 } else {
692 708 //TODO: should we notify the device actor about missed session?
693 709 log.debug("[{}] Missing session.", sessionId);
... ... @@ -695,6 +711,7 @@ public class DefaultTransportService implements TransportService {
695 711 }
696 712 }
697 713
  714 +
698 715 public void onProfileUpdate(DeviceProfile deviceProfile) {
699 716 long deviceProfileIdMSB = deviceProfile.getId().getId().getMostSignificantBits();
700 717 long deviceProfileIdLSB = deviceProfile.getId().getId().getLeastSignificantBits();
... ...
... ... @@ -454,6 +454,15 @@ public class ModelConstants {
454 454 public static final String API_USAGE_STATE_SMS_EXEC_COLUMN = "sms_exec";
455 455
456 456 /**
  457 + * Resource constants.
  458 + */
  459 + public static final String RESOURCE_TABLE_NAME = "resource";
  460 + public static final String RESOURCE_TENANT_ID_COLUMN = TENANT_ID_COLUMN;
  461 + public static final String RESOURCE_TYPE_COLUMN = "resource_type";
  462 + public static final String RESOURCE_ID_COLUMN = "resource_id";
  463 + public static final String RESOURCE_VALUE_COLUMN = "resource_value";
  464 +
  465 + /**
457 466 * Cassandra attributes and timeseries constants.
458 467 */
459 468 public static final String ATTRIBUTES_KV_CF = "attributes_kv_cf";
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model.sql;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
  20 +import lombok.NoArgsConstructor;
  21 +import org.thingsboard.server.common.data.relation.EntityRelation;
  22 +import org.thingsboard.server.common.data.transport.resource.Resource;
  23 +
  24 +import javax.persistence.Transient;
  25 +import java.io.Serializable;
  26 +import java.util.UUID;
  27 +
  28 +@NoArgsConstructor
  29 +@AllArgsConstructor
  30 +@Data
  31 +public class ResourceCompositeKey implements Serializable {
  32 +
  33 + @Transient
  34 + private static final long serialVersionUID = -3789469030818742769L;
  35 +
  36 + private UUID tenantId;
  37 + private String resourceType;
  38 + private String resourceId;
  39 +
  40 + public ResourceCompositeKey(Resource resource) {
  41 + this.tenantId = resource.getTenantId().getId();
  42 + this.resourceType = resource.getResourceType().name();
  43 + this.resourceId = resource.getResourceId();
  44 + }
  45 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model.sql;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.id.TenantId;
  20 +import org.thingsboard.server.common.data.transport.resource.Resource;
  21 +import org.thingsboard.server.common.data.transport.resource.ResourceType;
  22 +import org.thingsboard.server.dao.model.ToData;
  23 +
  24 +import javax.persistence.Column;
  25 +import javax.persistence.Entity;
  26 +import javax.persistence.Id;
  27 +import javax.persistence.IdClass;
  28 +import javax.persistence.Table;
  29 +import java.util.UUID;
  30 +
  31 +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ID_COLUMN;
  32 +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME;
  33 +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN;
  34 +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TYPE_COLUMN;
  35 +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_VALUE_COLUMN;
  36 +
  37 +@Data
  38 +@Entity
  39 +@Table(name = RESOURCE_TABLE_NAME)
  40 +@IdClass(ResourceCompositeKey.class)
  41 +public class ResourceEntity implements ToData<Resource> {
  42 +
  43 + @Id
  44 + @Column(name = RESOURCE_TENANT_ID_COLUMN, columnDefinition = "uuid")
  45 + private UUID tenantId;
  46 +
  47 + @Id
  48 + @Column(name = RESOURCE_TYPE_COLUMN)
  49 + private String resourceType;
  50 +
  51 + @Id
  52 + @Column(name = RESOURCE_ID_COLUMN)
  53 + private String resourceId;
  54 +
  55 + @Column(name = RESOURCE_VALUE_COLUMN)
  56 + private String value;
  57 +
  58 + public ResourceEntity() {
  59 + }
  60 +
  61 + public ResourceEntity(Resource resource) {
  62 + this.tenantId = resource.getTenantId().getId();
  63 + this.resourceType = resource.getResourceType().name();
  64 + this.resourceId = resource.getResourceId();
  65 + this.value = resource.getValue();
  66 + }
  67 +
  68 + @Override
  69 + public Resource toData() {
  70 + Resource resource = new Resource();
  71 + resource.setTenantId(new TenantId(tenantId));
  72 + resource.setResourceType(ResourceType.valueOf(resourceType));
  73 + resource.setResourceId(resourceId);
  74 + resource.setValue(value);
  75 + return resource;
  76 + }
  77 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.resource;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.stereotype.Service;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.data.page.PageData;
  22 +import org.thingsboard.server.common.data.page.PageLink;
  23 +import org.thingsboard.server.common.data.transport.resource.Resource;
  24 +import org.thingsboard.server.common.data.transport.resource.ResourceType;
  25 +import org.thingsboard.server.dao.exception.DataValidationException;
  26 +
  27 +import static org.thingsboard.server.dao.device.DeviceServiceImpl.INCORRECT_TENANT_ID;
  28 +import static org.thingsboard.server.dao.service.Validator.validateId;
  29 +
  30 +@Service
  31 +@Slf4j
  32 +public class BaseResourceService implements ResourceService {
  33 +
  34 + private final ResourceDao resourceDao;
  35 +
  36 + public BaseResourceService(ResourceDao resourceDao) {
  37 + this.resourceDao = resourceDao;
  38 + }
  39 +
  40 + @Override
  41 + public Resource saveResource(Resource resource) {
  42 + log.trace("Executing saveResource [{}]", resource);
  43 + validate(resource);
  44 + return resourceDao.saveResource(resource);
  45 + }
  46 +
  47 + @Override
  48 + public Resource getResource(TenantId tenantId, ResourceType resourceType, String resourceId) {
  49 + log.trace("Executing getResource [{}] [{}] [{}]", tenantId, resourceType, resourceId);
  50 + validate(tenantId, resourceType, resourceId);
  51 + return resourceDao.getResource(tenantId, resourceType, resourceId);
  52 + }
  53 +
  54 + @Override
  55 + public void deleteResource(TenantId tenantId, ResourceType resourceType, String resourceId) {
  56 + log.trace("Executing deleteResource [{}] [{}] [{}]", tenantId, resourceType, resourceId);
  57 + validate(tenantId, resourceType, resourceId);
  58 + resourceDao.deleteResource(tenantId, resourceType, resourceId);
  59 + }
  60 +
  61 + @Override
  62 + public PageData<Resource> findResourcesByTenantId(TenantId tenantId, PageLink pageLink) {
  63 + log.trace("Executing findByTenantId [{}]", tenantId);
  64 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  65 + return resourceDao.findAllByTenantId(tenantId, pageLink);
  66 + }
  67 +
  68 + @Override
  69 + public void deleteResourcesByTenantId(TenantId tenantId) {
  70 + log.trace("Executing deleteDevicesByTenantId, tenantId [{}]", tenantId);
  71 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  72 + resourceDao.removeAllByTenantId(tenantId);
  73 + }
  74 +
  75 + protected void validate(Resource resource) {
  76 + if (resource == null) {
  77 + throw new DataValidationException("Resource should be specified!");
  78 + }
  79 +
  80 + if (resource.getValue() == null) {
  81 + throw new DataValidationException("Resource value should be specified!");
  82 + }
  83 + validate(resource.getTenantId(), resource.getResourceType(), resource.getResourceId());
  84 + }
  85 +
  86 + protected void validate(TenantId tenantId, ResourceType resourceType, String resourceId) {
  87 + if (resourceType == null) {
  88 + throw new DataValidationException("Resource type should be specified!");
  89 + }
  90 + if (resourceId == null) {
  91 + throw new DataValidationException("Resource id should be specified!");
  92 + }
  93 + validateId(tenantId, "Incorrect tenantId ");
  94 + }
  95 +
  96 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.resource;
  17 +
  18 +import org.thingsboard.server.common.data.id.TenantId;
  19 +import org.thingsboard.server.common.data.page.PageData;
  20 +import org.thingsboard.server.common.data.page.PageLink;
  21 +import org.thingsboard.server.common.data.transport.resource.Resource;
  22 +import org.thingsboard.server.common.data.transport.resource.ResourceType;
  23 +
  24 +public interface ResourceDao {
  25 +
  26 + Resource saveResource(Resource resource);
  27 +
  28 + Resource getResource(TenantId tenantId, ResourceType resourceType, String resourceId);
  29 +
  30 + void deleteResource(TenantId tenantId, ResourceType resourceType, String resourceId);
  31 +
  32 + PageData<Resource> findAllByTenantId(TenantId tenantId, PageLink pageLink);
  33 +
  34 + void removeAllByTenantId(TenantId tenantId);
  35 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sql.resource;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.stereotype.Component;
  20 +import org.springframework.transaction.annotation.Transactional;
  21 +import org.thingsboard.server.common.data.id.TenantId;
  22 +import org.thingsboard.server.common.data.page.PageData;
  23 +import org.thingsboard.server.common.data.page.PageLink;
  24 +import org.thingsboard.server.common.data.transport.resource.Resource;
  25 +import org.thingsboard.server.common.data.transport.resource.ResourceType;
  26 +import org.thingsboard.server.dao.DaoUtil;
  27 +import org.thingsboard.server.dao.model.sql.ResourceCompositeKey;
  28 +import org.thingsboard.server.dao.model.sql.ResourceEntity;
  29 +import org.thingsboard.server.dao.resource.ResourceDao;
  30 +
  31 +@Slf4j
  32 +@Component
  33 +public class ResourceDaoImpl implements ResourceDao {
  34 +
  35 + private final ResourceRepository resourceRepository;
  36 +
  37 + public ResourceDaoImpl(ResourceRepository resourceRepository) {
  38 + this.resourceRepository = resourceRepository;
  39 + }
  40 +
  41 + @Override
  42 + @Transactional
  43 + public Resource saveResource(Resource resource) {
  44 + return DaoUtil.getData(resourceRepository.save(new ResourceEntity(resource)));
  45 + }
  46 +
  47 + @Override
  48 + public Resource getResource(TenantId tenantId, ResourceType resourceType, String resourceId) {
  49 + ResourceCompositeKey key = new ResourceCompositeKey();
  50 + key.setTenantId(tenantId.getId());
  51 + key.setResourceType(resourceType.name());
  52 + key.setResourceId(resourceId);
  53 +
  54 + return DaoUtil.getData(resourceRepository.findById(key));
  55 + }
  56 +
  57 + @Override
  58 + @Transactional
  59 + public void deleteResource(TenantId tenantId, ResourceType resourceType, String resourceId) {
  60 + ResourceCompositeKey key = new ResourceCompositeKey();
  61 + key.setTenantId(tenantId.getId());
  62 + key.setResourceType(resourceType.name());
  63 + key.setResourceId(resourceId);
  64 +
  65 + resourceRepository.deleteById(key);
  66 + }
  67 +
  68 + @Override
  69 + public PageData<Resource> findAllByTenantId(TenantId tenantId, PageLink pageLink) {
  70 + return DaoUtil.toPageData(resourceRepository.findAllByTenantId(tenantId.getId(), DaoUtil.toPageable(pageLink)));
  71 + }
  72 +
  73 + @Override
  74 + public void removeAllByTenantId(TenantId tenantId) {
  75 + resourceRepository.removeAllByTenantId(tenantId.getId());
  76 + }
  77 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sql.resource;
  17 +
  18 +import org.springframework.data.domain.Page;
  19 +import org.springframework.data.domain.Pageable;
  20 +import org.springframework.data.repository.CrudRepository;
  21 +import org.thingsboard.server.dao.model.sql.ResourceCompositeKey;
  22 +import org.thingsboard.server.dao.model.sql.ResourceEntity;
  23 +
  24 +import java.util.UUID;
  25 +
  26 +public interface ResourceRepository extends CrudRepository<ResourceEntity, ResourceCompositeKey> {
  27 +
  28 + Page<ResourceEntity> findAllByTenantId(UUID tenantId, Pageable pageable);
  29 +
  30 + void removeAllByTenantId(UUID tenantId);
  31 +}
... ...
... ... @@ -35,6 +35,7 @@ import org.thingsboard.server.dao.device.DeviceService;
35 35 import org.thingsboard.server.dao.entity.AbstractEntityService;
36 36 import org.thingsboard.server.dao.entityview.EntityViewService;
37 37 import org.thingsboard.server.dao.exception.DataValidationException;
  38 +import org.thingsboard.server.dao.resource.ResourceService;
38 39 import org.thingsboard.server.dao.rule.RuleChainService;
39 40 import org.thingsboard.server.dao.service.DataValidator;
40 41 import org.thingsboard.server.dao.service.PaginatedRemover;
... ... @@ -88,6 +89,9 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe
88 89 @Autowired
89 90 private RuleChainService ruleChainService;
90 91
  92 + @Autowired
  93 + private ResourceService resourceService;
  94 +
91 95 @Override
92 96 public Tenant findTenantById(TenantId tenantId) {
93 97 log.trace("Executing findTenantById [{}]", tenantId);
... ... @@ -140,6 +144,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe
140 144 userService.deleteTenantAdmins(tenantId);
141 145 ruleChainService.deleteRuleChainsByTenantId(tenantId);
142 146 apiUsageStateService.deleteApiUsageStateByTenantId(tenantId);
  147 + resourceService.deleteResourcesByTenantId(tenantId);
143 148 tenantDao.removeById(tenantId, tenantId.getId());
144 149 deleteEntityRelations(tenantId, tenantId);
145 150 }
... ...
... ... @@ -420,3 +420,11 @@ CREATE TABLE IF NOT EXISTS api_usage_state (
420 420 sms_exec varchar(32),
421 421 CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)
422 422 );
  423 +
  424 +CREATE TABLE IF NOT EXISTS resource (
  425 + tenant_id uuid NOT NULL,
  426 + resource_type varchar(32) NOT NULL,
  427 + resource_id varchar(255) NOT NULL,
  428 + resource_value varchar,
  429 + CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id)
  430 +);
... ...
... ... @@ -447,6 +447,14 @@ CREATE TABLE IF NOT EXISTS api_usage_state (
447 447 CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)
448 448 );
449 449
  450 +CREATE TABLE IF NOT EXISTS resource (
  451 + tenant_id uuid NOT NULL,
  452 + resource_type varchar(32) NOT NULL,
  453 + resource_id varchar(255) NOT NULL,
  454 + resource_value varchar,
  455 + CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id)
  456 +);
  457 +
450 458 CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint)
451 459 LANGUAGE plpgsql AS
452 460 $$
... ...
... ... @@ -28,4 +28,5 @@ DROP TABLE IF EXISTS oauth2_client_registration;
28 28 DROP TABLE IF EXISTS oauth2_client_registration_info;
29 29 DROP TABLE IF EXISTS oauth2_client_registration_template;
30 30 DROP TABLE IF EXISTS api_usage_state;
  31 +DROP TABLE IF EXISTS resource;
31 32 DROP FUNCTION IF EXISTS to_uuid;
... ...
... ... @@ -28,4 +28,5 @@ DROP TABLE IF EXISTS tb_schema_settings;
28 28 DROP TABLE IF EXISTS oauth2_client_registration;
29 29 DROP TABLE IF EXISTS oauth2_client_registration_info;
30 30 DROP TABLE IF EXISTS oauth2_client_registration_template;
31   -DROP TABLE IF EXISTS api_usage_state;
\ No newline at end of file
  31 +DROP TABLE IF EXISTS api_usage_state;
  32 +DROP TABLE IF EXISTS resource;
... ...