committed by
GitHub
51 changed files with 2006 additions and 237 deletions
@ -0,0 +1,94 @@ |
|||||
|
/** |
||||
|
* 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.service.asset; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.node.ObjectNode; |
||||
|
import com.fasterxml.jackson.databind.node.TextNode; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.cluster.TbClusterService; |
||||
|
import org.thingsboard.server.common.data.asset.Asset; |
||||
|
import org.thingsboard.server.dao.asset.AssetService; |
||||
|
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.action.EntityActionService; |
||||
|
import org.thingsboard.server.service.importing.AbstractBulkImportService; |
||||
|
import org.thingsboard.server.service.importing.BulkImportColumnType; |
||||
|
import org.thingsboard.server.service.importing.BulkImportRequest; |
||||
|
import org.thingsboard.server.service.importing.ImportedEntityInfo; |
||||
|
import org.thingsboard.server.service.security.AccessValidator; |
||||
|
import org.thingsboard.server.service.security.model.SecurityUser; |
||||
|
import org.thingsboard.server.service.security.permission.AccessControlService; |
||||
|
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
import java.util.Optional; |
||||
|
|
||||
|
@Service |
||||
|
@TbCoreComponent |
||||
|
public class AssetBulkImportService extends AbstractBulkImportService<Asset> { |
||||
|
private final AssetService assetService; |
||||
|
|
||||
|
public AssetBulkImportService(TelemetrySubscriptionService tsSubscriptionService, TbTenantProfileCache tenantProfileCache, |
||||
|
AccessControlService accessControlService, AccessValidator accessValidator, |
||||
|
EntityActionService entityActionService, TbClusterService clusterService, AssetService assetService) { |
||||
|
super(tsSubscriptionService, tenantProfileCache, accessControlService, accessValidator, entityActionService, clusterService); |
||||
|
this.assetService = assetService; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected ImportedEntityInfo<Asset> saveEntity(BulkImportRequest importRequest, Map<BulkImportColumnType, String> fields, SecurityUser user) { |
||||
|
ImportedEntityInfo<Asset> importedEntityInfo = new ImportedEntityInfo<>(); |
||||
|
|
||||
|
Asset asset = new Asset(); |
||||
|
asset.setTenantId(user.getTenantId()); |
||||
|
setAssetFields(asset, fields); |
||||
|
|
||||
|
Asset existingAsset = assetService.findAssetByTenantIdAndName(user.getTenantId(), asset.getName()); |
||||
|
if (existingAsset != null && importRequest.getMapping().getUpdate()) { |
||||
|
importedEntityInfo.setOldEntity(new Asset(existingAsset)); |
||||
|
importedEntityInfo.setUpdated(true); |
||||
|
existingAsset.update(asset); |
||||
|
asset = existingAsset; |
||||
|
} |
||||
|
asset = assetService.saveAsset(asset); |
||||
|
|
||||
|
importedEntityInfo.setEntity(asset); |
||||
|
return importedEntityInfo; |
||||
|
} |
||||
|
|
||||
|
private void setAssetFields(Asset asset, Map<BulkImportColumnType, String> fields) { |
||||
|
ObjectNode additionalInfo = (ObjectNode) Optional.ofNullable(asset.getAdditionalInfo()).orElseGet(JacksonUtil::newObjectNode); |
||||
|
fields.forEach((columnType, value) -> { |
||||
|
switch (columnType) { |
||||
|
case NAME: |
||||
|
asset.setName(value); |
||||
|
break; |
||||
|
case TYPE: |
||||
|
asset.setType(value); |
||||
|
break; |
||||
|
case LABEL: |
||||
|
asset.setLabel(value); |
||||
|
break; |
||||
|
case DESCRIPTION: |
||||
|
additionalInfo.set("description", new TextNode(value)); |
||||
|
break; |
||||
|
} |
||||
|
}); |
||||
|
asset.setAdditionalInfo(additionalInfo); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,259 @@ |
|||||
|
/** |
||||
|
* 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.service.device; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.node.BooleanNode; |
||||
|
import com.fasterxml.jackson.databind.node.ObjectNode; |
||||
|
import com.fasterxml.jackson.databind.node.TextNode; |
||||
|
import lombok.SneakyThrows; |
||||
|
import org.apache.commons.collections.CollectionUtils; |
||||
|
import org.apache.commons.lang3.RandomStringUtils; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.cluster.TbClusterService; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
import org.thingsboard.server.common.data.DeviceProfileProvisionType; |
||||
|
import org.thingsboard.server.common.data.DeviceProfileType; |
||||
|
import org.thingsboard.server.common.data.DeviceTransportType; |
||||
|
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; |
||||
|
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials; |
||||
|
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; |
||||
|
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
||||
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; |
||||
|
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.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; |
||||
|
import org.thingsboard.server.service.importing.AbstractBulkImportService; |
||||
|
import org.thingsboard.server.service.importing.BulkImportColumnType; |
||||
|
import org.thingsboard.server.service.importing.BulkImportRequest; |
||||
|
import org.thingsboard.server.service.importing.ImportedEntityInfo; |
||||
|
import org.thingsboard.server.service.security.AccessValidator; |
||||
|
import org.thingsboard.server.service.security.model.SecurityUser; |
||||
|
import org.thingsboard.server.service.security.permission.AccessControlService; |
||||
|
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
||||
|
|
||||
|
import java.util.Collection; |
||||
|
import java.util.EnumSet; |
||||
|
import java.util.Map; |
||||
|
import java.util.Objects; |
||||
|
import java.util.Optional; |
||||
|
import java.util.Set; |
||||
|
|
||||
|
@Service |
||||
|
@TbCoreComponent |
||||
|
public class DeviceBulkImportService extends AbstractBulkImportService<Device> { |
||||
|
protected final DeviceService deviceService; |
||||
|
protected final DeviceCredentialsService deviceCredentialsService; |
||||
|
protected final DeviceProfileService deviceProfileService; |
||||
|
|
||||
|
public DeviceBulkImportService(TelemetrySubscriptionService tsSubscriptionService, TbTenantProfileCache tenantProfileCache, |
||||
|
AccessControlService accessControlService, AccessValidator accessValidator, |
||||
|
EntityActionService entityActionService, TbClusterService clusterService, |
||||
|
DeviceService deviceService, DeviceCredentialsService deviceCredentialsService, |
||||
|
DeviceProfileService deviceProfileService) { |
||||
|
super(tsSubscriptionService, tenantProfileCache, accessControlService, accessValidator, entityActionService, clusterService); |
||||
|
this.deviceService = deviceService; |
||||
|
this.deviceCredentialsService = deviceCredentialsService; |
||||
|
this.deviceProfileService = deviceProfileService; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected ImportedEntityInfo<Device> saveEntity(BulkImportRequest importRequest, Map<BulkImportColumnType, String> fields, SecurityUser user) { |
||||
|
ImportedEntityInfo<Device> importedEntityInfo = new ImportedEntityInfo<>(); |
||||
|
|
||||
|
Device device = new Device(); |
||||
|
device.setTenantId(user.getTenantId()); |
||||
|
setDeviceFields(device, fields); |
||||
|
|
||||
|
Device existingDevice = deviceService.findDeviceByTenantIdAndName(user.getTenantId(), device.getName()); |
||||
|
if (existingDevice != null && importRequest.getMapping().getUpdate()) { |
||||
|
importedEntityInfo.setOldEntity(new Device(existingDevice)); |
||||
|
importedEntityInfo.setUpdated(true); |
||||
|
existingDevice.updateDevice(device); |
||||
|
device = existingDevice; |
||||
|
} |
||||
|
|
||||
|
DeviceCredentials deviceCredentials; |
||||
|
try { |
||||
|
deviceCredentials = createDeviceCredentials(fields); |
||||
|
deviceCredentialsService.formatCredentials(deviceCredentials); |
||||
|
} catch (Exception e) { |
||||
|
throw new DeviceCredentialsValidationException("Invalid device credentials: " + e.getMessage()); |
||||
|
} |
||||
|
|
||||
|
if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.LWM2M_CREDENTIALS) { |
||||
|
setUpLwM2mDeviceProfile(user.getTenantId(), device); |
||||
|
} |
||||
|
|
||||
|
device = deviceService.saveDeviceWithCredentials(device, deviceCredentials); |
||||
|
|
||||
|
importedEntityInfo.setEntity(device); |
||||
|
return importedEntityInfo; |
||||
|
} |
||||
|
|
||||
|
private void setDeviceFields(Device device, Map<BulkImportColumnType, String> fields) { |
||||
|
ObjectNode additionalInfo = (ObjectNode) Optional.ofNullable(device.getAdditionalInfo()).orElseGet(JacksonUtil::newObjectNode); |
||||
|
fields.forEach((columnType, value) -> { |
||||
|
switch (columnType) { |
||||
|
case NAME: |
||||
|
device.setName(value); |
||||
|
break; |
||||
|
case TYPE: |
||||
|
device.setType(value); |
||||
|
break; |
||||
|
case LABEL: |
||||
|
device.setLabel(value); |
||||
|
break; |
||||
|
case DESCRIPTION: |
||||
|
additionalInfo.set("description", new TextNode(value)); |
||||
|
break; |
||||
|
case IS_GATEWAY: |
||||
|
additionalInfo.set("gateway", BooleanNode.valueOf(Boolean.parseBoolean(value))); |
||||
|
break; |
||||
|
} |
||||
|
device.setAdditionalInfo(additionalInfo); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@SneakyThrows |
||||
|
private DeviceCredentials createDeviceCredentials(Map<BulkImportColumnType, String> fields) { |
||||
|
DeviceCredentials credentials = new DeviceCredentials(); |
||||
|
if (fields.containsKey(BulkImportColumnType.LWM2M_CLIENT_ENDPOINT)) { |
||||
|
credentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS); |
||||
|
setUpLwm2mCredentials(fields, credentials); |
||||
|
} else if (fields.containsKey(BulkImportColumnType.X509)) { |
||||
|
credentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE); |
||||
|
setUpX509CertificateCredentials(fields, credentials); |
||||
|
} else if (CollectionUtils.containsAny(fields.keySet(), EnumSet.of(BulkImportColumnType.MQTT_CLIENT_ID, BulkImportColumnType.MQTT_USER_NAME, BulkImportColumnType.MQTT_PASSWORD))) { |
||||
|
credentials.setCredentialsType(DeviceCredentialsType.MQTT_BASIC); |
||||
|
setUpBasicMqttCredentials(fields, credentials); |
||||
|
} else { |
||||
|
credentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); |
||||
|
setUpAccessTokenCredentials(fields, credentials); |
||||
|
} |
||||
|
return credentials; |
||||
|
} |
||||
|
|
||||
|
private void setUpAccessTokenCredentials(Map<BulkImportColumnType, String> fields, DeviceCredentials credentials) { |
||||
|
credentials.setCredentialsId(Optional.ofNullable(fields.get(BulkImportColumnType.ACCESS_TOKEN)) |
||||
|
.orElseGet(() -> RandomStringUtils.randomAlphanumeric(20))); |
||||
|
} |
||||
|
|
||||
|
private void setUpBasicMqttCredentials(Map<BulkImportColumnType, String> fields, DeviceCredentials credentials) { |
||||
|
BasicMqttCredentials basicMqttCredentials = new BasicMqttCredentials(); |
||||
|
basicMqttCredentials.setClientId(fields.get(BulkImportColumnType.MQTT_CLIENT_ID)); |
||||
|
basicMqttCredentials.setUserName(fields.get(BulkImportColumnType.MQTT_USER_NAME)); |
||||
|
basicMqttCredentials.setPassword(fields.get(BulkImportColumnType.MQTT_PASSWORD)); |
||||
|
credentials.setCredentialsValue(JacksonUtil.toString(basicMqttCredentials)); |
||||
|
} |
||||
|
|
||||
|
private void setUpX509CertificateCredentials(Map<BulkImportColumnType, String> fields, DeviceCredentials credentials) { |
||||
|
credentials.setCredentialsValue(fields.get(BulkImportColumnType.X509)); |
||||
|
} |
||||
|
|
||||
|
private void setUpLwm2mCredentials(Map<BulkImportColumnType, String> fields, DeviceCredentials credentials) throws com.fasterxml.jackson.core.JsonProcessingException { |
||||
|
ObjectNode lwm2mCredentials = JacksonUtil.newObjectNode(); |
||||
|
|
||||
|
Set.of(BulkImportColumnType.LWM2M_CLIENT_SECURITY_CONFIG_MODE, BulkImportColumnType.LWM2M_BOOTSTRAP_SERVER_SECURITY_MODE, |
||||
|
BulkImportColumnType.LWM2M_SERVER_SECURITY_MODE).stream() |
||||
|
.map(fields::get) |
||||
|
.filter(Objects::nonNull) |
||||
|
.forEach(securityMode -> { |
||||
|
try { |
||||
|
LwM2MSecurityMode.valueOf(securityMode.toUpperCase()); |
||||
|
} catch (IllegalArgumentException e) { |
||||
|
throw new DeviceCredentialsValidationException("Unknown LwM2M security mode: " + securityMode + ", (the mode should be: NO_SEC, PSK, RPK, X509)!"); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
ObjectNode client = JacksonUtil.newObjectNode(); |
||||
|
setValues(client, fields, Set.of(BulkImportColumnType.LWM2M_CLIENT_SECURITY_CONFIG_MODE, |
||||
|
BulkImportColumnType.LWM2M_CLIENT_ENDPOINT, BulkImportColumnType.LWM2M_CLIENT_IDENTITY, |
||||
|
BulkImportColumnType.LWM2M_CLIENT_KEY, BulkImportColumnType.LWM2M_CLIENT_CERT)); |
||||
|
LwM2MClientCredentials lwM2MClientCredentials = JacksonUtil.treeToValue(client, LwM2MClientCredentials.class); |
||||
|
// so that only fields needed for specific type of lwM2MClientCredentials were saved in json
|
||||
|
lwm2mCredentials.set("client", JacksonUtil.valueToTree(lwM2MClientCredentials)); |
||||
|
|
||||
|
ObjectNode bootstrapServer = JacksonUtil.newObjectNode(); |
||||
|
setValues(bootstrapServer, fields, Set.of(BulkImportColumnType.LWM2M_BOOTSTRAP_SERVER_SECURITY_MODE, |
||||
|
BulkImportColumnType.LWM2M_BOOTSTRAP_SERVER_PUBLIC_KEY_OR_ID, BulkImportColumnType.LWM2M_BOOTSTRAP_SERVER_SECRET_KEY)); |
||||
|
|
||||
|
ObjectNode lwm2mServer = JacksonUtil.newObjectNode(); |
||||
|
setValues(lwm2mServer, fields, Set.of(BulkImportColumnType.LWM2M_SERVER_SECURITY_MODE, |
||||
|
BulkImportColumnType.LWM2M_SERVER_CLIENT_PUBLIC_KEY_OR_ID, BulkImportColumnType.LWM2M_SERVER_CLIENT_SECRET_KEY)); |
||||
|
|
||||
|
ObjectNode bootstrap = JacksonUtil.newObjectNode(); |
||||
|
bootstrap.set("bootstrapServer", bootstrapServer); |
||||
|
bootstrap.set("lwm2mServer", lwm2mServer); |
||||
|
lwm2mCredentials.set("bootstrap", bootstrap); |
||||
|
|
||||
|
credentials.setCredentialsValue(lwm2mCredentials.toString()); |
||||
|
} |
||||
|
|
||||
|
private void setUpLwM2mDeviceProfile(TenantId tenantId, Device device) { |
||||
|
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByName(tenantId, device.getType()); |
||||
|
if (deviceProfile != null) { |
||||
|
if (deviceProfile.getTransportType() != DeviceTransportType.LWM2M) { |
||||
|
deviceProfile.setTransportType(DeviceTransportType.LWM2M); |
||||
|
deviceProfile.getProfileData().setTransportConfiguration(new Lwm2mDeviceProfileTransportConfiguration()); |
||||
|
deviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); |
||||
|
device.setDeviceProfileId(deviceProfile.getId()); |
||||
|
} |
||||
|
} else { |
||||
|
deviceProfile = new DeviceProfile(); |
||||
|
deviceProfile.setTenantId(tenantId); |
||||
|
deviceProfile.setType(DeviceProfileType.DEFAULT); |
||||
|
deviceProfile.setName(device.getType()); |
||||
|
deviceProfile.setTransportType(DeviceTransportType.LWM2M); |
||||
|
deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); |
||||
|
|
||||
|
DeviceProfileData deviceProfileData = new DeviceProfileData(); |
||||
|
DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); |
||||
|
DeviceProfileTransportConfiguration transportConfiguration = new Lwm2mDeviceProfileTransportConfiguration(); |
||||
|
DisabledDeviceProfileProvisionConfiguration provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(null); |
||||
|
|
||||
|
deviceProfileData.setConfiguration(configuration); |
||||
|
deviceProfileData.setTransportConfiguration(transportConfiguration); |
||||
|
deviceProfileData.setProvisionConfiguration(provisionConfiguration); |
||||
|
deviceProfile.setProfileData(deviceProfileData); |
||||
|
|
||||
|
deviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); |
||||
|
device.setDeviceProfileId(deviceProfile.getId()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void setValues(ObjectNode objectNode, Map<BulkImportColumnType, String> data, Collection<BulkImportColumnType> columns) { |
||||
|
for (BulkImportColumnType column : columns) { |
||||
|
String value = StringUtils.defaultString(data.get(column), column.getDefaultValue()); |
||||
|
if (value != null && column.getKey() != null) { |
||||
|
objectNode.set(column.getKey(), new TextNode(value)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,106 @@ |
|||||
|
/** |
||||
|
* 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.service.edge; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.node.ObjectNode; |
||||
|
import com.fasterxml.jackson.databind.node.TextNode; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.cluster.TbClusterService; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.dao.edge.EdgeService; |
||||
|
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.action.EntityActionService; |
||||
|
import org.thingsboard.server.service.importing.AbstractBulkImportService; |
||||
|
import org.thingsboard.server.service.importing.BulkImportColumnType; |
||||
|
import org.thingsboard.server.service.importing.BulkImportRequest; |
||||
|
import org.thingsboard.server.service.importing.ImportedEntityInfo; |
||||
|
import org.thingsboard.server.service.security.AccessValidator; |
||||
|
import org.thingsboard.server.service.security.model.SecurityUser; |
||||
|
import org.thingsboard.server.service.security.permission.AccessControlService; |
||||
|
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
import java.util.Optional; |
||||
|
|
||||
|
@Service |
||||
|
@TbCoreComponent |
||||
|
public class EdgeBulkImportService extends AbstractBulkImportService<Edge> { |
||||
|
private final EdgeService edgeService; |
||||
|
|
||||
|
public EdgeBulkImportService(TelemetrySubscriptionService tsSubscriptionService, TbTenantProfileCache tenantProfileCache, |
||||
|
AccessControlService accessControlService, AccessValidator accessValidator, |
||||
|
EntityActionService entityActionService, TbClusterService clusterService, EdgeService edgeService) { |
||||
|
super(tsSubscriptionService, tenantProfileCache, accessControlService, accessValidator, entityActionService, clusterService); |
||||
|
this.edgeService = edgeService; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected ImportedEntityInfo<Edge> saveEntity(BulkImportRequest importRequest, Map<BulkImportColumnType, String> fields, SecurityUser user) { |
||||
|
ImportedEntityInfo<Edge> importedEntityInfo = new ImportedEntityInfo<>(); |
||||
|
|
||||
|
Edge edge = new Edge(); |
||||
|
edge.setTenantId(user.getTenantId()); |
||||
|
setEdgeFields(edge, fields); |
||||
|
|
||||
|
Edge existingEdge = edgeService.findEdgeByTenantIdAndName(user.getTenantId(), edge.getName()); |
||||
|
if (existingEdge != null && importRequest.getMapping().getUpdate()) { |
||||
|
importedEntityInfo.setOldEntity(new Edge(existingEdge)); |
||||
|
importedEntityInfo.setUpdated(true); |
||||
|
existingEdge.update(edge); |
||||
|
edge = existingEdge; |
||||
|
} |
||||
|
edge = edgeService.saveEdge(edge, true); |
||||
|
|
||||
|
importedEntityInfo.setEntity(edge); |
||||
|
return importedEntityInfo; |
||||
|
} |
||||
|
|
||||
|
private void setEdgeFields(Edge edge, Map<BulkImportColumnType, String> fields) { |
||||
|
ObjectNode additionalInfo = (ObjectNode) Optional.ofNullable(edge.getAdditionalInfo()).orElseGet(JacksonUtil::newObjectNode); |
||||
|
fields.forEach((columnType, value) -> { |
||||
|
switch (columnType) { |
||||
|
case NAME: |
||||
|
edge.setName(value); |
||||
|
break; |
||||
|
case TYPE: |
||||
|
edge.setType(value); |
||||
|
break; |
||||
|
case LABEL: |
||||
|
edge.setLabel(value); |
||||
|
break; |
||||
|
case DESCRIPTION: |
||||
|
additionalInfo.set("description", new TextNode(value)); |
||||
|
break; |
||||
|
case EDGE_LICENSE_KEY: |
||||
|
edge.setEdgeLicenseKey(value); |
||||
|
break; |
||||
|
case CLOUD_ENDPOINT: |
||||
|
edge.setCloudEndpoint(value); |
||||
|
break; |
||||
|
case ROUTING_KEY: |
||||
|
edge.setRoutingKey(value); |
||||
|
break; |
||||
|
case SECRET: |
||||
|
edge.setSecret(value); |
||||
|
break; |
||||
|
} |
||||
|
}); |
||||
|
edge.setAdditionalInfo(additionalInfo); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,245 @@ |
|||||
|
/** |
||||
|
* 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.service.importing; |
||||
|
|
||||
|
import com.google.common.util.concurrent.FutureCallback; |
||||
|
import com.google.gson.JsonObject; |
||||
|
import com.google.gson.JsonPrimitive; |
||||
|
import lombok.Data; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.SneakyThrows; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import org.thingsboard.server.cluster.TbClusterService; |
||||
|
import org.thingsboard.server.common.data.BaseData; |
||||
|
import org.thingsboard.server.common.data.TenantProfile; |
||||
|
import org.thingsboard.server.common.data.audit.ActionType; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.data.id.UUIDBased; |
||||
|
import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
||||
|
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
||||
|
import org.thingsboard.server.common.data.kv.DataType; |
||||
|
import org.thingsboard.server.common.data.kv.TsKvEntry; |
||||
|
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
||||
|
import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
||||
|
import org.thingsboard.server.controller.BaseController; |
||||
|
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
||||
|
import org.thingsboard.server.service.action.EntityActionService; |
||||
|
import org.thingsboard.server.service.importing.BulkImportRequest.ColumnMapping; |
||||
|
import org.thingsboard.server.service.security.AccessValidator; |
||||
|
import org.thingsboard.server.service.security.model.SecurityUser; |
||||
|
import org.thingsboard.server.service.security.permission.AccessControlService; |
||||
|
import org.thingsboard.server.service.security.permission.Operation; |
||||
|
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
||||
|
import org.thingsboard.server.utils.CsvUtils; |
||||
|
import org.thingsboard.server.utils.TypeCastUtil; |
||||
|
|
||||
|
import javax.annotation.Nullable; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Arrays; |
||||
|
import java.util.LinkedHashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||
|
import java.util.function.Consumer; |
||||
|
import java.util.stream.Collectors; |
||||
|
import java.util.stream.Stream; |
||||
|
|
||||
|
@RequiredArgsConstructor |
||||
|
public abstract class AbstractBulkImportService<E extends BaseData<? extends EntityId>> { |
||||
|
protected final TelemetrySubscriptionService tsSubscriptionService; |
||||
|
protected final TbTenantProfileCache tenantProfileCache; |
||||
|
protected final AccessControlService accessControlService; |
||||
|
protected final AccessValidator accessValidator; |
||||
|
protected final EntityActionService entityActionService; |
||||
|
protected final TbClusterService clusterService; |
||||
|
|
||||
|
public final BulkImportResult<E> processBulkImport(BulkImportRequest request, SecurityUser user, Consumer<ImportedEntityInfo<E>> onEntityImported) throws Exception { |
||||
|
BulkImportResult<E> result = new BulkImportResult<>(); |
||||
|
|
||||
|
AtomicInteger i = new AtomicInteger(0); |
||||
|
if (request.getMapping().getHeader()) { |
||||
|
i.incrementAndGet(); |
||||
|
} |
||||
|
|
||||
|
parseData(request).forEach(entityData -> { |
||||
|
i.incrementAndGet(); |
||||
|
try { |
||||
|
ImportedEntityInfo<E> importedEntityInfo = saveEntity(request, entityData.getFields(), user); |
||||
|
onEntityImported.accept(importedEntityInfo); |
||||
|
|
||||
|
E entity = importedEntityInfo.getEntity(); |
||||
|
|
||||
|
saveKvs(user, entity, entityData.getKvs()); |
||||
|
|
||||
|
if (importedEntityInfo.getRelatedError() != null) { |
||||
|
throw new RuntimeException(importedEntityInfo.getRelatedError()); |
||||
|
} |
||||
|
|
||||
|
if (importedEntityInfo.isUpdated()) { |
||||
|
result.setUpdated(result.getUpdated() + 1); |
||||
|
} else { |
||||
|
result.setCreated(result.getCreated() + 1); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
result.setErrors(result.getErrors() + 1); |
||||
|
result.getErrorsList().add(String.format("Line %d: %s", i.get(), e.getMessage())); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
protected abstract ImportedEntityInfo<E> saveEntity(BulkImportRequest importRequest, Map<BulkImportColumnType, String> fields, SecurityUser user); |
||||
|
|
||||
|
/* |
||||
|
* Attributes' values are firstly added to JsonObject in order to then make some type cast, |
||||
|
* because we get all values as strings from CSV |
||||
|
* */ |
||||
|
private void saveKvs(SecurityUser user, E entity, Map<ColumnMapping, ParsedValue> data) { |
||||
|
Arrays.stream(BulkImportColumnType.values()) |
||||
|
.filter(BulkImportColumnType::isKv) |
||||
|
.map(kvType -> { |
||||
|
JsonObject kvs = new JsonObject(); |
||||
|
data.entrySet().stream() |
||||
|
.filter(dataEntry -> dataEntry.getKey().getType() == kvType && |
||||
|
StringUtils.isNotEmpty(dataEntry.getKey().getKey())) |
||||
|
.forEach(dataEntry -> kvs.add(dataEntry.getKey().getKey(), dataEntry.getValue().toJsonPrimitive())); |
||||
|
return Map.entry(kvType, kvs); |
||||
|
}) |
||||
|
.filter(kvsEntry -> kvsEntry.getValue().entrySet().size() > 0) |
||||
|
.forEach(kvsEntry -> { |
||||
|
BulkImportColumnType kvType = kvsEntry.getKey(); |
||||
|
if (kvType == BulkImportColumnType.SHARED_ATTRIBUTE || kvType == BulkImportColumnType.SERVER_ATTRIBUTE) { |
||||
|
saveAttributes(user, entity, kvsEntry, kvType); |
||||
|
} else { |
||||
|
saveTelemetry(user, entity, kvsEntry); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@SneakyThrows |
||||
|
private void saveTelemetry(SecurityUser user, E entity, Map.Entry<BulkImportColumnType, JsonObject> kvsEntry) { |
||||
|
List<TsKvEntry> timeseries = JsonConverter.convertToTelemetry(kvsEntry.getValue(), System.currentTimeMillis()) |
||||
|
.entrySet().stream() |
||||
|
.flatMap(entry -> entry.getValue().stream().map(kvEntry -> new BasicTsKvEntry(entry.getKey(), kvEntry))) |
||||
|
.collect(Collectors.toList()); |
||||
|
|
||||
|
accessValidator.validateEntityAndCallback(user, Operation.WRITE_TELEMETRY, entity.getId(), (result, tenantId, entityId) -> { |
||||
|
TenantProfile tenantProfile = tenantProfileCache.get(tenantId); |
||||
|
long tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays()); |
||||
|
tsSubscriptionService.saveAndNotify(tenantId, user.getCustomerId(), entityId, timeseries, tenantTtl, new FutureCallback<Void>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable Void tmp) { |
||||
|
entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, |
||||
|
ActionType.TIMESERIES_UPDATED, null, timeseries); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, |
||||
|
ActionType.TIMESERIES_UPDATED, BaseController.toException(t), timeseries); |
||||
|
throw new RuntimeException(t); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@SneakyThrows |
||||
|
private void saveAttributes(SecurityUser user, E entity, Map.Entry<BulkImportColumnType, JsonObject> kvsEntry, BulkImportColumnType kvType) { |
||||
|
String scope = kvType.getKey(); |
||||
|
List<AttributeKvEntry> attributes = new ArrayList<>(JsonConverter.convertToAttributes(kvsEntry.getValue())); |
||||
|
|
||||
|
accessValidator.validateEntityAndCallback(user, Operation.WRITE_ATTRIBUTES, entity.getId(), (result, tenantId, entityId) -> { |
||||
|
tsSubscriptionService.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback<>() { |
||||
|
|
||||
|
@Override |
||||
|
public void onSuccess(Void unused) { |
||||
|
entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, |
||||
|
null, ActionType.ATTRIBUTES_UPDATED, null, scope, attributes); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable throwable) { |
||||
|
entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, |
||||
|
null, ActionType.ATTRIBUTES_UPDATED, BaseController.toException(throwable), |
||||
|
scope, attributes); |
||||
|
throw new RuntimeException(throwable); |
||||
|
} |
||||
|
|
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private List<EntityData> parseData(BulkImportRequest request) throws Exception { |
||||
|
List<List<String>> records = CsvUtils.parseCsv(request.getFile(), request.getMapping().getDelimiter()); |
||||
|
if (request.getMapping().getHeader()) { |
||||
|
records.remove(0); |
||||
|
} |
||||
|
|
||||
|
List<ColumnMapping> columnsMappings = request.getMapping().getColumns(); |
||||
|
return records.stream() |
||||
|
.map(record -> { |
||||
|
EntityData entityData = new EntityData(); |
||||
|
Stream.iterate(0, i -> i < record.size(), i -> i + 1) |
||||
|
.map(i -> Map.entry(columnsMappings.get(i), record.get(i))) |
||||
|
.filter(entry -> StringUtils.isNotEmpty(entry.getValue())) |
||||
|
.forEach(entry -> { |
||||
|
if (!entry.getKey().getType().isKv()) { |
||||
|
entityData.getFields().put(entry.getKey().getType(), entry.getValue()); |
||||
|
} else { |
||||
|
Map.Entry<DataType, Object> castResult = TypeCastUtil.castValue(entry.getValue()); |
||||
|
entityData.getKvs().put(entry.getKey(), new ParsedValue(castResult.getValue(), castResult.getKey())); |
||||
|
} |
||||
|
}); |
||||
|
return entityData; |
||||
|
}) |
||||
|
.collect(Collectors.toList()); |
||||
|
} |
||||
|
|
||||
|
@Data |
||||
|
protected static class EntityData { |
||||
|
private final Map<BulkImportColumnType, String> fields = new LinkedHashMap<>(); |
||||
|
private final Map<ColumnMapping, ParsedValue> kvs = new LinkedHashMap<>(); |
||||
|
} |
||||
|
|
||||
|
@Data |
||||
|
protected static class ParsedValue { |
||||
|
private final Object value; |
||||
|
private final DataType dataType; |
||||
|
|
||||
|
public JsonPrimitive toJsonPrimitive() { |
||||
|
switch (dataType) { |
||||
|
case STRING: |
||||
|
return new JsonPrimitive((String) value); |
||||
|
case LONG: |
||||
|
return new JsonPrimitive((Long) value); |
||||
|
case DOUBLE: |
||||
|
return new JsonPrimitive((Double) value); |
||||
|
case BOOLEAN: |
||||
|
return new JsonPrimitive((Boolean) value); |
||||
|
default: |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public String stringValue() { |
||||
|
return value.toString(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
/** |
||||
|
* 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.service.importing; |
||||
|
|
||||
|
import lombok.Getter; |
||||
|
import org.thingsboard.server.common.data.DataConstants; |
||||
|
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; |
||||
|
|
||||
|
@Getter |
||||
|
public enum BulkImportColumnType { |
||||
|
NAME, |
||||
|
TYPE, |
||||
|
LABEL, |
||||
|
SHARED_ATTRIBUTE(DataConstants.SHARED_SCOPE, true), |
||||
|
SERVER_ATTRIBUTE(DataConstants.SERVER_SCOPE, true), |
||||
|
TIMESERIES(true), |
||||
|
ACCESS_TOKEN, |
||||
|
X509, |
||||
|
MQTT_CLIENT_ID, |
||||
|
MQTT_USER_NAME, |
||||
|
MQTT_PASSWORD, |
||||
|
LWM2M_CLIENT_ENDPOINT("endpoint"), |
||||
|
LWM2M_CLIENT_SECURITY_CONFIG_MODE("securityConfigClientMode", LwM2MSecurityMode.NO_SEC.name()), |
||||
|
LWM2M_CLIENT_IDENTITY("identity"), |
||||
|
LWM2M_CLIENT_KEY("key"), |
||||
|
LWM2M_CLIENT_CERT("cert"), |
||||
|
LWM2M_BOOTSTRAP_SERVER_SECURITY_MODE("securityMode", LwM2MSecurityMode.NO_SEC.name()), |
||||
|
LWM2M_BOOTSTRAP_SERVER_PUBLIC_KEY_OR_ID("clientPublicKeyOrId"), |
||||
|
LWM2M_BOOTSTRAP_SERVER_SECRET_KEY("clientSecretKey"), |
||||
|
LWM2M_SERVER_SECURITY_MODE("securityMode", LwM2MSecurityMode.NO_SEC.name()), |
||||
|
LWM2M_SERVER_CLIENT_PUBLIC_KEY_OR_ID("clientPublicKeyOrId"), |
||||
|
LWM2M_SERVER_CLIENT_SECRET_KEY("clientSecretKey"), |
||||
|
IS_GATEWAY, |
||||
|
DESCRIPTION, |
||||
|
EDGE_LICENSE_KEY, |
||||
|
CLOUD_ENDPOINT, |
||||
|
ROUTING_KEY, |
||||
|
SECRET; |
||||
|
|
||||
|
private String key; |
||||
|
private String defaultValue; |
||||
|
private boolean isKv = false; |
||||
|
|
||||
|
BulkImportColumnType() { |
||||
|
} |
||||
|
|
||||
|
BulkImportColumnType(String key) { |
||||
|
this.key = key; |
||||
|
} |
||||
|
|
||||
|
BulkImportColumnType(String key, String defaultValue) { |
||||
|
this.key = key; |
||||
|
this.defaultValue = defaultValue; |
||||
|
} |
||||
|
|
||||
|
BulkImportColumnType(boolean isKv) { |
||||
|
this.isKv = isKv; |
||||
|
} |
||||
|
|
||||
|
BulkImportColumnType(String key, boolean isKv) { |
||||
|
this.key = key; |
||||
|
this.isKv = isKv; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
/** |
||||
|
* 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.service.importing; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
@Data |
||||
|
public class BulkImportRequest { |
||||
|
private String file; |
||||
|
private Mapping mapping; |
||||
|
|
||||
|
@Data |
||||
|
public static class Mapping { |
||||
|
private List<ColumnMapping> columns; |
||||
|
private Character delimiter; |
||||
|
private Boolean update; |
||||
|
private Boolean header; |
||||
|
} |
||||
|
|
||||
|
@Data |
||||
|
public static class ColumnMapping { |
||||
|
private BulkImportColumnType type; |
||||
|
private String key; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
/** |
||||
|
* 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.service.importing; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.util.LinkedList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Data |
||||
|
public class BulkImportResult<E> { |
||||
|
private int created = 0; |
||||
|
private int updated = 0; |
||||
|
private int errors = 0; |
||||
|
private List<String> errorsList = new LinkedList<>(); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
/** |
||||
|
* 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.service.importing; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
public class ImportedEntityInfo<E> { |
||||
|
private E entity; |
||||
|
private boolean isUpdated; |
||||
|
private E oldEntity; |
||||
|
private String relatedError; |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
/** |
||||
|
* 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.utils; |
||||
|
|
||||
|
import lombok.AccessLevel; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import org.apache.commons.csv.CSVFormat; |
||||
|
import org.apache.commons.csv.CSVRecord; |
||||
|
import org.apache.commons.io.input.CharSequenceReader; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.stream.Collectors; |
||||
|
import java.util.stream.Stream; |
||||
|
|
||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) |
||||
|
public class CsvUtils { |
||||
|
|
||||
|
public static List<List<String>> parseCsv(String content, Character delimiter) throws Exception { |
||||
|
CSVFormat csvFormat = delimiter.equals(',') ? CSVFormat.DEFAULT : CSVFormat.DEFAULT.withDelimiter(delimiter); |
||||
|
|
||||
|
List<CSVRecord> records; |
||||
|
try (CharSequenceReader reader = new CharSequenceReader(content)) { |
||||
|
records = csvFormat.parse(reader).getRecords(); |
||||
|
} |
||||
|
|
||||
|
return records.stream() |
||||
|
.map(record -> Stream.iterate(0, i -> i < record.size(), i -> i + 1) |
||||
|
.map(record::get) |
||||
|
.collect(Collectors.toList())) |
||||
|
.collect(Collectors.toList()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
/** |
||||
|
* 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.utils; |
||||
|
|
||||
|
import org.apache.commons.lang3.math.NumberUtils; |
||||
|
import org.thingsboard.server.common.data.kv.DataType; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
public class TypeCastUtil { |
||||
|
|
||||
|
private TypeCastUtil() {} |
||||
|
|
||||
|
public static Map.Entry<DataType, Object> castValue(String value) { |
||||
|
if (isNumber(value)) { |
||||
|
String formattedValue = value.replace(',', '.'); |
||||
|
try { |
||||
|
BigDecimal bd = new BigDecimal(formattedValue); |
||||
|
if (bd.stripTrailingZeros().scale() > 0 || isSimpleDouble(formattedValue)) { |
||||
|
if (bd.scale() <= 16) { |
||||
|
return Map.entry(DataType.DOUBLE, bd.doubleValue()); |
||||
|
} |
||||
|
} else { |
||||
|
return Map.entry(DataType.LONG, bd.longValueExact()); |
||||
|
} |
||||
|
} catch (RuntimeException ignored) {} |
||||
|
} else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { |
||||
|
return Map.entry(DataType.BOOLEAN, Boolean.parseBoolean(value)); |
||||
|
} |
||||
|
return Map.entry(DataType.STRING, value); |
||||
|
} |
||||
|
|
||||
|
private static boolean isNumber(String value) { |
||||
|
return NumberUtils.isNumber(value.replace(',', '.')); |
||||
|
} |
||||
|
|
||||
|
private static boolean isSimpleDouble(String valueAsString) { |
||||
|
return valueAsString.contains(".") && !valueAsString.contains("E") && !valueAsString.contains("e"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
/** |
||||
|
* 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.common.data.device.credentials.lwm2m; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonIgnore; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
import lombok.SneakyThrows; |
||||
|
import org.apache.commons.codec.binary.Hex; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
public abstract class AbstractLwM2MServerCredentialsWithKeys implements LwM2MServerCredentials { |
||||
|
|
||||
|
private String clientPublicKeyOrId; |
||||
|
private String clientSecretKey; |
||||
|
|
||||
|
@JsonIgnore |
||||
|
public byte[] getDecodedClientPublicKeyOrId() { |
||||
|
return getDecoded(clientPublicKeyOrId); |
||||
|
} |
||||
|
|
||||
|
@JsonIgnore |
||||
|
public byte[] getDecodedClientSecretKey() { |
||||
|
return getDecoded(clientSecretKey); |
||||
|
} |
||||
|
|
||||
|
@SneakyThrows |
||||
|
private static byte[] getDecoded(String key) { |
||||
|
return Hex.decodeHex(key.toLowerCase().toCharArray()); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
/** |
||||
|
* 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.common.data.device.credentials.lwm2m; |
||||
|
|
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
public class LwM2MBootstrapCredentials { |
||||
|
private LwM2MServerCredentials bootstrapServer; |
||||
|
private LwM2MServerCredentials lwm2mServer; |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
/** |
||||
|
* 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.common.data.device.credentials.lwm2m; |
||||
|
|
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
public class LwM2MDeviceCredentials { |
||||
|
private LwM2MClientCredentials client; |
||||
|
private LwM2MBootstrapCredentials bootstrap; |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
/** |
||||
|
* 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.common.data.device.credentials.lwm2m; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonIgnore; |
||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes; |
||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo; |
||||
|
|
||||
|
@JsonTypeInfo( |
||||
|
use = JsonTypeInfo.Id.NAME, |
||||
|
property = "securityMode") |
||||
|
@JsonSubTypes({ |
||||
|
@JsonSubTypes.Type(value = NoSecServerCredentials.class, name = "NO_SEC"), |
||||
|
@JsonSubTypes.Type(value = PSKServerCredentials.class, name = "PSK"), |
||||
|
@JsonSubTypes.Type(value = RPKServerCredentials.class, name = "RPK"), |
||||
|
@JsonSubTypes.Type(value = X509ServerCredentials.class, name = "X509") |
||||
|
}) |
||||
|
@JsonIgnoreProperties(ignoreUnknown = true) |
||||
|
public interface LwM2MServerCredentials { |
||||
|
|
||||
|
@JsonIgnore |
||||
|
LwM2MSecurityMode getSecurityMode(); |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* 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.common.data.device.credentials.lwm2m; |
||||
|
|
||||
|
public class NoSecServerCredentials implements LwM2MServerCredentials { |
||||
|
|
||||
|
@Override |
||||
|
public LwM2MSecurityMode getSecurityMode() { |
||||
|
return LwM2MSecurityMode.NO_SEC; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* 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.common.data.device.credentials.lwm2m; |
||||
|
|
||||
|
public class PSKServerCredentials extends AbstractLwM2MServerCredentialsWithKeys { |
||||
|
|
||||
|
@Override |
||||
|
public LwM2MSecurityMode getSecurityMode() { |
||||
|
return LwM2MSecurityMode.PSK; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* 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.common.data.device.credentials.lwm2m; |
||||
|
|
||||
|
public class RPKServerCredentials extends AbstractLwM2MServerCredentialsWithKeys { |
||||
|
|
||||
|
@Override |
||||
|
public LwM2MSecurityMode getSecurityMode() { |
||||
|
return LwM2MSecurityMode.RPK; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* 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.common.data.device.credentials.lwm2m; |
||||
|
|
||||
|
public class X509ServerCredentials extends AbstractLwM2MServerCredentialsWithKeys { |
||||
|
|
||||
|
@Override |
||||
|
public LwM2MSecurityMode getSecurityMode() { |
||||
|
return LwM2MSecurityMode.X509; |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue