From 350581028021897cf1a51ad6f5bcc2b81f5cc71d Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 5 Aug 2021 15:13:05 +0300 Subject: [PATCH] Bulk import device credentials handling refactoring --- .../device/DeviceBulkImportService.java | 19 +++++++++----- .../importing/AbstractBulkImportService.java | 4 +++ .../service/importing/ImportedEntityInfo.java | 1 + .../server/dao/device/DeviceService.java | 2 ++ .../transport/adaptor/JsonConverter.java | 1 - .../device/DeviceCredentialsServiceImpl.java | 25 ++++++++++--------- .../server/dao/device/DeviceServiceImpl.java | 15 +++++++---- .../DeviceCredentialsValidationException.java | 22 ++++++++++++++++ 8 files changed, 65 insertions(+), 24 deletions(-) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/exception/DeviceCredentialsValidationException.java diff --git a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java index 5f876c18e2..2ad0dfb812 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java @@ -18,8 +18,6 @@ package org.thingsboard.server.service.device; import com.fasterxml.jackson.databind.node.BooleanNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; import lombok.SneakyThrows; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Service; @@ -39,10 +37,10 @@ import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTrans import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.action.EntityActionService; @@ -63,8 +61,6 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.thingsboard.server.dao.service.Validator.validateId; - @Service @TbCoreComponent public class DeviceBulkImportService extends AbstractBulkImportService { @@ -104,7 +100,18 @@ public class DeviceBulkImportService extends AbstractBulkImportService { if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.LWM2M_CREDENTIALS) { setUpLwM2mDeviceProfile(user.getTenantId(), device); } - device = deviceService.saveDeviceWithCredentials(device, deviceCredentials); + try { + device = deviceService.saveDeviceWithCredentials(device, deviceCredentials); + } catch (DeviceCredentialsValidationException e) { + if (deviceCredentials.getId() == null) { + device.setId(deviceCredentials.getDeviceId()); + importedEntityInfo.setRelatedError("Failed to create " + deviceCredentials.getCredentialsType() + " credentials: " + + e.getMessage() + ". Falling back to access token creds"); + deviceService.createAccessTokenCredentials(device, null); + } else { + importedEntityInfo.setRelatedError("Failed to update credentials: " + e.getMessage()); + } + } } else { device = deviceService.saveDevice(device); } diff --git a/application/src/main/java/org/thingsboard/server/service/importing/AbstractBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/importing/AbstractBulkImportService.java index 2de18e98e5..047209c6ee 100644 --- a/application/src/main/java/org/thingsboard/server/service/importing/AbstractBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/importing/AbstractBulkImportService.java @@ -80,6 +80,10 @@ public abstract class AbstractBulkImportService { private E entity; private boolean isUpdated; private E oldEntity; + private String relatedError; } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java index 162959fa5a..feb23d1e25 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java @@ -54,6 +54,8 @@ public interface DeviceService { Device saveDeviceWithCredentials(Device device, DeviceCredentials deviceCredentials); + void createAccessTokenCredentials(Device device, String accessToken); + Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, CustomerId customerId); Device unassignDeviceFromCustomer(TenantId tenantId, DeviceId deviceId); diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java index 947de9391e..be4143e388 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.common.transport.adaptor; -import com.fasterxml.jackson.databind.JsonNode; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java index 3cce5e1506..37ebf3528f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.service.DataValidator; import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CREDENTIALS_CACHE; @@ -117,10 +118,10 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen throw new IllegalArgumentException(); } } catch (IllegalArgumentException e) { - throw new DataValidationException("Invalid credentials body for simple mqtt credentials!"); + throw new DeviceCredentialsValidationException("Invalid credentials body for simple mqtt credentials!"); } if (StringUtils.isEmpty(mqttCredentials.getClientId()) && StringUtils.isEmpty(mqttCredentials.getUserName())) { - throw new DataValidationException("Both mqtt client id and user name are empty!"); + throw new DeviceCredentialsValidationException("Both mqtt client id and user name are empty!"); } if (StringUtils.isEmpty(mqttCredentials.getClientId())) { deviceCredentials.setCredentialsId(mqttCredentials.getUserName()); @@ -155,7 +156,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen throw new IllegalArgumentException(); } } catch (IllegalArgumentException e) { - throw new DataValidationException("Invalid credentials body for LwM2M credentials!"); + throw new DeviceCredentialsValidationException("Invalid credentials body for LwM2M credentials!"); } String credentialsId = null; @@ -183,7 +184,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen break; } if (credentialsId == null) { - throw new DataValidationException("Invalid credentials body for LwM2M credentials!"); + throw new DeviceCredentialsValidationException("Invalid credentials body for LwM2M credentials!"); } deviceCredentials.setCredentialsId(credentialsId); } @@ -201,38 +202,38 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen @Override protected void validateCreate(TenantId tenantId, DeviceCredentials deviceCredentials) { if (deviceCredentialsDao.findByDeviceId(tenantId, deviceCredentials.getDeviceId().getId()) != null) { - throw new DataValidationException("Credentials for this device are already specified!"); + throw new DeviceCredentialsValidationException("Credentials for this device are already specified!"); } if (deviceCredentialsDao.findByCredentialsId(tenantId, deviceCredentials.getCredentialsId()) != null) { - throw new DataValidationException("Device credentials are already assigned to another device!"); + throw new DeviceCredentialsValidationException("Device credentials are already assigned to another device!"); } } @Override protected void validateUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) { if (deviceCredentialsDao.findById(tenantId, deviceCredentials.getUuidId()) == null) { - throw new DataValidationException("Unable to update non-existent device credentials!"); + throw new DeviceCredentialsValidationException("Unable to update non-existent device credentials!"); } DeviceCredentials existingCredentials = deviceCredentialsDao.findByCredentialsId(tenantId, deviceCredentials.getCredentialsId()); if (existingCredentials != null && !existingCredentials.getId().equals(deviceCredentials.getId())) { - throw new DataValidationException("Device credentials are already assigned to another device!"); + throw new DeviceCredentialsValidationException("Device credentials are already assigned to another device!"); } } @Override protected void validateDataImpl(TenantId tenantId, DeviceCredentials deviceCredentials) { if (deviceCredentials.getDeviceId() == null) { - throw new DataValidationException("Device credentials should be assigned to device!"); + throw new DeviceCredentialsValidationException("Device credentials should be assigned to device!"); } if (deviceCredentials.getCredentialsType() == null) { - throw new DataValidationException("Device credentials type should be specified!"); + throw new DeviceCredentialsValidationException("Device credentials type should be specified!"); } if (StringUtils.isEmpty(deviceCredentials.getCredentialsId())) { - throw new DataValidationException("Device credentials id should be specified!"); + throw new DeviceCredentialsValidationException("Device credentials id should be specified!"); } Device device = deviceService.findDeviceById(tenantId, deviceCredentials.getDeviceId()); if (device == null) { - throw new DataValidationException("Can't assign device credentials to non-existent device!"); + throw new DeviceCredentialsValidationException("Can't assign device credentials to non-existent device!"); } } }; diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index e251e6a810..32dc0332cd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -230,15 +230,20 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe private Device doSaveDevice(Device device, String accessToken, boolean doValidate) { Device savedDevice = this.saveDeviceWithoutCredentials(device, doValidate); if (device.getId() == null) { - DeviceCredentials deviceCredentials = new DeviceCredentials(); - deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId())); - deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); - deviceCredentials.setCredentialsId(!StringUtils.isEmpty(accessToken) ? accessToken : RandomStringUtils.randomAlphanumeric(20)); - deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials); + createAccessTokenCredentials(savedDevice, accessToken); } return savedDevice; } + @Override + public void createAccessTokenCredentials(Device device, String accessToken) { + DeviceCredentials deviceCredentials = new DeviceCredentials(); + deviceCredentials.setDeviceId(new DeviceId(device.getUuidId())); + deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); + deviceCredentials.setCredentialsId(!StringUtils.isEmpty(accessToken) ? accessToken : RandomStringUtils.randomAlphanumeric(20)); + deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials); + } + private Device saveDeviceWithoutCredentials(Device device, boolean doValidate) { log.trace("Executing saveDevice [{}]", device); if (doValidate) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/exception/DeviceCredentialsValidationException.java b/dao/src/main/java/org/thingsboard/server/dao/exception/DeviceCredentialsValidationException.java new file mode 100644 index 0000000000..d4f0b584eb --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/exception/DeviceCredentialsValidationException.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.exception; + +public class DeviceCredentialsValidationException extends DataValidationException { + public DeviceCredentialsValidationException(String message) { + super(message); + } +}