Commit ad87924437b99ccdf0cf3ea6d4ea79599d524469

Authored by Igor Kulikov
1 parent 07738c31

Tenant and Device profile improvements

Showing 33 changed files with 783 additions and 129 deletions
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.controller;
17 17
  18 +import com.datastax.oss.driver.api.core.uuid.Uuids;
18 19 import com.fasterxml.jackson.core.type.TypeReference;
19 20 import com.fasterxml.jackson.databind.JsonNode;
20 21 import com.fasterxml.jackson.databind.ObjectMapper;
... ... @@ -59,9 +60,14 @@ import org.springframework.util.MultiValueMap;
59 60 import org.springframework.web.context.WebApplicationContext;
60 61 import org.thingsboard.server.common.data.BaseData;
61 62 import org.thingsboard.server.common.data.Customer;
  63 +import org.thingsboard.server.common.data.DeviceProfile;
  64 +import org.thingsboard.server.common.data.DeviceProfileType;
62 65 import org.thingsboard.server.common.data.Tenant;
63 66 import org.thingsboard.server.common.data.User;
  67 +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
  68 +import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
64 69 import org.thingsboard.server.common.data.id.HasId;
  70 +import org.thingsboard.server.common.data.id.RuleChainId;
65 71 import org.thingsboard.server.common.data.id.TenantId;
66 72 import org.thingsboard.server.common.data.id.UUIDBased;
67 73 import org.thingsboard.server.common.data.page.PageLink;
... ... @@ -305,6 +311,20 @@ public abstract class AbstractWebTest {
305 311 }
306 312 }
307 313
  314 + protected DeviceProfile createDeviceProfile(String name) {
  315 + DeviceProfile deviceProfile = new DeviceProfile();
  316 + deviceProfile.setName(name);
  317 + deviceProfile.setType(DeviceProfileType.DEFAULT);
  318 + deviceProfile.setDescription(name + " Test");
  319 + DeviceProfileData deviceProfileData = new DeviceProfileData();
  320 + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration();
  321 + deviceProfileData.setConfiguration(configuration);
  322 + deviceProfile.setProfileData(deviceProfileData);
  323 + deviceProfile.setDefault(false);
  324 + deviceProfile.setDefaultRuleChainId(new RuleChainId(Uuids.timeBased()));
  325 + return deviceProfile;
  326 + }
  327 +
308 328 protected ResultActions doGet(String urlTemplate, Object... urlVariables) throws Exception {
309 329 MockHttpServletRequestBuilder getRequest = get(urlTemplate, urlVariables);
310 330 setJwtToken(getRequest);
... ...
... ... @@ -24,6 +24,7 @@ import org.junit.Before;
24 24 import org.junit.Test;
25 25 import org.thingsboard.server.common.data.Customer;
26 26 import org.thingsboard.server.common.data.Device;
  27 +import org.thingsboard.server.common.data.DeviceProfile;
27 28 import org.thingsboard.server.common.data.EntitySubtype;
28 29 import org.thingsboard.server.common.data.Tenant;
29 30 import org.thingsboard.server.common.data.User;
... ... @@ -237,6 +238,21 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
237 238 }
238 239
239 240 @Test
  241 + public void testSaveSameDeviceWithDifferentDeviceProfileId() throws Exception {
  242 + Device device = new Device();
  243 + device.setName("My device");
  244 + device.setType("default");
  245 + Device savedDevice = doPost("/api/device", device, Device.class);
  246 + DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile 2");
  247 + DeviceProfile savedDeviceProfile2 = doPost("/api/deviceProfile", deviceProfile2, DeviceProfile.class);
  248 +
  249 + savedDevice.setDeviceProfileId(savedDeviceProfile2.getId());
  250 +
  251 + doPost("/api/device/", savedDevice).andExpect(status().isBadRequest())
  252 + .andExpect(statusReason(containsString("Changing device profile is prohibited")));
  253 + }
  254 +
  255 + @Test
240 256 public void testAssignDeviceToCustomerFromDifferentTenant() throws Exception {
241 257 loginSysAdmin();
242 258
... ...
... ... @@ -15,21 +15,20 @@
15 15 */
16 16 package org.thingsboard.server.controller;
17 17
18   -import com.datastax.oss.driver.api.core.uuid.Uuids;
19 18 import com.fasterxml.jackson.core.type.TypeReference;
20 19 import org.junit.After;
21 20 import org.junit.Assert;
22 21 import org.junit.Before;
23 22 import org.junit.Test;
  23 +import org.thingsboard.server.common.data.Device;
24 24 import org.thingsboard.server.common.data.DeviceProfile;
  25 +import org.thingsboard.server.common.data.DeviceProfileType;
25 26 import org.thingsboard.server.common.data.EntityInfo;
26 27 import org.thingsboard.server.common.data.Tenant;
27 28 import org.thingsboard.server.common.data.User;
28   -import org.thingsboard.server.common.data.id.RuleChainId;
29 29 import org.thingsboard.server.common.data.page.PageData;
30 30 import org.thingsboard.server.common.data.page.PageLink;
31 31 import org.thingsboard.server.common.data.security.Authority;
32   -import org.thingsboard.server.dao.util.mapping.JacksonUtil;
33 32
34 33 import java.util.ArrayList;
35 34 import java.util.Collections;
... ... @@ -149,6 +148,32 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
149 148 }
150 149
151 150 @Test
  151 + public void testSaveSameDeviceProfileWithDifferentType() throws Exception {
  152 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  153 + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
  154 + savedDeviceProfile.setType(DeviceProfileType.LWM2M);
  155 + doPost("/api/deviceProfile", savedDeviceProfile).andExpect(status().isBadRequest())
  156 + .andExpect(statusReason(containsString("Changing type of device profile is prohibited")));
  157 + }
  158 +
  159 + @Test
  160 + public void testDeleteDeviceProfileWithExistingDevice() throws Exception {
  161 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  162 + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
  163 +
  164 + Device device = new Device();
  165 + device.setName("Test device");
  166 + device.setType("default");
  167 + device.setDeviceProfileId(savedDeviceProfile.getId());
  168 +
  169 + Device savedDevice = doPost("/api/device", device, Device.class);
  170 +
  171 + doDelete("/api/deviceProfile/" + savedDeviceProfile.getId().getId().toString())
  172 + .andExpect(status().isBadRequest())
  173 + .andExpect(statusReason(containsString("The device profile referenced by the devices cannot be deleted")));
  174 + }
  175 +
  176 + @Test
152 177 public void testDeleteDeviceProfile() throws Exception {
153 178 DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
154 179 DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
... ... @@ -254,13 +279,4 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
254 279 Assert.assertEquals(1, pageData.getTotalElements());
255 280 }
256 281
257   - private DeviceProfile createDeviceProfile(String name) {
258   - DeviceProfile deviceProfile = new DeviceProfile();
259   - deviceProfile.setName(name);
260   - deviceProfile.setDescription(name + " Test");
261   - deviceProfile.setProfileData(JacksonUtil.OBJECT_MAPPER.createObjectNode());
262   - deviceProfile.setDefault(false);
263   - deviceProfile.setDefaultRuleChainId(new RuleChainId(Uuids.timeBased()));
264   - return deviceProfile;
265   - }
266 282 }
... ...
... ... @@ -20,16 +20,14 @@ import org.junit.After;
20 20 import org.junit.Assert;
21 21 import org.junit.Test;
22 22 import org.springframework.beans.factory.annotation.Autowired;
23   -import org.thingsboard.server.common.data.Device;
24 23 import org.thingsboard.server.common.data.EntityInfo;
25 24 import org.thingsboard.server.common.data.Tenant;
26 25 import org.thingsboard.server.common.data.TenantProfile;
  26 +import org.thingsboard.server.common.data.TenantProfileData;
27 27 import org.thingsboard.server.common.data.id.TenantId;
28 28 import org.thingsboard.server.common.data.page.PageData;
29 29 import org.thingsboard.server.common.data.page.PageLink;
30   -import org.thingsboard.server.dao.exception.DataValidationException;
31 30 import org.thingsboard.server.dao.tenant.TenantProfileService;
32   -import org.thingsboard.server.dao.util.mapping.JacksonUtil;
33 31
34 32 import java.util.ArrayList;
35 33 import java.util.Collections;
... ... @@ -48,7 +46,9 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
48 46 private TenantProfileService tenantProfileService;
49 47
50 48 @After
51   - public void after() {
  49 + @Override
  50 + public void teardown() throws Exception {
  51 + super.teardown();
52 52 tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID);
53 53 }
54 54
... ... @@ -134,6 +134,45 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
134 134 }
135 135
136 136 @Test
  137 + public void testSaveSameTenantProfileWithDifferentIsolatedTbRuleEngine() throws Exception {
  138 + loginSysAdmin();
  139 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
  140 + TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
  141 + savedTenantProfile.setIsolatedTbRuleEngine(true);
  142 + doPost("/api/tenantProfile", savedTenantProfile).andExpect(status().isBadRequest())
  143 + .andExpect(statusReason(containsString("Can't update isolatedTbRuleEngine property")));
  144 + }
  145 +
  146 + @Test
  147 + public void testSaveSameTenantProfileWithDifferentIsolatedTbCore() throws Exception {
  148 + loginSysAdmin();
  149 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
  150 + TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
  151 + savedTenantProfile.setIsolatedTbCore(true);
  152 + doPost("/api/tenantProfile", savedTenantProfile).andExpect(status().isBadRequest())
  153 + .andExpect(statusReason(containsString("Can't update isolatedTbCore property")));
  154 + }
  155 +
  156 + @Test
  157 + public void testDeleteTenantProfileWithExistingTenant() throws Exception {
  158 + loginSysAdmin();
  159 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
  160 + TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
  161 +
  162 + Tenant tenant = new Tenant();
  163 + tenant.setTitle("My tenant with tenant profile");
  164 + tenant.setTenantProfileId(savedTenantProfile.getId());
  165 + Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
  166 +
  167 + doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString())
  168 + .andExpect(status().isBadRequest())
  169 + .andExpect(statusReason(containsString("The tenant profile referenced by the tenants cannot be deleted")));
  170 +
  171 + doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
  172 + .andExpect(status().isOk());
  173 + }
  174 +
  175 + @Test
137 176 public void testDeleteTenantProfile() throws Exception {
138 177 loginSysAdmin();
139 178 TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
... ... @@ -246,7 +285,7 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
246 285 TenantProfile tenantProfile = new TenantProfile();
247 286 tenantProfile.setName(name);
248 287 tenantProfile.setDescription(name + " Test");
249   - tenantProfile.setProfileData(JacksonUtil.OBJECT_MAPPER.createObjectNode());
  288 + tenantProfile.setProfileData(new TenantProfileData());
250 289 tenantProfile.setDefault(false);
251 290 tenantProfile.setIsolatedTbCore(false);
252 291 tenantProfile.setIsolatedTbRuleEngine(false);
... ...
... ... @@ -15,13 +15,22 @@
15 15 */
16 16 package org.thingsboard.server.common.data;
17 17
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import com.fasterxml.jackson.core.JsonProcessingException;
18 20 import lombok.EqualsAndHashCode;
  21 +import lombok.extern.slf4j.Slf4j;
  22 +import org.thingsboard.server.common.data.device.data.DeviceData;
  23 +import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
19 24 import org.thingsboard.server.common.data.id.CustomerId;
20 25 import org.thingsboard.server.common.data.id.DeviceId;
21 26 import org.thingsboard.server.common.data.id.DeviceProfileId;
22 27 import org.thingsboard.server.common.data.id.TenantId;
23 28
  29 +import java.io.ByteArrayInputStream;
  30 +import java.io.IOException;
  31 +
24 32 @EqualsAndHashCode(callSuper = true)
  33 +@Slf4j
25 34 public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implements HasName, HasTenantId, HasCustomerId {
26 35
27 36 private static final long serialVersionUID = 2807343040519543363L;
... ... @@ -32,6 +41,9 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
32 41 private String type;
33 42 private String label;
34 43 private DeviceProfileId deviceProfileId;
  44 + private transient DeviceData deviceData;
  45 + @JsonIgnore
  46 + private byte[] deviceDataBytes;
35 47
36 48 public Device() {
37 49 super();
... ... @@ -49,6 +61,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
49 61 this.type = device.getType();
50 62 this.label = device.getLabel();
51 63 this.deviceProfileId = device.getDeviceProfileId();
  64 + this.setDeviceData(device.getDeviceData());
52 65 }
53 66
54 67 public TenantId getTenantId() {
... ... @@ -100,6 +113,33 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
100 113 this.deviceProfileId = deviceProfileId;
101 114 }
102 115
  116 + public DeviceData getDeviceData() {
  117 + if (deviceData != null) {
  118 + return deviceData;
  119 + } else {
  120 + if (deviceDataBytes != null) {
  121 + try {
  122 + deviceData = mapper.readValue(new ByteArrayInputStream(deviceDataBytes), DeviceData.class);
  123 + } catch (IOException e) {
  124 + log.warn("Can't deserialize device data: ", e);
  125 + return null;
  126 + }
  127 + return deviceData;
  128 + } else {
  129 + return null;
  130 + }
  131 + }
  132 + }
  133 +
  134 + public void setDeviceData(DeviceData data) {
  135 + this.deviceData = data;
  136 + try {
  137 + this.deviceDataBytes = data != null ? mapper.writeValueAsBytes(data) : null;
  138 + } catch (JsonProcessingException e) {
  139 + log.warn("Can't serialize device data: ", e);
  140 + }
  141 + }
  142 +
103 143 @Override
104 144 public String getSearchText() {
105 145 return getName();
... ... @@ -120,6 +160,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
120 160 builder.append(label);
121 161 builder.append(", deviceProfileId=");
122 162 builder.append(deviceProfileId);
  163 + builder.append(", deviceData=");
  164 + builder.append(deviceData);
123 165 builder.append(", additionalInfo=");
124 166 builder.append(getAdditionalInfo());
125 167 builder.append(", createdTime=");
... ...
... ... @@ -16,26 +16,32 @@
16 16 package org.thingsboard.server.common.data;
17 17
18 18 import com.fasterxml.jackson.annotation.JsonIgnore;
19   -import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.fasterxml.jackson.core.JsonProcessingException;
20 20 import lombok.Data;
21 21 import lombok.EqualsAndHashCode;
  22 +import lombok.extern.slf4j.Slf4j;
  23 +import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
22 24 import org.thingsboard.server.common.data.id.DeviceProfileId;
23 25 import org.thingsboard.server.common.data.id.RuleChainId;
24 26 import org.thingsboard.server.common.data.id.TenantId;
25 27
26   -import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.getJson;
27   -import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.setJson;
  28 +import java.io.ByteArrayInputStream;
  29 +import java.io.IOException;
  30 +
  31 +import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.mapper;
28 32
29 33 @Data
30 34 @EqualsAndHashCode(callSuper = true)
  35 +@Slf4j
31 36 public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId {
32 37
33 38 private TenantId tenantId;
34 39 private String name;
35 40 private String description;
36 41 private boolean isDefault;
  42 + private DeviceProfileType type;
37 43 private RuleChainId defaultRuleChainId;
38   - private transient JsonNode profileData;
  44 + private transient DeviceProfileData profileData;
39 45 @JsonIgnore
40 46 private byte[] profileDataBytes;
41 47
... ... @@ -67,12 +73,31 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
67 73 return name;
68 74 }
69 75
70   - public JsonNode getProfileData() {
71   - return getJson(() -> profileData, () -> profileDataBytes);
  76 + public DeviceProfileData getProfileData() {
  77 + if (profileData != null) {
  78 + return profileData;
  79 + } else {
  80 + if (profileDataBytes != null) {
  81 + try {
  82 + profileData = mapper.readValue(new ByteArrayInputStream(profileDataBytes), DeviceProfileData.class);
  83 + } catch (IOException e) {
  84 + log.warn("Can't deserialize device profile data: ", e);
  85 + return null;
  86 + }
  87 + return profileData;
  88 + } else {
  89 + return null;
  90 + }
  91 + }
72 92 }
73 93
74   - public void setProfileData(JsonNode data) {
75   - setJson(data, json -> this.profileData = json, bytes -> this.profileDataBytes = bytes);
  94 + public void setProfileData(DeviceProfileData data) {
  95 + this.profileData = data;
  96 + try {
  97 + this.profileDataBytes = data != null ? mapper.writeValueAsBytes(data) : null;
  98 + } catch (JsonProcessingException e) {
  99 + log.warn("Can't serialize device profile data: ", e);
  100 + }
76 101 }
77 102
78 103 }
... ...
  1 +/**
  2 + * Copyright © 2016-2020 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;
  17 +
  18 +public enum DeviceProfileType {
  19 + DEFAULT,
  20 + LWM2M
  21 +}
... ...
... ... @@ -35,7 +35,7 @@ import java.util.function.Consumer;
35 35 @Slf4j
36 36 public abstract class SearchTextBasedWithAdditionalInfo<I extends UUIDBased> extends SearchTextBased<I> implements HasAdditionalInfo {
37 37
38   - private static final ObjectMapper mapper = new ObjectMapper();
  38 + public static final ObjectMapper mapper = new ObjectMapper();
39 39 private transient JsonNode additionalInfo;
40 40 @JsonIgnore
41 41 private byte[] additionalInfoBytes;
... ... @@ -84,7 +84,7 @@ public abstract class SearchTextBasedWithAdditionalInfo<I extends UUIDBased> ext
84 84 byte[] data = binaryData.get();
85 85 if (data != null) {
86 86 try {
87   - return new ObjectMapper().readTree(new ByteArrayInputStream(data));
  87 + return mapper.readTree(new ByteArrayInputStream(data));
88 88 } catch (IOException e) {
89 89 log.warn("Can't deserialize json data: ", e);
90 90 return null;
... ...
... ... @@ -16,16 +16,20 @@
16 16 package org.thingsboard.server.common.data;
17 17
18 18 import com.fasterxml.jackson.annotation.JsonIgnore;
19   -import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.fasterxml.jackson.core.JsonProcessingException;
20 20 import lombok.Data;
21 21 import lombok.EqualsAndHashCode;
  22 +import lombok.extern.slf4j.Slf4j;
22 23 import org.thingsboard.server.common.data.id.TenantProfileId;
23 24
24   -import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.getJson;
25   -import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.setJson;
  25 +import java.io.ByteArrayInputStream;
  26 +import java.io.IOException;
  27 +
  28 +import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.mapper;
26 29
27 30 @Data
28 31 @EqualsAndHashCode(callSuper = true)
  32 +@Slf4j
29 33 public class TenantProfile extends SearchTextBased<TenantProfileId> implements HasName {
30 34
31 35 private String name;
... ... @@ -33,7 +37,7 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H
33 37 private boolean isDefault;
34 38 private boolean isolatedTbCore;
35 39 private boolean isolatedTbRuleEngine;
36   - private transient JsonNode profileData;
  40 + private transient TenantProfileData profileData;
37 41 @JsonIgnore
38 42 private byte[] profileDataBytes;
39 43
... ... @@ -65,12 +69,31 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H
65 69 return name;
66 70 }
67 71
68   - public JsonNode getProfileData() {
69   - return getJson(() -> profileData, () -> profileDataBytes);
  72 + public TenantProfileData getProfileData() {
  73 + if (profileData != null) {
  74 + return profileData;
  75 + } else {
  76 + if (profileDataBytes != null) {
  77 + try {
  78 + profileData = mapper.readValue(new ByteArrayInputStream(profileDataBytes), TenantProfileData.class);
  79 + } catch (IOException e) {
  80 + log.warn("Can't deserialize tenant profile data: ", e);
  81 + return null;
  82 + }
  83 + return profileData;
  84 + } else {
  85 + return null;
  86 + }
  87 + }
70 88 }
71 89
72   - public void setProfileData(JsonNode data) {
73   - setJson(data, json -> this.profileData = json, bytes -> this.profileDataBytes = bytes);
  90 + public void setProfileData(TenantProfileData data) {
  91 + this.profileData = data;
  92 + try {
  93 + this.profileDataBytes = data != null ? mapper.writeValueAsBytes(data) : null;
  94 + } catch (JsonProcessingException e) {
  95 + log.warn("Can't serialize tenant profile data: ", e);
  96 + }
74 97 }
75 98
76 99 }
... ...
  1 +/**
  2 + * Copyright © 2016-2020 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;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonAnyGetter;
  19 +import com.fasterxml.jackson.annotation.JsonAnySetter;
  20 +import lombok.Data;
  21 +
  22 +import java.util.HashMap;
  23 +import java.util.Map;
  24 +
  25 +@Data
  26 +public class TenantProfileData {
  27 +
  28 + private Map<String, String> properties = new HashMap<>();
  29 +
  30 + @JsonAnyGetter
  31 + public Map<String, String> properties() {
  32 + return this.properties;
  33 + }
  34 +
  35 + @JsonAnySetter
  36 + public void put(String name, String value) {
  37 + this.properties.put(name, value);
  38 + }
  39 +
  40 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 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.device.data;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.DeviceProfileType;
  20 +
  21 +@Data
  22 +public class DefaultDeviceConfiguration implements DeviceConfiguration {
  23 +
  24 + @Override
  25 + public DeviceProfileType getType() {
  26 + return DeviceProfileType.DEFAULT;
  27 + }
  28 +
  29 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 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.device.data;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  20 +import com.fasterxml.jackson.annotation.JsonSubTypes;
  21 +import com.fasterxml.jackson.annotation.JsonTypeInfo;
  22 +import org.thingsboard.server.common.data.DeviceProfileType;
  23 +
  24 +@JsonIgnoreProperties(ignoreUnknown = true)
  25 +@JsonTypeInfo(
  26 + use = JsonTypeInfo.Id.NAME,
  27 + include = JsonTypeInfo.As.PROPERTY,
  28 + property = "type")
  29 +@JsonSubTypes({
  30 + @JsonSubTypes.Type(value = DefaultDeviceConfiguration.class, name = "DEFAULT"),
  31 + @JsonSubTypes.Type(value = Lwm2mDeviceConfiguration.class, name = "LWM2M")})
  32 +public interface DeviceConfiguration {
  33 +
  34 + @JsonIgnore
  35 + DeviceProfileType getType();
  36 +
  37 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 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.device.data;
  17 +
  18 +import lombok.Data;
  19 +
  20 +@Data
  21 +public class DeviceData {
  22 +
  23 + private DeviceConfiguration configuration;
  24 +
  25 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 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.device.data;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.DeviceProfileType;
  20 +
  21 +@Data
  22 +public class Lwm2mDeviceConfiguration implements DeviceConfiguration {
  23 +
  24 + @Override
  25 + public DeviceProfileType getType() {
  26 + return DeviceProfileType.LWM2M;
  27 + }
  28 +
  29 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 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.device.profile;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.DeviceProfileType;
  20 +
  21 +@Data
  22 +public class DefaultDeviceProfileConfiguration implements DeviceProfileConfiguration {
  23 +
  24 + @Override
  25 + public DeviceProfileType getType() {
  26 + return DeviceProfileType.DEFAULT;
  27 + }
  28 +
  29 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 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.device.profile;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  20 +import com.fasterxml.jackson.annotation.JsonSubTypes;
  21 +import com.fasterxml.jackson.annotation.JsonTypeInfo;
  22 +import org.thingsboard.server.common.data.DeviceProfileType;
  23 +
  24 +@JsonIgnoreProperties(ignoreUnknown = true)
  25 +@JsonTypeInfo(
  26 + use = JsonTypeInfo.Id.NAME,
  27 + include = JsonTypeInfo.As.PROPERTY,
  28 + property = "type")
  29 +@JsonSubTypes({
  30 + @JsonSubTypes.Type(value = DefaultDeviceProfileConfiguration.class, name = "DEFAULT"),
  31 + @JsonSubTypes.Type(value = Lwm2mDeviceProfileConfiguration.class, name = "LWM2M")})
  32 +public interface DeviceProfileConfiguration {
  33 +
  34 + @JsonIgnore
  35 + DeviceProfileType getType();
  36 +
  37 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 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.device.profile;
  17 +
  18 +import lombok.Data;
  19 +
  20 +@Data
  21 +public class DeviceProfileData {
  22 +
  23 + private DeviceProfileConfiguration configuration;
  24 +
  25 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 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.device.profile;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.DeviceProfileType;
  20 +
  21 +@Data
  22 +public class Lwm2mDeviceProfileConfiguration implements DeviceProfileConfiguration {
  23 +
  24 + @Override
  25 + public DeviceProfileType getType() {
  26 + return DeviceProfileType.LWM2M;
  27 + }
  28 +
  29 +}
... ...
... ... @@ -24,8 +24,11 @@ import org.springframework.cache.CacheManager;
24 24 import org.springframework.cache.annotation.Cacheable;
25 25 import org.springframework.stereotype.Service;
26 26 import org.thingsboard.server.common.data.DeviceProfile;
  27 +import org.thingsboard.server.common.data.DeviceProfileType;
27 28 import org.thingsboard.server.common.data.EntityInfo;
28 29 import org.thingsboard.server.common.data.Tenant;
  30 +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
  31 +import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
29 32 import org.thingsboard.server.common.data.id.DeviceProfileId;
30 33 import org.thingsboard.server.common.data.id.TenantId;
31 34 import org.thingsboard.server.common.data.page.PageData;
... ... @@ -113,8 +116,17 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
113 116 }
114 117
115 118 private void removeDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) {
  119 + try {
  120 + deviceProfileDao.removeById(tenantId, deviceProfileId.getId());
  121 + } catch (Exception t) {
  122 + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
  123 + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_device_profile")) {
  124 + throw new DataValidationException("The device profile referenced by the devices cannot be deleted!");
  125 + } else {
  126 + throw t;
  127 + }
  128 + }
116 129 deleteEntityRelations(tenantId, deviceProfileId);
117   - deviceProfileDao.removeById(tenantId, deviceProfileId.getId());
118 130 Cache cache = cacheManager.getCache(DEVICE_PROFILE_CACHE);
119 131 cache.evict(Collections.singletonList(deviceProfileId.getId()));
120 132 cache.evict(Arrays.asList("info", deviceProfileId.getId()));
... ... @@ -144,8 +156,12 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
144 156 deviceProfile.setTenantId(tenantId);
145 157 deviceProfile.setDefault(true);
146 158 deviceProfile.setName("Default");
  159 + deviceProfile.setType(DeviceProfileType.DEFAULT);
147 160 deviceProfile.setDescription("Default device profile");
148   - deviceProfile.setProfileData(JacksonUtil.OBJECT_MAPPER.createObjectNode());
  161 + DeviceProfileData deviceProfileData = new DeviceProfileData();
  162 + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration();
  163 + deviceProfileData.setConfiguration(configuration);
  164 + deviceProfile.setProfileData(deviceProfileData);
149 165 return saveDeviceProfile(deviceProfile);
150 166 }
151 167
... ... @@ -226,6 +242,16 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
226 242 }
227 243 }
228 244 }
  245 +
  246 + @Override
  247 + protected void validateUpdate(TenantId tenantId, DeviceProfile deviceProfile) {
  248 + DeviceProfile old = deviceProfileDao.findById(deviceProfile.getTenantId(), deviceProfile.getId().getId());
  249 + if (old == null) {
  250 + throw new DataValidationException("Can't update non existing device profile!");
  251 + } else if (!old.getType().equals(deviceProfile.getType())) {
  252 + throw new DataValidationException("Changing type of device profile is prohibited!");
  253 + }
  254 + }
229 255 };
230 256
231 257 private PaginatedRemover<TenantId, DeviceProfile> tenantDeviceProfilesRemover =
... ...
... ... @@ -41,6 +41,8 @@ import org.thingsboard.server.common.data.EntityType;
41 41 import org.thingsboard.server.common.data.EntityView;
42 42 import org.thingsboard.server.common.data.Tenant;
43 43 import org.thingsboard.server.common.data.device.DeviceSearchQuery;
  44 +import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration;
  45 +import org.thingsboard.server.common.data.device.data.DeviceData;
44 46 import org.thingsboard.server.common.data.id.CustomerId;
45 47 import org.thingsboard.server.common.data.id.DeviceId;
46 48 import org.thingsboard.server.common.data.id.DeviceProfileId;
... ... @@ -168,6 +170,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
168 170 if (device.getDeviceProfileId() == null) {
169 171 EntityInfo deviceProfile = this.deviceProfileService.findDefaultDeviceProfileInfo(device.getTenantId());
170 172 device.setDeviceProfileId(new DeviceProfileId(deviceProfile.getId().getId()));
  173 + DeviceData deviceData = new DeviceData();
  174 + deviceData.setConfiguration(new DefaultDeviceConfiguration());
  175 + device.setDeviceData(deviceData);
171 176 }
172 177 savedDevice = deviceDao.save(device.getTenantId(), device);
173 178 } catch (Exception t) {
... ... @@ -411,6 +416,12 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
411 416
412 417 @Override
413 418 protected void validateUpdate(TenantId tenantId, Device device) {
  419 + Device old = deviceDao.findById(device.getTenantId(), device.getId().getId());
  420 + if (old == null) {
  421 + throw new DataValidationException("Can't update non existing device!");
  422 + } else if (!old.getDeviceProfileId().equals(device.getDeviceProfileId())) {
  423 + throw new DataValidationException("Changing device profile is prohibited!");
  424 + }
414 425 }
415 426
416 427 @Override
... ...
... ... @@ -152,6 +152,8 @@ public class ModelConstants {
152 152 public static final String DEVICE_LABEL_PROPERTY = "label";
153 153 public static final String DEVICE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
154 154 public static final String DEVICE_DEVICE_PROFILE_ID_PROPERTY = "device_profile_id";
  155 + public static final String DEVICE_DEVICE_DATA_PROPERTY = "device_data";
  156 +
155 157 public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text";
156 158 public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text";
157 159 public static final String DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_and_search_text";
... ... @@ -165,6 +167,7 @@ public class ModelConstants {
165 167 public static final String DEVICE_PROFILE_COLUMN_FAMILY_NAME = "device_profile";
166 168 public static final String DEVICE_PROFILE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
167 169 public static final String DEVICE_PROFILE_NAME_PROPERTY = "name";
  170 + public static final String DEVICE_PROFILE_TYPE_PROPERTY = "type";
168 171 public static final String DEVICE_PROFILE_PROFILE_DATA_PROPERTY = "profile_data";
169 172 public static final String DEVICE_PROFILE_DESCRIPTION_PROPERTY = "description";
170 173 public static final String DEVICE_PROFILE_IS_DEFAULT_PROPERTY = "is_default";
... ...
... ... @@ -16,11 +16,13 @@
16 16 package org.thingsboard.server.dao.model.sql;
17 17
18 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 20 import lombok.Data;
20 21 import lombok.EqualsAndHashCode;
21 22 import org.hibernate.annotations.Type;
22 23 import org.hibernate.annotations.TypeDef;
23 24 import org.thingsboard.server.common.data.Device;
  25 +import org.thingsboard.server.common.data.device.data.DeviceData;
24 26 import org.thingsboard.server.common.data.id.CustomerId;
25 27 import org.thingsboard.server.common.data.id.DeviceId;
26 28 import org.thingsboard.server.common.data.id.DeviceProfileId;
... ... @@ -28,6 +30,7 @@ import org.thingsboard.server.common.data.id.TenantId;
28 30 import org.thingsboard.server.dao.model.BaseSqlEntity;
29 31 import org.thingsboard.server.dao.model.ModelConstants;
30 32 import org.thingsboard.server.dao.model.SearchTextEntity;
  33 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
31 34 import org.thingsboard.server.dao.util.mapping.JsonStringType;
32 35
33 36 import javax.persistence.Column;
... ... @@ -65,6 +68,10 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
65 68 @Column(name = ModelConstants.DEVICE_DEVICE_PROFILE_ID_PROPERTY, columnDefinition = "uuid")
66 69 private UUID deviceProfileId;
67 70
  71 + @Type(type = "json")
  72 + @Column(name = ModelConstants.DEVICE_DEVICE_DATA_PROPERTY)
  73 + private JsonNode deviceData;
  74 +
68 75 public AbstractDeviceEntity() {
69 76 super();
70 77 }
... ... @@ -83,6 +90,7 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
83 90 if (device.getDeviceProfileId() != null) {
84 91 this.deviceProfileId = device.getDeviceProfileId().getId();
85 92 }
  93 + this.deviceData = JacksonUtil.convertValue(device.getDeviceData(), ObjectNode.class);
86 94 this.name = device.getName();
87 95 this.type = device.getType();
88 96 this.label = device.getLabel();
... ... @@ -95,6 +103,7 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
95 103 this.tenantId = deviceEntity.getTenantId();
96 104 this.customerId = deviceEntity.getCustomerId();
97 105 this.deviceProfileId = deviceEntity.getDeviceProfileId();
  106 + this.deviceData = deviceEntity.getDeviceData();
98 107 this.type = deviceEntity.getType();
99 108 this.name = deviceEntity.getName();
100 109 this.label = deviceEntity.getLabel();
... ... @@ -124,6 +133,7 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
124 133 if (deviceProfileId != null) {
125 134 device.setDeviceProfileId(new DeviceProfileId(deviceProfileId));
126 135 }
  136 + device.setDeviceData(JacksonUtil.convertValue(deviceData, DeviceData.class));
127 137 device.setName(name);
128 138 device.setType(type);
129 139 device.setLabel(label);
... ...
... ... @@ -16,21 +16,27 @@
16 16 package org.thingsboard.server.dao.model.sql;
17 17
18 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 20 import lombok.Data;
20 21 import lombok.EqualsAndHashCode;
21 22 import org.hibernate.annotations.Type;
22 23 import org.hibernate.annotations.TypeDef;
23 24 import org.thingsboard.server.common.data.DeviceProfile;
  25 +import org.thingsboard.server.common.data.DeviceProfileType;
  26 +import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
24 27 import org.thingsboard.server.common.data.id.DeviceProfileId;
25 28 import org.thingsboard.server.common.data.id.RuleChainId;
26 29 import org.thingsboard.server.common.data.id.TenantId;
27 30 import org.thingsboard.server.dao.model.BaseSqlEntity;
28 31 import org.thingsboard.server.dao.model.ModelConstants;
29 32 import org.thingsboard.server.dao.model.SearchTextEntity;
  33 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
30 34 import org.thingsboard.server.dao.util.mapping.JsonStringType;
31 35
32 36 import javax.persistence.Column;
33 37 import javax.persistence.Entity;
  38 +import javax.persistence.EnumType;
  39 +import javax.persistence.Enumerated;
34 40 import javax.persistence.Table;
35 41 import java.util.UUID;
36 42
... ... @@ -47,6 +53,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
47 53 @Column(name = ModelConstants.DEVICE_PROFILE_NAME_PROPERTY)
48 54 private String name;
49 55
  56 + @Enumerated(EnumType.STRING)
  57 + @Column(name = ModelConstants.DEVICE_PROFILE_TYPE_PROPERTY)
  58 + private DeviceProfileType type;
  59 +
50 60 @Column(name = ModelConstants.DEVICE_PROFILE_DESCRIPTION_PROPERTY)
51 61 private String description;
52 62
... ... @@ -76,9 +86,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
76 86 }
77 87 this.setCreatedTime(deviceProfile.getCreatedTime());
78 88 this.name = deviceProfile.getName();
  89 + this.type = deviceProfile.getType();
79 90 this.description = deviceProfile.getDescription();
80 91 this.isDefault = deviceProfile.isDefault();
81   - this.profileData = deviceProfile.getProfileData();
  92 + this.profileData = JacksonUtil.convertValue(deviceProfile.getProfileData(), ObjectNode.class);
82 93 if (deviceProfile.getDefaultRuleChainId() != null) {
83 94 this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId().getId();
84 95 }
... ... @@ -106,9 +117,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
106 117 deviceProfile.setTenantId(new TenantId(tenantId));
107 118 }
108 119 deviceProfile.setName(name);
  120 + deviceProfile.setType(type);
109 121 deviceProfile.setDescription(description);
110 122 deviceProfile.setDefault(isDefault);
111   - deviceProfile.setProfileData(profileData);
  123 + deviceProfile.setProfileData(JacksonUtil.convertValue(profileData, DeviceProfileData.class));
112 124 if (defaultRuleChainId != null) {
113 125 deviceProfile.setDefaultRuleChainId(new RuleChainId(defaultRuleChainId));
114 126 }
... ...
... ... @@ -16,15 +16,18 @@
16 16 package org.thingsboard.server.dao.model.sql;
17 17
18 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 20 import lombok.Data;
20 21 import lombok.EqualsAndHashCode;
21 22 import org.hibernate.annotations.Type;
22 23 import org.hibernate.annotations.TypeDef;
23 24 import org.thingsboard.server.common.data.TenantProfile;
  25 +import org.thingsboard.server.common.data.TenantProfileData;
24 26 import org.thingsboard.server.common.data.id.TenantProfileId;
25 27 import org.thingsboard.server.dao.model.BaseSqlEntity;
26 28 import org.thingsboard.server.dao.model.ModelConstants;
27 29 import org.thingsboard.server.dao.model.SearchTextEntity;
  30 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
28 31 import org.thingsboard.server.dao.util.mapping.JsonStringType;
29 32
30 33 import javax.persistence.Column;
... ... @@ -74,7 +77,7 @@ public final class TenantProfileEntity extends BaseSqlEntity<TenantProfile> impl
74 77 this.isDefault = tenantProfile.isDefault();
75 78 this.isolatedTbCore = tenantProfile.isIsolatedTbCore();
76 79 this.isolatedTbRuleEngine = tenantProfile.isIsolatedTbRuleEngine();
77   - this.profileData = tenantProfile.getProfileData();
  80 + this.profileData = JacksonUtil.convertValue(tenantProfile.getProfileData(), ObjectNode.class);
78 81 }
79 82
80 83 @Override
... ... @@ -100,9 +103,8 @@ public final class TenantProfileEntity extends BaseSqlEntity<TenantProfile> impl
100 103 tenantProfile.setDefault(isDefault);
101 104 tenantProfile.setIsolatedTbCore(isolatedTbCore);
102 105 tenantProfile.setIsolatedTbRuleEngine(isolatedTbRuleEngine);
103   - tenantProfile.setProfileData(profileData);
  106 + tenantProfile.setProfileData(JacksonUtil.convertValue(profileData, TenantProfileData.class));
104 107 return tenantProfile;
105 108 }
106 109
107   -
108 110 }
... ...
... ... @@ -23,8 +23,10 @@ import org.springframework.cache.Cache;
23 23 import org.springframework.cache.CacheManager;
24 24 import org.springframework.cache.annotation.Cacheable;
25 25 import org.springframework.stereotype.Service;
  26 +import org.thingsboard.server.common.data.DeviceProfile;
26 27 import org.thingsboard.server.common.data.EntityInfo;
27 28 import org.thingsboard.server.common.data.TenantProfile;
  29 +import org.thingsboard.server.common.data.TenantProfileData;
28 30 import org.thingsboard.server.common.data.id.TenantId;
29 31 import org.thingsboard.server.common.data.id.TenantProfileId;
30 32 import org.thingsboard.server.common.data.page.PageData;
... ... @@ -107,8 +109,17 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T
107 109 }
108 110
109 111 private void removeTenantProfile(TenantId tenantId, TenantProfileId tenantProfileId) {
  112 + try {
  113 + tenantProfileDao.removeById(tenantId, tenantProfileId.getId());
  114 + } catch (Exception t) {
  115 + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
  116 + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_tenant_profile")) {
  117 + throw new DataValidationException("The tenant profile referenced by the tenants cannot be deleted!");
  118 + } else {
  119 + throw t;
  120 + }
  121 + }
110 122 deleteEntityRelations(tenantId, tenantProfileId);
111   - tenantProfileDao.removeById(tenantId, tenantProfileId.getId());
112 123 Cache cache = cacheManager.getCache(TENANT_PROFILE_CACHE);
113 124 cache.evict(Collections.singletonList(tenantProfileId.getId()));
114 125 cache.evict(Arrays.asList("info", tenantProfileId.getId()));
... ... @@ -136,7 +147,7 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T
136 147 defaultTenantProfile = new TenantProfile();
137 148 defaultTenantProfile.setDefault(true);
138 149 defaultTenantProfile.setName("Default");
139   - defaultTenantProfile.setProfileData(JacksonUtil.OBJECT_MAPPER.createObjectNode());
  150 + defaultTenantProfile.setProfileData(new TenantProfileData());
140 151 defaultTenantProfile.setDescription("Default tenant profile");
141 152 defaultTenantProfile.setIsolatedTbCore(false);
142 153 defaultTenantProfile.setIsolatedTbRuleEngine(false);
... ... @@ -211,6 +222,18 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T
211 222 }
212 223 }
213 224 }
  225 +
  226 + @Override
  227 + protected void validateUpdate(TenantId tenantId, TenantProfile tenantProfile) {
  228 + TenantProfile old = tenantProfileDao.findById(TenantId.SYS_TENANT_ID, tenantProfile.getId().getId());
  229 + if (old == null) {
  230 + throw new DataValidationException("Can't update non existing tenant profile!");
  231 + } else if (old.isIsolatedTbRuleEngine() != tenantProfile.isIsolatedTbRuleEngine()) {
  232 + throw new DataValidationException("Can't update isolatedTbRuleEngine property!");
  233 + } else if (old.isIsolatedTbCore() != tenantProfile.isIsolatedTbCore()) {
  234 + throw new DataValidationException("Can't update isolatedTbCore property!");
  235 + }
  236 + }
214 237 };
215 238
216 239 private PaginatedRemover<String, TenantProfile> tenantProfilesRemover =
... ...
... ... @@ -163,12 +163,6 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe
163 163 if (old == null) {
164 164 throw new DataValidationException("Can't update non existing tenant!");
165 165 }
166   - // TODO: Move validation to tenant profile
167   - /* else if (old.isIsolatedTbRuleEngine() != tenant.isIsolatedTbRuleEngine()) {
168   - throw new DataValidationException("Can't update isolatedTbRuleEngine property!");
169   - } else if (old.isIsolatedTbCore() != tenant.isIsolatedTbCore()) {
170   - throw new DataValidationException("Can't update isolatedTbCore property!");
171   - } */
172 166 }
173 167 };
174 168
... ...
... ... @@ -30,7 +30,7 @@ public class JacksonUtil {
30 30
31 31 public static <T> T convertValue(Object fromValue, Class<T> toValueType) {
32 32 try {
33   - return OBJECT_MAPPER.convertValue(fromValue, toValueType);
  33 + return fromValue != null ? OBJECT_MAPPER.convertValue(fromValue, toValueType) : null;
34 34 } catch (IllegalArgumentException e) {
35 35 throw new IllegalArgumentException("The given object value: "
36 36 + fromValue + " cannot be converted to " + toValueType);
... ... @@ -39,7 +39,7 @@ public class JacksonUtil {
39 39
40 40 public static <T> T fromString(String string, Class<T> clazz) {
41 41 try {
42   - return OBJECT_MAPPER.readValue(string, clazz);
  42 + return string != null ? OBJECT_MAPPER.readValue(string, clazz) : null;
43 43 } catch (IOException e) {
44 44 throw new IllegalArgumentException("The given string value: "
45 45 + string + " cannot be transformed to Json object");
... ... @@ -48,7 +48,7 @@ public class JacksonUtil {
48 48
49 49 public static String toString(Object value) {
50 50 try {
51   - return OBJECT_MAPPER.writeValueAsString(value);
  51 + return value != null ? OBJECT_MAPPER.writeValueAsString(value) : null;
52 52 } catch (JsonProcessingException e) {
53 53 throw new IllegalArgumentException("The given Json object value: "
54 54 + value + " cannot be transformed to a String");
... ...
... ... @@ -122,24 +122,12 @@ CREATE TABLE IF NOT EXISTS dashboard (
122 122 title varchar(255)
123 123 );
124 124
125   -CREATE TABLE IF NOT EXISTS device (
126   - id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY,
127   - created_time bigint NOT NULL,
128   - additional_info varchar,
129   - customer_id uuid,
130   - device_profile_id uuid NOT NULL,
131   - type varchar(255),
132   - name varchar(255),
133   - label varchar(255),
134   - search_text varchar(255),
135   - tenant_id uuid,
136   - CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name)
137   -);
138 125
139 126 CREATE TABLE IF NOT EXISTS device_profile (
140 127 id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY,
141 128 created_time bigint NOT NULL,
142 129 name varchar(255),
  130 + type varchar(255),
143 131 profile_data varchar,
144 132 description varchar,
145 133 search_text varchar(255),
... ... @@ -149,6 +137,22 @@ CREATE TABLE IF NOT EXISTS device_profile (
149 137 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name)
150 138 );
151 139
  140 +CREATE TABLE IF NOT EXISTS device (
  141 + id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY,
  142 + created_time bigint NOT NULL,
  143 + additional_info varchar,
  144 + customer_id uuid,
  145 + device_profile_id uuid NOT NULL,
  146 + device_data varchar,
  147 + type varchar(255),
  148 + name varchar(255),
  149 + label varchar(255),
  150 + search_text varchar(255),
  151 + tenant_id uuid,
  152 + CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
  153 + CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id)
  154 +);
  155 +
152 156 CREATE TABLE IF NOT EXISTS device_credentials (
153 157 id uuid NOT NULL CONSTRAINT device_credentials_pkey PRIMARY KEY,
154 158 created_time bigint NOT NULL,
... ... @@ -197,6 +201,19 @@ CREATE TABLE IF NOT EXISTS tb_user (
197 201 tenant_id uuid
198 202 );
199 203
  204 +CREATE TABLE IF NOT EXISTS tenant_profile (
  205 + id uuid NOT NULL CONSTRAINT tenant_profile_pkey PRIMARY KEY,
  206 + created_time bigint NOT NULL,
  207 + name varchar(255),
  208 + profile_data varchar,
  209 + description varchar,
  210 + search_text varchar(255),
  211 + is_default boolean,
  212 + isolated_tb_core boolean,
  213 + isolated_tb_rule_engine boolean,
  214 + CONSTRAINT tenant_profile_name_unq_key UNIQUE (name)
  215 +);
  216 +
200 217 CREATE TABLE IF NOT EXISTS tenant (
201 218 id uuid NOT NULL CONSTRAINT tenant_pkey PRIMARY KEY,
202 219 created_time bigint NOT NULL,
... ... @@ -214,20 +231,8 @@ CREATE TABLE IF NOT EXISTS tenant (
214 231 title varchar(255),
215 232 zip varchar(255),
216 233 isolated_tb_core boolean,
217   - isolated_tb_rule_engine boolean
218   -);
219   -
220   -CREATE TABLE IF NOT EXISTS tenant_profile (
221   - id uuid NOT NULL CONSTRAINT tenant_profile_pkey PRIMARY KEY,
222   - created_time bigint NOT NULL,
223   - name varchar(255),
224   - profile_data varchar,
225   - description varchar,
226   - search_text varchar(255),
227   - is_default boolean,
228   - isolated_tb_core boolean,
229 234 isolated_tb_rule_engine boolean,
230   - CONSTRAINT tenant_profile_name_unq_key UNIQUE (name)
  235 + CONSTRAINT fk_tenant_profile FOREIGN KEY (tenant_profile_id) REFERENCES tenant_profile(id)
231 236 );
232 237
233 238 CREATE TABLE IF NOT EXISTS user_credentials (
... ...
... ... @@ -139,24 +139,12 @@ CREATE TABLE IF NOT EXISTS dashboard (
139 139 title varchar(255)
140 140 );
141 141
142   -CREATE TABLE IF NOT EXISTS device (
143   - id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY,
144   - created_time bigint NOT NULL,
145   - additional_info varchar,
146   - customer_id uuid,
147   - device_profile_id uuid NOT NULL,
148   - type varchar(255),
149   - name varchar(255),
150   - label varchar(255),
151   - search_text varchar(255),
152   - tenant_id uuid,
153   - CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name)
154   -);
155 142
156 143 CREATE TABLE IF NOT EXISTS device_profile (
157 144 id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY,
158 145 created_time bigint NOT NULL,
159 146 name varchar(255),
  147 + type varchar(255),
160 148 profile_data varchar,
161 149 description varchar,
162 150 search_text varchar(255),
... ... @@ -166,6 +154,22 @@ CREATE TABLE IF NOT EXISTS device_profile (
166 154 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name)
167 155 );
168 156
  157 +CREATE TABLE IF NOT EXISTS device (
  158 + id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY,
  159 + created_time bigint NOT NULL,
  160 + additional_info varchar,
  161 + customer_id uuid,
  162 + device_profile_id uuid NOT NULL,
  163 + device_data varchar,
  164 + type varchar(255),
  165 + name varchar(255),
  166 + label varchar(255),
  167 + search_text varchar(255),
  168 + tenant_id uuid,
  169 + CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
  170 + CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id)
  171 +);
  172 +
169 173 CREATE TABLE IF NOT EXISTS device_credentials (
170 174 id uuid NOT NULL CONSTRAINT device_credentials_pkey PRIMARY KEY,
171 175 created_time bigint NOT NULL,
... ... @@ -221,6 +225,19 @@ CREATE TABLE IF NOT EXISTS tb_user (
221 225 tenant_id uuid
222 226 );
223 227
  228 +CREATE TABLE IF NOT EXISTS tenant_profile (
  229 + id uuid NOT NULL CONSTRAINT tenant_profile_pkey PRIMARY KEY,
  230 + created_time bigint NOT NULL,
  231 + name varchar(255),
  232 + profile_data varchar,
  233 + description varchar,
  234 + search_text varchar(255),
  235 + is_default boolean,
  236 + isolated_tb_core boolean,
  237 + isolated_tb_rule_engine boolean,
  238 + CONSTRAINT tenant_profile_name_unq_key UNIQUE (name)
  239 +);
  240 +
224 241 CREATE TABLE IF NOT EXISTS tenant (
225 242 id uuid NOT NULL CONSTRAINT tenant_pkey PRIMARY KEY,
226 243 created_time bigint NOT NULL,
... ... @@ -238,20 +255,8 @@ CREATE TABLE IF NOT EXISTS tenant (
238 255 title varchar(255),
239 256 zip varchar(255),
240 257 isolated_tb_core boolean,
241   - isolated_tb_rule_engine boolean
242   -);
243   -
244   -CREATE TABLE IF NOT EXISTS tenant_profile (
245   - id uuid NOT NULL CONSTRAINT tenant_profile_pkey PRIMARY KEY,
246   - created_time bigint NOT NULL,
247   - name varchar(255),
248   - profile_data varchar,
249   - description varchar,
250   - search_text varchar(255),
251   - is_default boolean,
252   - isolated_tb_core boolean,
253 258 isolated_tb_rule_engine boolean,
254   - CONSTRAINT tenant_profile_name_unq_key UNIQUE (name)
  259 + CONSTRAINT fk_tenant_profile FOREIGN KEY (tenant_profile_id) REFERENCES tenant_profile(id)
255 260 );
256 261
257 262 CREATE TABLE IF NOT EXISTS user_credentials (
... ...
... ... @@ -28,10 +28,15 @@ import org.springframework.test.context.ContextConfiguration;
28 28 import org.springframework.test.context.junit4.SpringRunner;
29 29 import org.springframework.test.context.support.AnnotationConfigContextLoader;
30 30 import org.thingsboard.server.common.data.BaseData;
  31 +import org.thingsboard.server.common.data.DeviceProfile;
  32 +import org.thingsboard.server.common.data.DeviceProfileType;
31 33 import org.thingsboard.server.common.data.EntityType;
32 34 import org.thingsboard.server.common.data.Event;
  35 +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
  36 +import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
33 37 import org.thingsboard.server.common.data.id.EntityId;
34 38 import org.thingsboard.server.common.data.id.HasId;
  39 +import org.thingsboard.server.common.data.id.RuleChainId;
35 40 import org.thingsboard.server.common.data.id.TenantId;
36 41 import org.thingsboard.server.common.data.id.UUIDBased;
37 42 import org.thingsboard.server.dao.alarm.AlarmService;
... ... @@ -187,4 +192,19 @@ public abstract class AbstractServiceTest {
187 192 return new AuditLogLevelFilter(mask);
188 193 }
189 194
  195 + protected DeviceProfile createDeviceProfile(TenantId tenantId, String name) {
  196 + DeviceProfile deviceProfile = new DeviceProfile();
  197 + deviceProfile.setTenantId(tenantId);
  198 + deviceProfile.setName(name);
  199 + deviceProfile.setType(DeviceProfileType.DEFAULT);
  200 + deviceProfile.setDescription(name + " Test");
  201 + DeviceProfileData deviceProfileData = new DeviceProfileData();
  202 + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration();
  203 + deviceProfileData.setConfiguration(configuration);
  204 + deviceProfile.setProfileData(deviceProfileData);
  205 + deviceProfile.setDefault(false);
  206 + deviceProfile.setDefaultRuleChainId(new RuleChainId(Uuids.timeBased()));
  207 + return deviceProfile;
  208 + }
  209 +
190 210 }
... ...
... ... @@ -15,20 +15,19 @@
15 15 */
16 16 package org.thingsboard.server.dao.service;
17 17
18   -import com.datastax.oss.driver.api.core.uuid.Uuids;
19 18 import org.junit.After;
20 19 import org.junit.Assert;
21 20 import org.junit.Before;
22 21 import org.junit.Test;
  22 +import org.thingsboard.server.common.data.Device;
23 23 import org.thingsboard.server.common.data.DeviceProfile;
  24 +import org.thingsboard.server.common.data.DeviceProfileType;
24 25 import org.thingsboard.server.common.data.EntityInfo;
25 26 import org.thingsboard.server.common.data.Tenant;
26   -import org.thingsboard.server.common.data.id.RuleChainId;
27 27 import org.thingsboard.server.common.data.id.TenantId;
28 28 import org.thingsboard.server.common.data.page.PageData;
29 29 import org.thingsboard.server.common.data.page.PageLink;
30 30 import org.thingsboard.server.dao.exception.DataValidationException;
31   -import org.thingsboard.server.dao.util.mapping.JacksonUtil;
32 31
33 32 import java.util.ArrayList;
34 33 import java.util.Collections;
... ... @@ -58,7 +57,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
58 57
59 58 @Test
60 59 public void testSaveDeviceProfile() {
61   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  60 + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile");
62 61 DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
63 62 Assert.assertNotNull(savedDeviceProfile);
64 63 Assert.assertNotNull(savedDeviceProfile.getId());
... ... @@ -76,7 +75,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
76 75
77 76 @Test
78 77 public void testFindDeviceProfileById() {
79   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  78 + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile");
80 79 DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
81 80 DeviceProfile foundDeviceProfile = deviceProfileService.findDeviceProfileById(tenantId, savedDeviceProfile.getId());
82 81 Assert.assertNotNull(foundDeviceProfile);
... ... @@ -85,7 +84,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
85 84
86 85 @Test
87 86 public void testFindDeviceProfileInfoById() {
88   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  87 + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile");
89 88 DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
90 89 EntityInfo foundDeviceProfileInfo = deviceProfileService.findDeviceProfileInfoById(tenantId, savedDeviceProfile.getId());
91 90 Assert.assertNotNull(foundDeviceProfileInfo);
... ... @@ -111,8 +110,8 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
111 110
112 111 @Test
113 112 public void testSetDefaultDeviceProfile() {
114   - DeviceProfile deviceProfile1 = this.createDeviceProfile("Device Profile 1");
115   - DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile 2");
  113 + DeviceProfile deviceProfile1 = this.createDeviceProfile(tenantId,"Device Profile 1");
  114 + DeviceProfile deviceProfile2 = this.createDeviceProfile(tenantId,"Device Profile 2");
116 115
117 116 DeviceProfile savedDeviceProfile1 = deviceProfileService.saveDeviceProfile(deviceProfile1);
118 117 DeviceProfile savedDeviceProfile2 = deviceProfileService.saveDeviceProfile(deviceProfile2);
... ... @@ -138,15 +137,36 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
138 137
139 138 @Test(expected = DataValidationException.class)
140 139 public void testSaveDeviceProfileWithSameName() {
141   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  140 + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile");
142 141 deviceProfileService.saveDeviceProfile(deviceProfile);
143   - DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile");
  142 + DeviceProfile deviceProfile2 = this.createDeviceProfile(tenantId,"Device Profile");
144 143 deviceProfileService.saveDeviceProfile(deviceProfile2);
145 144 }
146 145
  146 + @Test(expected = DataValidationException.class)
  147 + public void testSaveSameDeviceProfileWithDifferentType() {
  148 + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile");
  149 + DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
  150 + savedDeviceProfile.setType(DeviceProfileType.LWM2M);
  151 + deviceProfileService.saveDeviceProfile(savedDeviceProfile);
  152 + }
  153 +
  154 + @Test(expected = DataValidationException.class)
  155 + public void testDeleteDeviceProfileWithExistingDevice() {
  156 + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile");
  157 + DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
  158 + Device device = new Device();
  159 + device.setTenantId(tenantId);
  160 + device.setName("Test device");
  161 + device.setType("default");
  162 + device.setDeviceProfileId(savedDeviceProfile.getId());
  163 + deviceService.saveDevice(device);
  164 + deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId());
  165 + }
  166 +
147 167 @Test
148 168 public void testDeleteDeviceProfile() {
149   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  169 + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile");
150 170 DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
151 171 deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId());
152 172 DeviceProfile foundDeviceProfile = deviceProfileService.findDeviceProfileById(tenantId, savedDeviceProfile.getId());
... ... @@ -164,7 +184,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
164 184 deviceProfiles.addAll(pageData.getData());
165 185
166 186 for (int i=0;i<28;i++) {
167   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i);
  187 + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile"+i);
168 188 deviceProfiles.add(deviceProfileService.saveDeviceProfile(deviceProfile));
169 189 }
170 190
... ... @@ -206,7 +226,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
206 226 deviceProfiles.addAll(deviceProfilePageData.getData());
207 227
208 228 for (int i=0;i<28;i++) {
209   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i);
  229 + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile"+i);
210 230 deviceProfiles.add(deviceProfileService.saveDeviceProfile(deviceProfile));
211 231 }
212 232
... ... @@ -242,15 +262,6 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
242 262 Assert.assertEquals(1, pageData.getTotalElements());
243 263 }
244 264
245   - private DeviceProfile createDeviceProfile(String name) {
246   - DeviceProfile deviceProfile = new DeviceProfile();
247   - deviceProfile.setTenantId(tenantId);
248   - deviceProfile.setName(name);
249   - deviceProfile.setDescription(name + " Test");
250   - deviceProfile.setProfileData(JacksonUtil.OBJECT_MAPPER.createObjectNode());
251   - deviceProfile.setDefault(false);
252   - deviceProfile.setDefaultRuleChainId(new RuleChainId(Uuids.timeBased()));
253   - return deviceProfile;
254   - }
  265 +
255 266
256 267 }
... ...
... ... @@ -127,6 +127,23 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
127 127 deviceService.deleteDevice(tenantId, device.getId());
128 128 }
129 129 }
  130 +
  131 + @Test(expected = DataValidationException.class)
  132 + public void testSaveSameDeviceWithDifferentDeviceProfileId() {
  133 + Device device = new Device();
  134 + device.setName("My device");
  135 + device.setType("default");
  136 + device.setTenantId(tenantId);
  137 + device = deviceService.saveDevice(device);
  138 + DeviceProfile deviceProfile2 = this.createDeviceProfile(tenantId,"Device Profile 2");
  139 + DeviceProfile savedDeviceProfile2 = deviceProfileService.saveDeviceProfile(deviceProfile2);
  140 + device.setDeviceProfileId(savedDeviceProfile2.getId());
  141 + try {
  142 + deviceService.saveDevice(device);
  143 + } finally {
  144 + deviceService.deleteDevice(tenantId, device.getId());
  145 + }
  146 + }
130 147
131 148 @Test(expected = DataValidationException.class)
132 149 public void testAssignDeviceToCustomerFromDifferentTenant() {
... ...
... ... @@ -19,7 +19,9 @@ import org.junit.After;
19 19 import org.junit.Assert;
20 20 import org.junit.Test;
21 21 import org.thingsboard.server.common.data.EntityInfo;
  22 +import org.thingsboard.server.common.data.Tenant;
22 23 import org.thingsboard.server.common.data.TenantProfile;
  24 +import org.thingsboard.server.common.data.TenantProfileData;
23 25 import org.thingsboard.server.common.data.id.TenantId;
24 26 import org.thingsboard.server.common.data.id.TenantProfileId;
25 27 import org.thingsboard.server.common.data.page.PageData;
... ... @@ -136,6 +138,37 @@ public class BaseTenantProfileServiceTest extends AbstractServiceTest {
136 138 tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile2);
137 139 }
138 140
  141 + @Test(expected = DataValidationException.class)
  142 + public void testSaveSameTenantProfileWithDifferentIsolatedTbRuleEngine() {
  143 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
  144 + TenantProfile savedTenantProfile = tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile);
  145 + savedTenantProfile.setIsolatedTbRuleEngine(true);
  146 + tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile);
  147 + }
  148 +
  149 + @Test(expected = DataValidationException.class)
  150 + public void testSaveSameTenantProfileWithDifferentIsolatedTbCore() {
  151 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
  152 + TenantProfile savedTenantProfile = tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile);
  153 + savedTenantProfile.setIsolatedTbCore(true);
  154 + tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile);
  155 + }
  156 +
  157 + @Test(expected = DataValidationException.class)
  158 + public void testDeleteTenantProfileWithExistingTenant() {
  159 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
  160 + TenantProfile savedTenantProfile = tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile);
  161 + Tenant tenant = new Tenant();
  162 + tenant.setTitle("Test tenant");
  163 + tenant.setTenantProfileId(savedTenantProfile.getId());
  164 + tenant = tenantService.saveTenant(tenant);
  165 + try {
  166 + tenantProfileService.deleteTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile.getId());
  167 + } finally {
  168 + tenantService.deleteTenant(tenant.getId());
  169 + }
  170 + }
  171 +
139 172 @Test
140 173 public void testDeleteTenantProfile() {
141 174 TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
... ... @@ -230,7 +263,7 @@ public class BaseTenantProfileServiceTest extends AbstractServiceTest {
230 263 TenantProfile tenantProfile = new TenantProfile();
231 264 tenantProfile.setName(name);
232 265 tenantProfile.setDescription(name + " Test");
233   - tenantProfile.setProfileData(JacksonUtil.OBJECT_MAPPER.createObjectNode());
  266 + tenantProfile.setProfileData(new TenantProfileData());
234 267 tenantProfile.setDefault(false);
235 268 tenantProfile.setIsolatedTbCore(false);
236 269 tenantProfile.setIsolatedTbRuleEngine(false);
... ...