/**
* Copyright © 2016 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.device;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.CustomerEntity;
import org.thingsboard.server.dao.model.DeviceEntity;
import org.thingsboard.server.dao.model.TenantEntity;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.tenant.TenantDao;
import java.util.List;
import static org.thingsboard.server.dao.DaoUtil.convertDataList;
import static org.thingsboard.server.dao.DaoUtil.getData;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validatePageLink;
@Service
@Slf4j
public class DeviceServiceImpl implements DeviceService {
@Autowired
private DeviceDao deviceDao;
@Autowired
private TenantDao tenantDao;
@Autowired
private CustomerDao customerDao;
@Autowired
private DeviceCredentialsService deviceCredentialsService;
@Override
public Device findDeviceById(DeviceId deviceId) {
log.trace("Executing findDeviceById [{}]", deviceId);
validateId(deviceId, "Incorrect deviceId " + deviceId);
DeviceEntity deviceEntity = deviceDao.findById(deviceId.getId());
return getData(deviceEntity);
}
@Override
public Device saveDevice(Device device) {
log.trace("Executing saveDevice [{}]", device);
deviceValidator.validate(device);
DeviceEntity deviceEntity = deviceDao.save(device);
if (device.getId() == null) {
DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setDeviceId(new DeviceId(deviceEntity.getId()));
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId(RandomStringUtils.randomAlphanumeric(20));
deviceCredentialsService.createDeviceCredentials(deviceCredentials);
}
return getData(deviceEntity);
}
@Override
public Device assignDeviceToCustomer(DeviceId deviceId, CustomerId customerId) {
Device device = findDeviceById(deviceId);
device.setCustomerId(customerId);
return saveDevice(device);
}
@Override
public Device unassignDeviceFromCustomer(DeviceId deviceId) {
Device device = findDeviceById(deviceId);
device.setCustomerId(null);
return saveDevice(device);
}
@Override
public void deleteDevice(DeviceId deviceId) {
log.trace("Executing deleteDevice [{}]", deviceId);
validateId(deviceId, "Incorrect deviceId " + deviceId);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(deviceId);
if (deviceCredentials != null) {
deviceCredentialsService.deleteDeviceCredentials(deviceCredentials);
}
deviceDao.removeById(deviceId.getId());
}
@Override
public TextPageData<Device> findDevicesByTenantId(TenantId tenantId, TextPageLink pageLink) {
log.trace("Executing findDevicesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, "Incorrect tenantId " + tenantId);
validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantId(tenantId.getId(), pageLink);
List<Device> devices = convertDataList(deviceEntities);
return new TextPageData<Device>(devices, pageLink);
}
@Override
public void deleteDevicesByTenantId(TenantId tenantId) {
log.trace("Executing deleteDevicesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, "Incorrect tenantId " + tenantId);
tenantDevicesRemover.removeEntitites(tenantId);
}
@Override
public TextPageData<Device> findDevicesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink);
validateId(tenantId, "Incorrect tenantId " + tenantId);
validateId(customerId, "Incorrect customerId " + customerId);
validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
List<Device> devices = convertDataList(deviceEntities);
return new TextPageData<Device>(devices, pageLink);
}
@Override
public void unassignCustomerDevices(TenantId tenantId, CustomerId customerId) {
log.trace("Executing unassignCustomerDevices, tenantId [{}], customerId [{}]", tenantId, customerId);
validateId(tenantId, "Incorrect tenantId " + tenantId);
validateId(customerId, "Incorrect customerId " + customerId);
new CustomerDevicesUnassigner(tenantId).removeEntitites(customerId);
}
private DataValidator<Device> deviceValidator =
new DataValidator<Device>() {
@Override
protected void validateCreate(Device device) {
deviceDao.findDevicesByTenantIdAndName(device.getTenantId().getId(), device.getName()).ifPresent(
d -> {
throw new DataValidationException("Device with such name already exists!");
}
);
}
@Override
protected void validateUpdate(Device device) {
deviceDao.findDevicesByTenantIdAndName(device.getTenantId().getId(), device.getName()).ifPresent(
d -> {
if (!d.getId().equals(device.getUuidId())) {
throw new DataValidationException("Device with such name already exists!");
}
}
);
}
@Override
protected void validateDataImpl(Device device) {
if (StringUtils.isEmpty(device.getName())) {
throw new DataValidationException("Device name should be specified!");
}
if (device.getTenantId() == null) {
throw new DataValidationException("Device should be assigned to tenant!");
} else {
TenantEntity tenant = tenantDao.findById(device.getTenantId().getId());
if (tenant == null) {
throw new DataValidationException("Device is referencing to non-existent tenant!");
}
}
if (device.getCustomerId() == null) {
device.setCustomerId(new CustomerId(NULL_UUID));
} else if (!device.getCustomerId().getId().equals(NULL_UUID)) {
CustomerEntity customer = customerDao.findById(device.getCustomerId().getId());
if (customer == null) {
throw new DataValidationException("Can't assign device to non-existent customer!");
}
if (!customer.getTenantId().equals(device.getTenantId().getId())) {
throw new DataValidationException("Can't assign device to customer from different tenant!");
}
}
}
};
private PaginatedRemover<TenantId, DeviceEntity> tenantDevicesRemover =
new PaginatedRemover<TenantId, DeviceEntity>() {
@Override
protected List<DeviceEntity> findEntities(TenantId id, TextPageLink pageLink) {
return deviceDao.findDevicesByTenantId(id.getId(), pageLink);
}
@Override
protected void removeEntity(DeviceEntity entity) {
deleteDevice(new DeviceId(entity.getId()));
}
};
class CustomerDevicesUnassigner extends PaginatedRemover<CustomerId, DeviceEntity> {
private TenantId tenantId;
CustomerDevicesUnassigner(TenantId tenantId) {
this.tenantId = tenantId;
}
@Override
protected List<DeviceEntity> findEntities(CustomerId id, TextPageLink pageLink) {
return deviceDao.findDevicesByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink);
}
@Override
protected void removeEntity(DeviceEntity entity) {
unassignDeviceFromCustomer(new DeviceId(entity.getId()));
}
}
}