Commit d14f6a4ddd4291e14b5d9d85ae9e95c1c9113166

Authored by Viacheslav Klimov
1 parent bd1cfa44

Refactor device bulk import

@@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
20 import com.fasterxml.jackson.databind.node.TextNode; 20 import com.fasterxml.jackson.databind.node.TextNode;
21 import lombok.SneakyThrows; 21 import lombok.SneakyThrows;
22 import org.apache.commons.collections.CollectionUtils; 22 import org.apache.commons.collections.CollectionUtils;
  23 +import org.apache.commons.lang3.RandomStringUtils;
23 import org.apache.commons.lang3.StringUtils; 24 import org.apache.commons.lang3.StringUtils;
24 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
25 import org.thingsboard.common.util.JacksonUtil; 26 import org.thingsboard.common.util.JacksonUtil;
@@ -31,6 +32,7 @@ import org.thingsboard.server.common.data.DeviceProfileType; @@ -31,6 +32,7 @@ import org.thingsboard.server.common.data.DeviceProfileType;
31 import org.thingsboard.server.common.data.DeviceTransportType; 32 import org.thingsboard.server.common.data.DeviceTransportType;
32 import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; 33 import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
33 import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials; 34 import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials;
  35 +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
34 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; 36 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
35 import org.thingsboard.server.common.data.device.profile.DeviceProfileData; 37 import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
36 import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; 38 import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
@@ -58,9 +60,9 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; @@ -58,9 +60,9 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
58 import java.util.Collection; 60 import java.util.Collection;
59 import java.util.EnumSet; 61 import java.util.EnumSet;
60 import java.util.Map; 62 import java.util.Map;
  63 +import java.util.Objects;
61 import java.util.Optional; 64 import java.util.Optional;
62 import java.util.Set; 65 import java.util.Set;
63 -import java.util.stream.Stream;  
64 66
65 @Service 67 @Service
66 @TbCoreComponent 68 @TbCoreComponent
@@ -96,29 +98,21 @@ public class DeviceBulkImportService extends AbstractBulkImportService<Device> { @@ -96,29 +98,21 @@ public class DeviceBulkImportService extends AbstractBulkImportService<Device> {
96 device = existingDevice; 98 device = existingDevice;
97 } 99 }
98 100
99 - DeviceCredentials deviceCredentials = createDeviceCredentials(fields);  
100 - if (deviceCredentials.getCredentialsType() != null) {  
101 - if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.LWM2M_CREDENTIALS) {  
102 - setUpLwM2mDeviceProfile(user.getTenantId(), device);  
103 - }  
104 - try {  
105 - device = deviceService.saveDeviceWithCredentials(device, deviceCredentials);  
106 - } catch (DeviceCredentialsValidationException e) {  
107 - if (deviceCredentials.getId() == null) {  
108 - device.setId(deviceCredentials.getDeviceId());  
109 - importedEntityInfo.setRelatedError("Failed to create " + deviceCredentials.getCredentialsType() + " credentials: "  
110 - + e.getMessage() + ". Falling back to access token creds");  
111 - deviceService.createAccessTokenCredentials(device, null);  
112 - } else {  
113 - importedEntityInfo.setRelatedError("Failed to update credentials: " + e.getMessage());  
114 - }  
115 - }  
116 - } else {  
117 - device = deviceService.saveDevice(device); 101 + DeviceCredentials deviceCredentials;
  102 + try {
  103 + deviceCredentials = createDeviceCredentials(fields);
  104 + deviceCredentialsService.formatCredentials(deviceCredentials);
  105 + } catch (Exception e) {
  106 + throw new DeviceCredentialsValidationException("Invalid device credentials: " + e.getMessage());
118 } 107 }
119 108
120 - importedEntityInfo.setEntity(device); 109 + if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.LWM2M_CREDENTIALS) {
  110 + setUpLwM2mDeviceProfile(user.getTenantId(), device);
  111 + }
  112 +
  113 + device = deviceService.saveDeviceWithCredentials(device, deviceCredentials);
121 114
  115 + importedEntityInfo.setEntity(device);
122 return importedEntityInfo; 116 return importedEntityInfo;
123 } 117 }
124 118
@@ -148,54 +142,79 @@ public class DeviceBulkImportService extends AbstractBulkImportService<Device> { @@ -148,54 +142,79 @@ public class DeviceBulkImportService extends AbstractBulkImportService<Device> {
148 142
149 @SneakyThrows 143 @SneakyThrows
150 private DeviceCredentials createDeviceCredentials(Map<BulkImportColumnType, String> fields) { 144 private DeviceCredentials createDeviceCredentials(Map<BulkImportColumnType, String> fields) {
151 - Set<BulkImportColumnType> columns = fields.keySet();  
152 -  
153 DeviceCredentials credentials = new DeviceCredentials(); 145 DeviceCredentials credentials = new DeviceCredentials();
154 -  
155 - if (columns.contains(BulkImportColumnType.LWM2M_CLIENT_ENDPOINT)) { 146 + if (fields.containsKey(BulkImportColumnType.LWM2M_CLIENT_ENDPOINT)) {
156 credentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS); 147 credentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS);
157 - ObjectNode lwm2mCredentials = JacksonUtil.newObjectNode();  
158 -  
159 - ObjectNode client = JacksonUtil.newObjectNode();  
160 - setValues(client, fields, Set.of(BulkImportColumnType.LWM2M_CLIENT_SECURITY_CONFIG_MODE,  
161 - BulkImportColumnType.LWM2M_CLIENT_ENDPOINT, BulkImportColumnType.LWM2M_CLIENT_IDENTITY,  
162 - BulkImportColumnType.LWM2M_CLIENT_KEY, BulkImportColumnType.LWM2M_CLIENT_CERT));  
163 - LwM2MClientCredentials lwM2MClientCredentials = JacksonUtil.treeToValue(client, LwM2MClientCredentials.class);  
164 - // so that only fields needed for specific type of lwM2MClientCredentials were saved in json  
165 - lwm2mCredentials.set("client", JacksonUtil.valueToTree(lwM2MClientCredentials));  
166 -  
167 - ObjectNode bootstrapServer = JacksonUtil.newObjectNode();  
168 - setValues(bootstrapServer, fields, Set.of(BulkImportColumnType.LWM2M_BOOTSTRAP_SERVER_SECURITY_MODE,  
169 - BulkImportColumnType.LWM2M_BOOTSTRAP_SERVER_PUBLIC_KEY_OR_ID, BulkImportColumnType.LWM2M_BOOTSTRAP_SERVER_SECRET_KEY));  
170 -  
171 - ObjectNode lwm2mServer = JacksonUtil.newObjectNode();  
172 - setValues(lwm2mServer, fields, Set.of(BulkImportColumnType.LWM2M_SERVER_SECURITY_MODE,  
173 - BulkImportColumnType.LWM2M_SERVER_CLIENT_PUBLIC_KEY_OR_ID, BulkImportColumnType.LWM2M_SERVER_CLIENT_SECRET_KEY));  
174 -  
175 - ObjectNode bootstrap = JacksonUtil.newObjectNode();  
176 - bootstrap.set("bootstrapServer", bootstrapServer);  
177 - bootstrap.set("lwm2mServer", lwm2mServer);  
178 - lwm2mCredentials.set("bootstrap", bootstrap);  
179 -  
180 - credentials.setCredentialsValue(lwm2mCredentials.toString());  
181 - } else if (columns.contains(BulkImportColumnType.X509)) { 148 + setUpLwm2mCredentials(fields, credentials);
  149 + } else if (fields.containsKey(BulkImportColumnType.X509)) {
182 credentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE); 150 credentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE);
183 - credentials.setCredentialsValue(fields.get(BulkImportColumnType.X509));  
184 - } else if (CollectionUtils.containsAny(columns, EnumSet.of(BulkImportColumnType.MQTT_CLIENT_ID, BulkImportColumnType.MQTT_USER_NAME, BulkImportColumnType.MQTT_PASSWORD))) { 151 + setUpX509CertificateCredentials(fields, credentials);
  152 + } else if (CollectionUtils.containsAny(fields.keySet(), EnumSet.of(BulkImportColumnType.MQTT_CLIENT_ID, BulkImportColumnType.MQTT_USER_NAME, BulkImportColumnType.MQTT_PASSWORD))) {
185 credentials.setCredentialsType(DeviceCredentialsType.MQTT_BASIC); 153 credentials.setCredentialsType(DeviceCredentialsType.MQTT_BASIC);
186 -  
187 - BasicMqttCredentials basicMqttCredentials = new BasicMqttCredentials();  
188 - basicMqttCredentials.setClientId(fields.get(BulkImportColumnType.MQTT_CLIENT_ID));  
189 - basicMqttCredentials.setUserName(fields.get(BulkImportColumnType.MQTT_USER_NAME));  
190 - basicMqttCredentials.setPassword(fields.get(BulkImportColumnType.MQTT_PASSWORD));  
191 - credentials.setCredentialsValue(JacksonUtil.toString(basicMqttCredentials));  
192 - } else if (columns.contains(BulkImportColumnType.ACCESS_TOKEN)) { 154 + setUpBasicMqttCredentials(fields, credentials);
  155 + } else {
193 credentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); 156 credentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
194 - credentials.setCredentialsId(fields.get(BulkImportColumnType.ACCESS_TOKEN)); 157 + setUpAccessTokenCredentials(fields, credentials);
195 } 158 }
196 return credentials; 159 return credentials;
197 } 160 }
198 161
  162 + private void setUpAccessTokenCredentials(Map<BulkImportColumnType, String> fields, DeviceCredentials credentials) {
  163 + credentials.setCredentialsValue(Optional.ofNullable(fields.get(BulkImportColumnType.ACCESS_TOKEN))
  164 + .orElseGet(() -> RandomStringUtils.randomAlphanumeric(20)));
  165 + }
  166 +
  167 + private void setUpBasicMqttCredentials(Map<BulkImportColumnType, String> fields, DeviceCredentials credentials) {
  168 + BasicMqttCredentials basicMqttCredentials = new BasicMqttCredentials();
  169 + basicMqttCredentials.setClientId(fields.get(BulkImportColumnType.MQTT_CLIENT_ID));
  170 + basicMqttCredentials.setUserName(fields.get(BulkImportColumnType.MQTT_USER_NAME));
  171 + basicMqttCredentials.setPassword(fields.get(BulkImportColumnType.MQTT_PASSWORD));
  172 + credentials.setCredentialsValue(JacksonUtil.toString(basicMqttCredentials));
  173 + }
  174 +
  175 + private void setUpX509CertificateCredentials(Map<BulkImportColumnType, String> fields, DeviceCredentials credentials) {
  176 + credentials.setCredentialsValue(fields.get(BulkImportColumnType.X509));
  177 + }
  178 +
  179 + private void setUpLwm2mCredentials(Map<BulkImportColumnType, String> fields, DeviceCredentials credentials) throws com.fasterxml.jackson.core.JsonProcessingException {
  180 + ObjectNode lwm2mCredentials = JacksonUtil.newObjectNode();
  181 +
  182 + Set.of(BulkImportColumnType.LWM2M_CLIENT_SECURITY_CONFIG_MODE, BulkImportColumnType.LWM2M_BOOTSTRAP_SERVER_SECURITY_MODE,
  183 + BulkImportColumnType.LWM2M_SERVER_SECURITY_MODE).stream()
  184 + .map(fields::get)
  185 + .filter(Objects::nonNull)
  186 + .forEach(securityMode -> {
  187 + try {
  188 + LwM2MSecurityMode.valueOf(securityMode);
  189 + } catch (IllegalArgumentException e) {
  190 + throw new DeviceCredentialsValidationException("Unknown LwM2M security mode: " + securityMode);
  191 + }
  192 + });
  193 +
  194 + ObjectNode client = JacksonUtil.newObjectNode();
  195 + setValues(client, fields, Set.of(BulkImportColumnType.LWM2M_CLIENT_SECURITY_CONFIG_MODE,
  196 + BulkImportColumnType.LWM2M_CLIENT_ENDPOINT, BulkImportColumnType.LWM2M_CLIENT_IDENTITY,
  197 + BulkImportColumnType.LWM2M_CLIENT_KEY, BulkImportColumnType.LWM2M_CLIENT_CERT));
  198 + LwM2MClientCredentials lwM2MClientCredentials = JacksonUtil.treeToValue(client, LwM2MClientCredentials.class);
  199 + // so that only fields needed for specific type of lwM2MClientCredentials were saved in json
  200 + lwm2mCredentials.set("client", JacksonUtil.valueToTree(lwM2MClientCredentials));
  201 +
  202 + ObjectNode bootstrapServer = JacksonUtil.newObjectNode();
  203 + setValues(bootstrapServer, fields, Set.of(BulkImportColumnType.LWM2M_BOOTSTRAP_SERVER_SECURITY_MODE,
  204 + BulkImportColumnType.LWM2M_BOOTSTRAP_SERVER_PUBLIC_KEY_OR_ID, BulkImportColumnType.LWM2M_BOOTSTRAP_SERVER_SECRET_KEY));
  205 +
  206 + ObjectNode lwm2mServer = JacksonUtil.newObjectNode();
  207 + setValues(lwm2mServer, fields, Set.of(BulkImportColumnType.LWM2M_SERVER_SECURITY_MODE,
  208 + BulkImportColumnType.LWM2M_SERVER_CLIENT_PUBLIC_KEY_OR_ID, BulkImportColumnType.LWM2M_SERVER_CLIENT_SECRET_KEY));
  209 +
  210 + ObjectNode bootstrap = JacksonUtil.newObjectNode();
  211 + bootstrap.set("bootstrapServer", bootstrapServer);
  212 + bootstrap.set("lwm2mServer", lwm2mServer);
  213 + lwm2mCredentials.set("bootstrap", bootstrap);
  214 +
  215 + credentials.setCredentialsValue(lwm2mCredentials.toString());
  216 + }
  217 +
199 private void setUpLwM2mDeviceProfile(TenantId tenantId, Device device) { 218 private void setUpLwM2mDeviceProfile(TenantId tenantId, Device device) {
200 DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByName(tenantId, device.getType()); 219 DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByName(tenantId, device.getType());
201 if (deviceProfile != null) { 220 if (deviceProfile != null) {
@@ -29,5 +29,7 @@ public interface DeviceCredentialsService { @@ -29,5 +29,7 @@ public interface DeviceCredentialsService {
29 29
30 DeviceCredentials createDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials); 30 DeviceCredentials createDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials);
31 31
  32 + void formatCredentials(DeviceCredentials deviceCredentials);
  33 +
32 void deleteDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials); 34 void deleteDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials);
33 } 35 }
@@ -56,8 +56,6 @@ public interface DeviceService { @@ -56,8 +56,6 @@ public interface DeviceService {
56 56
57 Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile); 57 Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile);
58 58
59 - void createAccessTokenCredentials(Device device, String accessToken);  
60 -  
61 Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, CustomerId customerId); 59 Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, CustomerId customerId);
62 60
63 Device unassignDeviceFromCustomer(TenantId tenantId, DeviceId deviceId); 61 Device unassignDeviceFromCustomer(TenantId tenantId, DeviceId deviceId);
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.common.data.device.credentials.lwm2m; 16 package org.thingsboard.server.common.data.device.credentials.lwm2m;
17 17
18 import com.fasterxml.jackson.annotation.JsonIgnore; 18 import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
19 import com.fasterxml.jackson.annotation.JsonSubTypes; 20 import com.fasterxml.jackson.annotation.JsonSubTypes;
20 import com.fasterxml.jackson.annotation.JsonTypeInfo; 21 import com.fasterxml.jackson.annotation.JsonTypeInfo;
21 22
@@ -28,6 +29,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; @@ -28,6 +29,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
28 @JsonSubTypes.Type(value = RPKClientCredentials.class, name = "RPK"), 29 @JsonSubTypes.Type(value = RPKClientCredentials.class, name = "RPK"),
29 @JsonSubTypes.Type(value = X509ClientCredentials.class, name = "X509") 30 @JsonSubTypes.Type(value = X509ClientCredentials.class, name = "X509")
30 }) 31 })
  32 +@JsonIgnoreProperties(ignoreUnknown = true)
31 public interface LwM2MClientCredentials { 33 public interface LwM2MClientCredentials {
32 34
33 @JsonIgnore 35 @JsonIgnore
@@ -81,20 +81,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen @@ -81,20 +81,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
81 } 81 }
82 82
83 private DeviceCredentials saveOrUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) { 83 private DeviceCredentials saveOrUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) {
84 - if (deviceCredentials.getCredentialsType() == null) {  
85 - throw new DataValidationException("Device credentials type should be specified");  
86 - }  
87 - switch (deviceCredentials.getCredentialsType()) {  
88 - case X509_CERTIFICATE:  
89 - formatCertData(deviceCredentials);  
90 - break;  
91 - case MQTT_BASIC:  
92 - formatSimpleMqttCredentials(deviceCredentials);  
93 - break;  
94 - case LWM2M_CREDENTIALS:  
95 - formatSimpleLwm2mCredentials(deviceCredentials);  
96 - break;  
97 - } 84 + formatCredentials(deviceCredentials);
98 log.trace("Executing updateDeviceCredentials [{}]", deviceCredentials); 85 log.trace("Executing updateDeviceCredentials [{}]", deviceCredentials);
99 credentialsValidator.validate(deviceCredentials, id -> tenantId); 86 credentialsValidator.validate(deviceCredentials, id -> tenantId);
100 try { 87 try {
@@ -110,6 +97,21 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen @@ -110,6 +97,21 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
110 } 97 }
111 } 98 }
112 99
  100 + @Override
  101 + public void formatCredentials(DeviceCredentials deviceCredentials) {
  102 + switch (deviceCredentials.getCredentialsType()) {
  103 + case X509_CERTIFICATE:
  104 + formatCertData(deviceCredentials);
  105 + break;
  106 + case MQTT_BASIC:
  107 + formatSimpleMqttCredentials(deviceCredentials);
  108 + break;
  109 + case LWM2M_CREDENTIALS:
  110 + formatSimpleLwm2mCredentials(deviceCredentials);
  111 + break;
  112 + }
  113 + }
  114 +
113 private void formatSimpleMqttCredentials(DeviceCredentials deviceCredentials) { 115 private void formatSimpleMqttCredentials(DeviceCredentials deviceCredentials) {
114 BasicMqttCredentials mqttCredentials; 116 BasicMqttCredentials mqttCredentials;
115 try { 117 try {
@@ -238,20 +238,15 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -238,20 +238,15 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
238 private Device doSaveDevice(Device device, String accessToken, boolean doValidate) { 238 private Device doSaveDevice(Device device, String accessToken, boolean doValidate) {
239 Device savedDevice = this.saveDeviceWithoutCredentials(device, doValidate); 239 Device savedDevice = this.saveDeviceWithoutCredentials(device, doValidate);
240 if (device.getId() == null) { 240 if (device.getId() == null) {
241 - createAccessTokenCredentials(savedDevice, accessToken); 241 + DeviceCredentials deviceCredentials = new DeviceCredentials();
  242 + deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId()));
  243 + deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
  244 + deviceCredentials.setCredentialsId(!StringUtils.isEmpty(accessToken) ? accessToken : RandomStringUtils.randomAlphanumeric(20));
  245 + deviceCredentialsService.createDeviceCredentials(savedDevice.getTenantId(), deviceCredentials);
242 } 246 }
243 return savedDevice; 247 return savedDevice;
244 } 248 }
245 249
246 - @Override  
247 - public void createAccessTokenCredentials(Device device, String accessToken) {  
248 - DeviceCredentials deviceCredentials = new DeviceCredentials();  
249 - deviceCredentials.setDeviceId(new DeviceId(device.getUuidId()));  
250 - deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);  
251 - deviceCredentials.setCredentialsId(!StringUtils.isEmpty(accessToken) ? accessToken : RandomStringUtils.randomAlphanumeric(20));  
252 - deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials);  
253 - }  
254 -  
255 private Device saveDeviceWithoutCredentials(Device device, boolean doValidate) { 250 private Device saveDeviceWithoutCredentials(Device device, boolean doValidate) {
256 log.trace("Executing saveDevice [{}]", device); 251 log.trace("Executing saveDevice [{}]", device);
257 if (doValidate) { 252 if (doValidate) {