diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index b7f8b086c4..1fffd210ef 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -59,9 +60,14 @@ import org.springframework.util.MultiValueMap; import org.springframework.web.context.WebApplicationContext; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.page.PageLink; @@ -305,6 +311,20 @@ public abstract class AbstractWebTest { } } + protected DeviceProfile createDeviceProfile(String name) { + DeviceProfile deviceProfile = new DeviceProfile(); + deviceProfile.setName(name); + deviceProfile.setType(DeviceProfileType.DEFAULT); + deviceProfile.setDescription(name + " Test"); + DeviceProfileData deviceProfileData = new DeviceProfileData(); + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); + deviceProfileData.setConfiguration(configuration); + deviceProfile.setProfileData(deviceProfileData); + deviceProfile.setDefault(false); + deviceProfile.setDefaultRuleChainId(new RuleChainId(Uuids.timeBased())); + return deviceProfile; + } + protected ResultActions doGet(String urlTemplate, Object... urlVariables) throws Exception { MockHttpServletRequestBuilder getRequest = get(urlTemplate, urlVariables); setJwtToken(getRequest); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java index f87dc5de6d..51f305c988 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -24,6 +24,7 @@ import org.junit.Before; import org.junit.Test; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; @@ -236,6 +237,21 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { .andExpect(status().isNotFound()); } + @Test + public void testSaveSameDeviceWithDifferentDeviceProfileId() throws Exception { + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile 2"); + DeviceProfile savedDeviceProfile2 = doPost("/api/deviceProfile", deviceProfile2, DeviceProfile.class); + + savedDevice.setDeviceProfileId(savedDeviceProfile2.getId()); + + doPost("/api/device/", savedDevice).andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Changing device profile is prohibited"))); + } + @Test public void testAssignDeviceToCustomerFromDifferentTenant() throws Exception { loginSysAdmin(); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java index fc54791ddd..033a1eef0f 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java @@ -15,21 +15,20 @@ */ package org.thingsboard.server.controller; -import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.core.type.TypeReference; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; -import org.thingsboard.server.dao.util.mapping.JacksonUtil; import java.util.ArrayList; import java.util.Collections; @@ -148,6 +147,32 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController .andExpect(statusReason(containsString("Device profile with such name already exists"))); } + @Test + public void testSaveSameDeviceProfileWithDifferentType() throws Exception { + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); + savedDeviceProfile.setType(DeviceProfileType.LWM2M); + doPost("/api/deviceProfile", savedDeviceProfile).andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Changing type of device profile is prohibited"))); + } + + @Test + public void testDeleteDeviceProfileWithExistingDevice() throws Exception { + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); + + Device device = new Device(); + device.setName("Test device"); + device.setType("default"); + device.setDeviceProfileId(savedDeviceProfile.getId()); + + Device savedDevice = doPost("/api/device", device, Device.class); + + doDelete("/api/deviceProfile/" + savedDeviceProfile.getId().getId().toString()) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("The device profile referenced by the devices cannot be deleted"))); + } + @Test public void testDeleteDeviceProfile() throws Exception { DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); @@ -254,13 +279,4 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController Assert.assertEquals(1, pageData.getTotalElements()); } - private DeviceProfile createDeviceProfile(String name) { - DeviceProfile deviceProfile = new DeviceProfile(); - deviceProfile.setName(name); - deviceProfile.setDescription(name + " Test"); - deviceProfile.setProfileData(JacksonUtil.OBJECT_MAPPER.createObjectNode()); - deviceProfile.setDefault(false); - deviceProfile.setDefaultRuleChainId(new RuleChainId(Uuids.timeBased())); - return deviceProfile; - } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java index a0354be2c3..a4d615ae2b 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java @@ -20,16 +20,14 @@ import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.TenantProfileData; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.tenant.TenantProfileService; -import org.thingsboard.server.dao.util.mapping.JacksonUtil; import java.util.ArrayList; import java.util.Collections; @@ -48,7 +46,9 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController private TenantProfileService tenantProfileService; @After - public void after() { + @Override + public void teardown() throws Exception { + super.teardown(); tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID); } @@ -133,6 +133,45 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController .andExpect(statusReason(containsString("Tenant profile with such name already exists"))); } + @Test + public void testSaveSameTenantProfileWithDifferentIsolatedTbRuleEngine() throws Exception { + loginSysAdmin(); + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); + TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); + savedTenantProfile.setIsolatedTbRuleEngine(true); + doPost("/api/tenantProfile", savedTenantProfile).andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Can't update isolatedTbRuleEngine property"))); + } + + @Test + public void testSaveSameTenantProfileWithDifferentIsolatedTbCore() throws Exception { + loginSysAdmin(); + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); + TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); + savedTenantProfile.setIsolatedTbCore(true); + doPost("/api/tenantProfile", savedTenantProfile).andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Can't update isolatedTbCore property"))); + } + + @Test + public void testDeleteTenantProfileWithExistingTenant() throws Exception { + loginSysAdmin(); + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); + TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant with tenant profile"); + tenant.setTenantProfileId(savedTenantProfile.getId()); + Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class); + + doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString()) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("The tenant profile referenced by the tenants cannot be deleted"))); + + doDelete("/api/tenant/"+savedTenant.getId().getId().toString()) + .andExpect(status().isOk()); + } + @Test public void testDeleteTenantProfile() throws Exception { loginSysAdmin(); @@ -246,7 +285,7 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController TenantProfile tenantProfile = new TenantProfile(); tenantProfile.setName(name); tenantProfile.setDescription(name + " Test"); - tenantProfile.setProfileData(JacksonUtil.OBJECT_MAPPER.createObjectNode()); + tenantProfile.setProfileData(new TenantProfileData()); tenantProfile.setDefault(false); tenantProfile.setIsolatedTbCore(false); tenantProfile.setIsolatedTbRuleEngine(false); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index 6233933cfd..ca8e5f5575 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -15,13 +15,22 @@ */ package org.thingsboard.server.common.data; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.core.JsonProcessingException; import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.device.data.DeviceData; +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; +import java.io.ByteArrayInputStream; +import java.io.IOException; + @EqualsAndHashCode(callSuper = true) +@Slf4j public class Device extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId { private static final long serialVersionUID = 2807343040519543363L; @@ -32,6 +41,9 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen private String type; private String label; private DeviceProfileId deviceProfileId; + private transient DeviceData deviceData; + @JsonIgnore + private byte[] deviceDataBytes; public Device() { super(); @@ -49,6 +61,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen this.type = device.getType(); this.label = device.getLabel(); this.deviceProfileId = device.getDeviceProfileId(); + this.setDeviceData(device.getDeviceData()); } public TenantId getTenantId() { @@ -100,6 +113,33 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen this.deviceProfileId = deviceProfileId; } + public DeviceData getDeviceData() { + if (deviceData != null) { + return deviceData; + } else { + if (deviceDataBytes != null) { + try { + deviceData = mapper.readValue(new ByteArrayInputStream(deviceDataBytes), DeviceData.class); + } catch (IOException e) { + log.warn("Can't deserialize device data: ", e); + return null; + } + return deviceData; + } else { + return null; + } + } + } + + public void setDeviceData(DeviceData data) { + this.deviceData = data; + try { + this.deviceDataBytes = data != null ? mapper.writeValueAsBytes(data) : null; + } catch (JsonProcessingException e) { + log.warn("Can't serialize device data: ", e); + } + } + @Override public String getSearchText() { return getName(); @@ -120,6 +160,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen builder.append(label); builder.append(", deviceProfileId="); builder.append(deviceProfileId); + builder.append(", deviceData="); + builder.append(deviceData); builder.append(", additionalInfo="); builder.append(getAdditionalInfo()); builder.append(", createdTime="); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java index 62859e6f79..79620c2d9f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java @@ -16,26 +16,32 @@ package org.thingsboard.server.common.data; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.core.JsonProcessingException; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; -import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.getJson; -import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.setJson; +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.mapper; @Data @EqualsAndHashCode(callSuper = true) +@Slf4j public class DeviceProfile extends SearchTextBased implements HasName, HasTenantId { private TenantId tenantId; private String name; private String description; private boolean isDefault; + private DeviceProfileType type; private RuleChainId defaultRuleChainId; - private transient JsonNode profileData; + private transient DeviceProfileData profileData; @JsonIgnore private byte[] profileDataBytes; @@ -67,12 +73,31 @@ public class DeviceProfile extends SearchTextBased implements H return name; } - public JsonNode getProfileData() { - return getJson(() -> profileData, () -> profileDataBytes); + public DeviceProfileData getProfileData() { + if (profileData != null) { + return profileData; + } else { + if (profileDataBytes != null) { + try { + profileData = mapper.readValue(new ByteArrayInputStream(profileDataBytes), DeviceProfileData.class); + } catch (IOException e) { + log.warn("Can't deserialize device profile data: ", e); + return null; + } + return profileData; + } else { + return null; + } + } } - public void setProfileData(JsonNode data) { - setJson(data, json -> this.profileData = json, bytes -> this.profileDataBytes = bytes); + public void setProfileData(DeviceProfileData data) { + this.profileData = data; + try { + this.profileDataBytes = data != null ? mapper.writeValueAsBytes(data) : null; + } catch (JsonProcessingException e) { + log.warn("Can't serialize device profile data: ", e); + } } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileType.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileType.java new file mode 100644 index 0000000000..19da934bf7 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileType.java @@ -0,0 +1,21 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data; + +public enum DeviceProfileType { + DEFAULT, + LWM2M +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java index 8dc9bf6abc..4efd673d7b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java @@ -35,7 +35,7 @@ import java.util.function.Consumer; @Slf4j public abstract class SearchTextBasedWithAdditionalInfo extends SearchTextBased implements HasAdditionalInfo { - private static final ObjectMapper mapper = new ObjectMapper(); + public static final ObjectMapper mapper = new ObjectMapper(); private transient JsonNode additionalInfo; @JsonIgnore private byte[] additionalInfoBytes; @@ -84,7 +84,7 @@ public abstract class SearchTextBasedWithAdditionalInfo ext byte[] data = binaryData.get(); if (data != null) { try { - return new ObjectMapper().readTree(new ByteArrayInputStream(data)); + return mapper.readTree(new ByteArrayInputStream(data)); } catch (IOException e) { log.warn("Can't deserialize json data: ", e); return null; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java index 1c855af68c..32f620f869 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java @@ -16,16 +16,20 @@ package org.thingsboard.server.common.data; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.core.JsonProcessingException; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.id.TenantProfileId; -import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.getJson; -import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.setJson; +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.mapper; @Data @EqualsAndHashCode(callSuper = true) +@Slf4j public class TenantProfile extends SearchTextBased implements HasName { private String name; @@ -33,7 +37,7 @@ public class TenantProfile extends SearchTextBased implements H private boolean isDefault; private boolean isolatedTbCore; private boolean isolatedTbRuleEngine; - private transient JsonNode profileData; + private transient TenantProfileData profileData; @JsonIgnore private byte[] profileDataBytes; @@ -65,12 +69,31 @@ public class TenantProfile extends SearchTextBased implements H return name; } - public JsonNode getProfileData() { - return getJson(() -> profileData, () -> profileDataBytes); + public TenantProfileData getProfileData() { + if (profileData != null) { + return profileData; + } else { + if (profileDataBytes != null) { + try { + profileData = mapper.readValue(new ByteArrayInputStream(profileDataBytes), TenantProfileData.class); + } catch (IOException e) { + log.warn("Can't deserialize tenant profile data: ", e); + return null; + } + return profileData; + } else { + return null; + } + } } - public void setProfileData(JsonNode data) { - setJson(data, json -> this.profileData = json, bytes -> this.profileDataBytes = bytes); + public void setProfileData(TenantProfileData data) { + this.profileData = data; + try { + this.profileDataBytes = data != null ? mapper.writeValueAsBytes(data) : null; + } catch (JsonProcessingException e) { + log.warn("Can't serialize tenant profile data: ", e); + } } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfileData.java b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfileData.java new file mode 100644 index 0000000000..c1610d919a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfileData.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import lombok.Data; + +import java.util.HashMap; +import java.util.Map; + +@Data +public class TenantProfileData { + + private Map properties = new HashMap<>(); + + @JsonAnyGetter + public Map properties() { + return this.properties; + } + + @JsonAnySetter + public void put(String name, String value) { + this.properties.put(name, value); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DefaultDeviceConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DefaultDeviceConfiguration.java new file mode 100644 index 0000000000..61a2481922 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DefaultDeviceConfiguration.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.device.data; + +import lombok.Data; +import org.thingsboard.server.common.data.DeviceProfileType; + +@Data +public class DefaultDeviceConfiguration implements DeviceConfiguration { + + @Override + public DeviceProfileType getType() { + return DeviceProfileType.DEFAULT; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceConfiguration.java new file mode 100644 index 0000000000..4794f1592e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceConfiguration.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.device.data; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.thingsboard.server.common.data.DeviceProfileType; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = DefaultDeviceConfiguration.class, name = "DEFAULT"), + @JsonSubTypes.Type(value = Lwm2mDeviceConfiguration.class, name = "LWM2M")}) +public interface DeviceConfiguration { + + @JsonIgnore + DeviceProfileType getType(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceData.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceData.java new file mode 100644 index 0000000000..eea7491e23 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceData.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.device.data; + +import lombok.Data; + +@Data +public class DeviceData { + + private DeviceConfiguration configuration; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/Lwm2mDeviceConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/Lwm2mDeviceConfiguration.java new file mode 100644 index 0000000000..af3e4ac310 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/Lwm2mDeviceConfiguration.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.device.data; + +import lombok.Data; +import org.thingsboard.server.common.data.DeviceProfileType; + +@Data +public class Lwm2mDeviceConfiguration implements DeviceConfiguration { + + @Override + public DeviceProfileType getType() { + return DeviceProfileType.LWM2M; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DefaultDeviceProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DefaultDeviceProfileConfiguration.java new file mode 100644 index 0000000000..d8e6cef10b --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DefaultDeviceProfileConfiguration.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.device.profile; + +import lombok.Data; +import org.thingsboard.server.common.data.DeviceProfileType; + +@Data +public class DefaultDeviceProfileConfiguration implements DeviceProfileConfiguration { + + @Override + public DeviceProfileType getType() { + return DeviceProfileType.DEFAULT; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileConfiguration.java new file mode 100644 index 0000000000..9a15aba144 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileConfiguration.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.device.profile; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.thingsboard.server.common.data.DeviceProfileType; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = DefaultDeviceProfileConfiguration.class, name = "DEFAULT"), + @JsonSubTypes.Type(value = Lwm2mDeviceProfileConfiguration.class, name = "LWM2M")}) +public interface DeviceProfileConfiguration { + + @JsonIgnore + DeviceProfileType getType(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java new file mode 100644 index 0000000000..2665d9a102 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.device.profile; + +import lombok.Data; + +@Data +public class DeviceProfileData { + + private DeviceProfileConfiguration configuration; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileConfiguration.java new file mode 100644 index 0000000000..3ad18f35bc --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileConfiguration.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.device.profile; + +import lombok.Data; +import org.thingsboard.server.common.data.DeviceProfileType; + +@Data +public class Lwm2mDeviceProfileConfiguration implements DeviceProfileConfiguration { + + @Override + public DeviceProfileType getType() { + return DeviceProfileType.LWM2M; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index f50c8d9ba5..d5cdd55e3e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -24,8 +24,11 @@ import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -113,8 +116,17 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D } private void removeDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) { + try { + deviceProfileDao.removeById(tenantId, deviceProfileId.getId()); + } catch (Exception t) { + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_device_profile")) { + throw new DataValidationException("The device profile referenced by the devices cannot be deleted!"); + } else { + throw t; + } + } deleteEntityRelations(tenantId, deviceProfileId); - deviceProfileDao.removeById(tenantId, deviceProfileId.getId()); Cache cache = cacheManager.getCache(DEVICE_PROFILE_CACHE); cache.evict(Collections.singletonList(deviceProfileId.getId())); cache.evict(Arrays.asList("info", deviceProfileId.getId())); @@ -144,8 +156,12 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D deviceProfile.setTenantId(tenantId); deviceProfile.setDefault(true); deviceProfile.setName("Default"); + deviceProfile.setType(DeviceProfileType.DEFAULT); deviceProfile.setDescription("Default device profile"); - deviceProfile.setProfileData(JacksonUtil.OBJECT_MAPPER.createObjectNode()); + DeviceProfileData deviceProfileData = new DeviceProfileData(); + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); + deviceProfileData.setConfiguration(configuration); + deviceProfile.setProfileData(deviceProfileData); return saveDeviceProfile(deviceProfile); } @@ -226,6 +242,16 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D } } } + + @Override + protected void validateUpdate(TenantId tenantId, DeviceProfile deviceProfile) { + DeviceProfile old = deviceProfileDao.findById(deviceProfile.getTenantId(), deviceProfile.getId().getId()); + if (old == null) { + throw new DataValidationException("Can't update non existing device profile!"); + } else if (!old.getType().equals(deviceProfile.getType())) { + throw new DataValidationException("Changing type of device profile is prohibited!"); + } + } }; private PaginatedRemover tenantDeviceProfilesRemover = diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index eb4c22c469..0dd46a5ec3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -41,6 +41,8 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.device.DeviceSearchQuery; +import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration; +import org.thingsboard.server.common.data.device.data.DeviceData; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -168,6 +170,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe if (device.getDeviceProfileId() == null) { EntityInfo deviceProfile = this.deviceProfileService.findDefaultDeviceProfileInfo(device.getTenantId()); device.setDeviceProfileId(new DeviceProfileId(deviceProfile.getId().getId())); + DeviceData deviceData = new DeviceData(); + deviceData.setConfiguration(new DefaultDeviceConfiguration()); + device.setDeviceData(deviceData); } savedDevice = deviceDao.save(device.getTenantId(), device); } catch (Exception t) { @@ -411,6 +416,12 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @Override protected void validateUpdate(TenantId tenantId, Device device) { + Device old = deviceDao.findById(device.getTenantId(), device.getId().getId()); + if (old == null) { + throw new DataValidationException("Can't update non existing device!"); + } else if (!old.getDeviceProfileId().equals(device.getDeviceProfileId())) { + throw new DataValidationException("Changing device profile is prohibited!"); + } } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 9a455778d1..4b58e81e03 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -152,6 +152,8 @@ public class ModelConstants { public static final String DEVICE_LABEL_PROPERTY = "label"; public static final String DEVICE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; public static final String DEVICE_DEVICE_PROFILE_ID_PROPERTY = "device_profile_id"; + public static final String DEVICE_DEVICE_DATA_PROPERTY = "device_data"; + public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text"; public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text"; 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 { public static final String DEVICE_PROFILE_COLUMN_FAMILY_NAME = "device_profile"; public static final String DEVICE_PROFILE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; public static final String DEVICE_PROFILE_NAME_PROPERTY = "name"; + public static final String DEVICE_PROFILE_TYPE_PROPERTY = "type"; public static final String DEVICE_PROFILE_PROFILE_DATA_PROPERTY = "profile_data"; public static final String DEVICE_PROFILE_DESCRIPTION_PROPERTY = "description"; public static final String DEVICE_PROFILE_IS_DEFAULT_PROPERTY = "is_default"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java index eeb89b8994..77882c8f83 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java @@ -16,11 +16,13 @@ package org.thingsboard.server.dao.model.sql; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.device.data.DeviceData; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -28,6 +30,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.SearchTextEntity; +import org.thingsboard.server.dao.util.mapping.JacksonUtil; import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; @@ -65,6 +68,10 @@ public abstract class AbstractDeviceEntity extends BaseSqlEnti @Column(name = ModelConstants.DEVICE_DEVICE_PROFILE_ID_PROPERTY, columnDefinition = "uuid") private UUID deviceProfileId; + @Type(type = "json") + @Column(name = ModelConstants.DEVICE_DEVICE_DATA_PROPERTY) + private JsonNode deviceData; + public AbstractDeviceEntity() { super(); } @@ -83,6 +90,7 @@ public abstract class AbstractDeviceEntity extends BaseSqlEnti if (device.getDeviceProfileId() != null) { this.deviceProfileId = device.getDeviceProfileId().getId(); } + this.deviceData = JacksonUtil.convertValue(device.getDeviceData(), ObjectNode.class); this.name = device.getName(); this.type = device.getType(); this.label = device.getLabel(); @@ -95,6 +103,7 @@ public abstract class AbstractDeviceEntity extends BaseSqlEnti this.tenantId = deviceEntity.getTenantId(); this.customerId = deviceEntity.getCustomerId(); this.deviceProfileId = deviceEntity.getDeviceProfileId(); + this.deviceData = deviceEntity.getDeviceData(); this.type = deviceEntity.getType(); this.name = deviceEntity.getName(); this.label = deviceEntity.getLabel(); @@ -124,6 +133,7 @@ public abstract class AbstractDeviceEntity extends BaseSqlEnti if (deviceProfileId != null) { device.setDeviceProfileId(new DeviceProfileId(deviceProfileId)); } + device.setDeviceData(JacksonUtil.convertValue(deviceData, DeviceData.class)); device.setName(name); device.setType(type); device.setLabel(label); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java index d9c97c6f18..7381ddf638 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java @@ -16,21 +16,27 @@ package org.thingsboard.server.dao.model.sql; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileType; +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.SearchTextEntity; +import org.thingsboard.server.dao.util.mapping.JacksonUtil; import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.Table; import java.util.UUID; @@ -47,6 +53,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl @Column(name = ModelConstants.DEVICE_PROFILE_NAME_PROPERTY) private String name; + @Enumerated(EnumType.STRING) + @Column(name = ModelConstants.DEVICE_PROFILE_TYPE_PROPERTY) + private DeviceProfileType type; + @Column(name = ModelConstants.DEVICE_PROFILE_DESCRIPTION_PROPERTY) private String description; @@ -76,9 +86,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl } this.setCreatedTime(deviceProfile.getCreatedTime()); this.name = deviceProfile.getName(); + this.type = deviceProfile.getType(); this.description = deviceProfile.getDescription(); this.isDefault = deviceProfile.isDefault(); - this.profileData = deviceProfile.getProfileData(); + this.profileData = JacksonUtil.convertValue(deviceProfile.getProfileData(), ObjectNode.class); if (deviceProfile.getDefaultRuleChainId() != null) { this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId().getId(); } @@ -106,9 +117,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl deviceProfile.setTenantId(new TenantId(tenantId)); } deviceProfile.setName(name); + deviceProfile.setType(type); deviceProfile.setDescription(description); deviceProfile.setDefault(isDefault); - deviceProfile.setProfileData(profileData); + deviceProfile.setProfileData(JacksonUtil.convertValue(profileData, DeviceProfileData.class)); if (defaultRuleChainId != null) { deviceProfile.setDefaultRuleChainId(new RuleChainId(defaultRuleChainId)); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TenantProfileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TenantProfileEntity.java index 399b0c6b33..ae5e6695e8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TenantProfileEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TenantProfileEntity.java @@ -16,15 +16,18 @@ package org.thingsboard.server.dao.model.sql; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.TenantProfileData; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.SearchTextEntity; +import org.thingsboard.server.dao.util.mapping.JacksonUtil; import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; @@ -74,7 +77,7 @@ public final class TenantProfileEntity extends BaseSqlEntity impl this.isDefault = tenantProfile.isDefault(); this.isolatedTbCore = tenantProfile.isIsolatedTbCore(); this.isolatedTbRuleEngine = tenantProfile.isIsolatedTbRuleEngine(); - this.profileData = tenantProfile.getProfileData(); + this.profileData = JacksonUtil.convertValue(tenantProfile.getProfileData(), ObjectNode.class); } @Override @@ -100,9 +103,8 @@ public final class TenantProfileEntity extends BaseSqlEntity impl tenantProfile.setDefault(isDefault); tenantProfile.setIsolatedTbCore(isolatedTbCore); tenantProfile.setIsolatedTbRuleEngine(isolatedTbRuleEngine); - tenantProfile.setProfileData(profileData); + tenantProfile.setProfileData(JacksonUtil.convertValue(profileData, TenantProfileData.class)); return tenantProfile; } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java index 0c474cd8cb..939e2b7426 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java @@ -23,8 +23,10 @@ import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.TenantProfileData; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.page.PageData; @@ -107,8 +109,17 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T } private void removeTenantProfile(TenantId tenantId, TenantProfileId tenantProfileId) { + try { + tenantProfileDao.removeById(tenantId, tenantProfileId.getId()); + } catch (Exception t) { + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_tenant_profile")) { + throw new DataValidationException("The tenant profile referenced by the tenants cannot be deleted!"); + } else { + throw t; + } + } deleteEntityRelations(tenantId, tenantProfileId); - tenantProfileDao.removeById(tenantId, tenantProfileId.getId()); Cache cache = cacheManager.getCache(TENANT_PROFILE_CACHE); cache.evict(Collections.singletonList(tenantProfileId.getId())); cache.evict(Arrays.asList("info", tenantProfileId.getId())); @@ -136,7 +147,7 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T defaultTenantProfile = new TenantProfile(); defaultTenantProfile.setDefault(true); defaultTenantProfile.setName("Default"); - defaultTenantProfile.setProfileData(JacksonUtil.OBJECT_MAPPER.createObjectNode()); + defaultTenantProfile.setProfileData(new TenantProfileData()); defaultTenantProfile.setDescription("Default tenant profile"); defaultTenantProfile.setIsolatedTbCore(false); defaultTenantProfile.setIsolatedTbRuleEngine(false); @@ -211,6 +222,18 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T } } } + + @Override + protected void validateUpdate(TenantId tenantId, TenantProfile tenantProfile) { + TenantProfile old = tenantProfileDao.findById(TenantId.SYS_TENANT_ID, tenantProfile.getId().getId()); + if (old == null) { + throw new DataValidationException("Can't update non existing tenant profile!"); + } else if (old.isIsolatedTbRuleEngine() != tenantProfile.isIsolatedTbRuleEngine()) { + throw new DataValidationException("Can't update isolatedTbRuleEngine property!"); + } else if (old.isIsolatedTbCore() != tenantProfile.isIsolatedTbCore()) { + throw new DataValidationException("Can't update isolatedTbCore property!"); + } + } }; private PaginatedRemover tenantProfilesRemover = diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index e807560ef1..f8b7096b98 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -163,12 +163,6 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe if (old == null) { throw new DataValidationException("Can't update non existing tenant!"); } - // TODO: Move validation to tenant profile - /* else if (old.isIsolatedTbRuleEngine() != tenant.isIsolatedTbRuleEngine()) { - throw new DataValidationException("Can't update isolatedTbRuleEngine property!"); - } else if (old.isIsolatedTbCore() != tenant.isIsolatedTbCore()) { - throw new DataValidationException("Can't update isolatedTbCore property!"); - } */ } }; diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/mapping/JacksonUtil.java b/dao/src/main/java/org/thingsboard/server/dao/util/mapping/JacksonUtil.java index d654e85d5c..09c6fff1c1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/mapping/JacksonUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/mapping/JacksonUtil.java @@ -30,7 +30,7 @@ public class JacksonUtil { public static T convertValue(Object fromValue, Class toValueType) { try { - return OBJECT_MAPPER.convertValue(fromValue, toValueType); + return fromValue != null ? OBJECT_MAPPER.convertValue(fromValue, toValueType) : null; } catch (IllegalArgumentException e) { throw new IllegalArgumentException("The given object value: " + fromValue + " cannot be converted to " + toValueType); @@ -39,7 +39,7 @@ public class JacksonUtil { public static T fromString(String string, Class clazz) { try { - return OBJECT_MAPPER.readValue(string, clazz); + return string != null ? OBJECT_MAPPER.readValue(string, clazz) : null; } catch (IOException e) { throw new IllegalArgumentException("The given string value: " + string + " cannot be transformed to Json object"); @@ -48,7 +48,7 @@ public class JacksonUtil { public static String toString(Object value) { try { - return OBJECT_MAPPER.writeValueAsString(value); + return value != null ? OBJECT_MAPPER.writeValueAsString(value) : null; } catch (JsonProcessingException e) { throw new IllegalArgumentException("The given Json object value: " + value + " cannot be transformed to a String"); diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index ba303454f7..2d34f0b1ba 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -122,24 +122,12 @@ CREATE TABLE IF NOT EXISTS dashboard ( title varchar(255) ); -CREATE TABLE IF NOT EXISTS device ( - id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY, - created_time bigint NOT NULL, - additional_info varchar, - customer_id uuid, - device_profile_id uuid NOT NULL, - type varchar(255), - name varchar(255), - label varchar(255), - search_text varchar(255), - tenant_id uuid, - CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name) -); CREATE TABLE IF NOT EXISTS device_profile ( id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY, created_time bigint NOT NULL, name varchar(255), + type varchar(255), profile_data varchar, description varchar, search_text varchar(255), @@ -149,6 +137,22 @@ CREATE TABLE IF NOT EXISTS device_profile ( CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name) ); +CREATE TABLE IF NOT EXISTS device ( + id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY, + created_time bigint NOT NULL, + additional_info varchar, + customer_id uuid, + device_profile_id uuid NOT NULL, + device_data varchar, + type varchar(255), + name varchar(255), + label varchar(255), + search_text varchar(255), + tenant_id uuid, + CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), + CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) +); + CREATE TABLE IF NOT EXISTS device_credentials ( id uuid NOT NULL CONSTRAINT device_credentials_pkey PRIMARY KEY, created_time bigint NOT NULL, @@ -197,6 +201,19 @@ CREATE TABLE IF NOT EXISTS tb_user ( tenant_id uuid ); +CREATE TABLE IF NOT EXISTS tenant_profile ( + id uuid NOT NULL CONSTRAINT tenant_profile_pkey PRIMARY KEY, + created_time bigint NOT NULL, + name varchar(255), + profile_data varchar, + description varchar, + search_text varchar(255), + is_default boolean, + isolated_tb_core boolean, + isolated_tb_rule_engine boolean, + CONSTRAINT tenant_profile_name_unq_key UNIQUE (name) +); + CREATE TABLE IF NOT EXISTS tenant ( id uuid NOT NULL CONSTRAINT tenant_pkey PRIMARY KEY, created_time bigint NOT NULL, @@ -214,20 +231,8 @@ CREATE TABLE IF NOT EXISTS tenant ( title varchar(255), zip varchar(255), isolated_tb_core boolean, - isolated_tb_rule_engine boolean -); - -CREATE TABLE IF NOT EXISTS tenant_profile ( - id uuid NOT NULL CONSTRAINT tenant_profile_pkey PRIMARY KEY, - created_time bigint NOT NULL, - name varchar(255), - profile_data varchar, - description varchar, - search_text varchar(255), - is_default boolean, - isolated_tb_core boolean, isolated_tb_rule_engine boolean, - CONSTRAINT tenant_profile_name_unq_key UNIQUE (name) + CONSTRAINT fk_tenant_profile FOREIGN KEY (tenant_profile_id) REFERENCES tenant_profile(id) ); CREATE TABLE IF NOT EXISTS user_credentials ( diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 7e87dbdd58..053d7420a5 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -139,24 +139,12 @@ CREATE TABLE IF NOT EXISTS dashboard ( title varchar(255) ); -CREATE TABLE IF NOT EXISTS device ( - id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY, - created_time bigint NOT NULL, - additional_info varchar, - customer_id uuid, - device_profile_id uuid NOT NULL, - type varchar(255), - name varchar(255), - label varchar(255), - search_text varchar(255), - tenant_id uuid, - CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name) -); CREATE TABLE IF NOT EXISTS device_profile ( id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY, created_time bigint NOT NULL, name varchar(255), + type varchar(255), profile_data varchar, description varchar, search_text varchar(255), @@ -166,6 +154,22 @@ CREATE TABLE IF NOT EXISTS device_profile ( CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name) ); +CREATE TABLE IF NOT EXISTS device ( + id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY, + created_time bigint NOT NULL, + additional_info varchar, + customer_id uuid, + device_profile_id uuid NOT NULL, + device_data varchar, + type varchar(255), + name varchar(255), + label varchar(255), + search_text varchar(255), + tenant_id uuid, + CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), + CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) +); + CREATE TABLE IF NOT EXISTS device_credentials ( id uuid NOT NULL CONSTRAINT device_credentials_pkey PRIMARY KEY, created_time bigint NOT NULL, @@ -221,6 +225,19 @@ CREATE TABLE IF NOT EXISTS tb_user ( tenant_id uuid ); +CREATE TABLE IF NOT EXISTS tenant_profile ( + id uuid NOT NULL CONSTRAINT tenant_profile_pkey PRIMARY KEY, + created_time bigint NOT NULL, + name varchar(255), + profile_data varchar, + description varchar, + search_text varchar(255), + is_default boolean, + isolated_tb_core boolean, + isolated_tb_rule_engine boolean, + CONSTRAINT tenant_profile_name_unq_key UNIQUE (name) +); + CREATE TABLE IF NOT EXISTS tenant ( id uuid NOT NULL CONSTRAINT tenant_pkey PRIMARY KEY, created_time bigint NOT NULL, @@ -238,20 +255,8 @@ CREATE TABLE IF NOT EXISTS tenant ( title varchar(255), zip varchar(255), isolated_tb_core boolean, - isolated_tb_rule_engine boolean -); - -CREATE TABLE IF NOT EXISTS tenant_profile ( - id uuid NOT NULL CONSTRAINT tenant_profile_pkey PRIMARY KEY, - created_time bigint NOT NULL, - name varchar(255), - profile_data varchar, - description varchar, - search_text varchar(255), - is_default boolean, - isolated_tb_core boolean, isolated_tb_rule_engine boolean, - CONSTRAINT tenant_profile_name_unq_key UNIQUE (name) + CONSTRAINT fk_tenant_profile FOREIGN KEY (tenant_profile_id) REFERENCES tenant_profile(id) ); CREATE TABLE IF NOT EXISTS user_credentials ( diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java index 0b7111686e..8d84d76087 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java @@ -28,10 +28,15 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.support.AnnotationConfigContextLoader; import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Event; +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.dao.alarm.AlarmService; @@ -187,4 +192,19 @@ public abstract class AbstractServiceTest { return new AuditLogLevelFilter(mask); } + protected DeviceProfile createDeviceProfile(TenantId tenantId, String name) { + DeviceProfile deviceProfile = new DeviceProfile(); + deviceProfile.setTenantId(tenantId); + deviceProfile.setName(name); + deviceProfile.setType(DeviceProfileType.DEFAULT); + deviceProfile.setDescription(name + " Test"); + DeviceProfileData deviceProfileData = new DeviceProfileData(); + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); + deviceProfileData.setConfiguration(configuration); + deviceProfile.setProfileData(deviceProfileData); + deviceProfile.setDefault(false); + deviceProfile.setDefaultRuleChainId(new RuleChainId(Uuids.timeBased())); + return deviceProfile; + } + } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java index 3850e29b12..ba823e8595 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java @@ -15,20 +15,19 @@ */ package org.thingsboard.server.dao.service; -import com.datastax.oss.driver.api.core.uuid.Uuids; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.util.mapping.JacksonUtil; import java.util.ArrayList; import java.util.Collections; @@ -58,7 +57,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { @Test public void testSaveDeviceProfile() { - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile"); DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); Assert.assertNotNull(savedDeviceProfile); Assert.assertNotNull(savedDeviceProfile.getId()); @@ -76,7 +75,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { @Test public void testFindDeviceProfileById() { - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile"); DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); DeviceProfile foundDeviceProfile = deviceProfileService.findDeviceProfileById(tenantId, savedDeviceProfile.getId()); Assert.assertNotNull(foundDeviceProfile); @@ -85,7 +84,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { @Test public void testFindDeviceProfileInfoById() { - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile"); DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); EntityInfo foundDeviceProfileInfo = deviceProfileService.findDeviceProfileInfoById(tenantId, savedDeviceProfile.getId()); Assert.assertNotNull(foundDeviceProfileInfo); @@ -111,8 +110,8 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { @Test public void testSetDefaultDeviceProfile() { - DeviceProfile deviceProfile1 = this.createDeviceProfile("Device Profile 1"); - DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile 2"); + DeviceProfile deviceProfile1 = this.createDeviceProfile(tenantId,"Device Profile 1"); + DeviceProfile deviceProfile2 = this.createDeviceProfile(tenantId,"Device Profile 2"); DeviceProfile savedDeviceProfile1 = deviceProfileService.saveDeviceProfile(deviceProfile1); DeviceProfile savedDeviceProfile2 = deviceProfileService.saveDeviceProfile(deviceProfile2); @@ -138,15 +137,36 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { @Test(expected = DataValidationException.class) public void testSaveDeviceProfileWithSameName() { - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile"); deviceProfileService.saveDeviceProfile(deviceProfile); - DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile"); + DeviceProfile deviceProfile2 = this.createDeviceProfile(tenantId,"Device Profile"); deviceProfileService.saveDeviceProfile(deviceProfile2); } + @Test(expected = DataValidationException.class) + public void testSaveSameDeviceProfileWithDifferentType() { + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile"); + DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); + savedDeviceProfile.setType(DeviceProfileType.LWM2M); + deviceProfileService.saveDeviceProfile(savedDeviceProfile); + } + + @Test(expected = DataValidationException.class) + public void testDeleteDeviceProfileWithExistingDevice() { + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile"); + DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); + Device device = new Device(); + device.setTenantId(tenantId); + device.setName("Test device"); + device.setType("default"); + device.setDeviceProfileId(savedDeviceProfile.getId()); + deviceService.saveDevice(device); + deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); + } + @Test public void testDeleteDeviceProfile() { - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile"); DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); DeviceProfile foundDeviceProfile = deviceProfileService.findDeviceProfileById(tenantId, savedDeviceProfile.getId()); @@ -164,7 +184,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { deviceProfiles.addAll(pageData.getData()); for (int i=0;i<28;i++) { - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i); + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile"+i); deviceProfiles.add(deviceProfileService.saveDeviceProfile(deviceProfile)); } @@ -206,7 +226,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { deviceProfiles.addAll(deviceProfilePageData.getData()); for (int i=0;i<28;i++) { - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i); + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId,"Device Profile"+i); deviceProfiles.add(deviceProfileService.saveDeviceProfile(deviceProfile)); } @@ -242,15 +262,6 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { Assert.assertEquals(1, pageData.getTotalElements()); } - private DeviceProfile createDeviceProfile(String name) { - DeviceProfile deviceProfile = new DeviceProfile(); - deviceProfile.setTenantId(tenantId); - deviceProfile.setName(name); - deviceProfile.setDescription(name + " Test"); - deviceProfile.setProfileData(JacksonUtil.OBJECT_MAPPER.createObjectNode()); - deviceProfile.setDefault(false); - deviceProfile.setDefaultRuleChainId(new RuleChainId(Uuids.timeBased())); - return deviceProfile; - } + } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java index 149e78a341..1b7561467e 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java @@ -127,6 +127,23 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { deviceService.deleteDevice(tenantId, device.getId()); } } + + @Test(expected = DataValidationException.class) + public void testSaveSameDeviceWithDifferentDeviceProfileId() { + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + device.setTenantId(tenantId); + device = deviceService.saveDevice(device); + DeviceProfile deviceProfile2 = this.createDeviceProfile(tenantId,"Device Profile 2"); + DeviceProfile savedDeviceProfile2 = deviceProfileService.saveDeviceProfile(deviceProfile2); + device.setDeviceProfileId(savedDeviceProfile2.getId()); + try { + deviceService.saveDevice(device); + } finally { + deviceService.deleteDevice(tenantId, device.getId()); + } + } @Test(expected = DataValidationException.class) public void testAssignDeviceToCustomerFromDifferentTenant() { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantProfileServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantProfileServiceTest.java index c032a8ac98..a9917126df 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantProfileServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantProfileServiceTest.java @@ -19,7 +19,9 @@ import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.thingsboard.server.common.data.EntityInfo; +import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.TenantProfileData; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.page.PageData; @@ -136,6 +138,37 @@ public class BaseTenantProfileServiceTest extends AbstractServiceTest { tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile2); } + @Test(expected = DataValidationException.class) + public void testSaveSameTenantProfileWithDifferentIsolatedTbRuleEngine() { + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); + TenantProfile savedTenantProfile = tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile); + savedTenantProfile.setIsolatedTbRuleEngine(true); + tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile); + } + + @Test(expected = DataValidationException.class) + public void testSaveSameTenantProfileWithDifferentIsolatedTbCore() { + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); + TenantProfile savedTenantProfile = tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile); + savedTenantProfile.setIsolatedTbCore(true); + tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile); + } + + @Test(expected = DataValidationException.class) + public void testDeleteTenantProfileWithExistingTenant() { + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); + TenantProfile savedTenantProfile = tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile); + Tenant tenant = new Tenant(); + tenant.setTitle("Test tenant"); + tenant.setTenantProfileId(savedTenantProfile.getId()); + tenant = tenantService.saveTenant(tenant); + try { + tenantProfileService.deleteTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile.getId()); + } finally { + tenantService.deleteTenant(tenant.getId()); + } + } + @Test public void testDeleteTenantProfile() { TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); @@ -230,7 +263,7 @@ public class BaseTenantProfileServiceTest extends AbstractServiceTest { TenantProfile tenantProfile = new TenantProfile(); tenantProfile.setName(name); tenantProfile.setDescription(name + " Test"); - tenantProfile.setProfileData(JacksonUtil.OBJECT_MAPPER.createObjectNode()); + tenantProfile.setProfileData(new TenantProfileData()); tenantProfile.setDefault(false); tenantProfile.setIsolatedTbCore(false); tenantProfile.setIsolatedTbRuleEngine(false);