Commit ad87924437b99ccdf0cf3ea6d4ea79599d524469
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 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceConfiguration.java
0 → 100644
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 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java
0 → 100644
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); | ... | ... |