/** * Copyright © 2017 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.gateway.extensions.sigfox.conf.mapping; import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.JsonPath; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import org.thingsboard.gateway.extensions.sigfox.conf.mapping.transformer.DataValueTransformer; import org.thingsboard.gateway.service.data.DeviceData; import org.thingsboard.gateway.util.converter.AbstractJsonConverter; import org.thingsboard.server.common.data.kv.*; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Collectors; @Data @EqualsAndHashCode(callSuper = true) @Slf4j public class SigfoxDeviceDataConverter extends AbstractJsonConverter { public static final Pattern TAG_PATTERN = Pattern.compile("\\$\\{(.*?)\\}"); private String filterExpression; private String deviceNameJsonExpression; private final List<AttributesMapping> attributes; private final List<TimeseriesMapping> timeseries; public boolean isApplicable(String body) { if (filterExpression == null || filterExpression.isEmpty()) { return true; } else { try { List jsonArray = JsonPath.parse(body).read(filterExpression); return !jsonArray.isEmpty(); } catch (RuntimeException e) { log.debug("Failed to apply filter expression: {}", filterExpression, e); throw new RuntimeException("Failed to apply filter expression " + filterExpression, e); } } } public DeviceData parseBody(String body) { try { DocumentContext document = JsonPath.parse(body); if (filterExpression != null && !filterExpression.isEmpty()) { try { log.debug("Data before filtering {}", body); List jsonArray = document.read(filterExpression); Object jsonObj = jsonArray.get(0); // take 1st element from filtered array (jayway jsonpath library limitation) document = JsonPath.parse(jsonObj); body = document.jsonString(); log.debug("Data after filtering {}", body); } catch (RuntimeException e) { log.debug("Failed to apply filter expression: {}", filterExpression, e); throw new RuntimeException("Failed to apply filter expression " + filterExpression, e); } } long ts = System.currentTimeMillis(); String deviceName = eval(document, deviceNameJsonExpression); if (!StringUtils.isEmpty(deviceName)) { List<KvEntry> attrData = getKvEntries(JsonPath.parse(body), attributes); List<TsKvEntry> tsData = getKvEntries(JsonPath.parse(body), timeseries).stream() .map(kv -> new BasicTsKvEntry(ts, kv)) .collect(Collectors.toList()); return new DeviceData(deviceName, attrData, tsData); } } catch (Exception e) { log.error("Exception occurred while parsing json request body [{}]", body, e); throw new RuntimeException("Exception occurred while parsing json request body [" + body +"]", e); } return null; } private List<KvEntry> getKvEntries(DocumentContext document, List<? extends SigfoxKVMapping> mappings) { List<KvEntry> result = new ArrayList<>(); if (mappings != null) { for (SigfoxKVMapping mapping : mappings) { String key = eval(document, mapping.getKey()); String strVal = eval(document, mapping.getValue()); result.add(getKvEntry(mapping, key, strVal)); } } return result; } private BasicKvEntry getKvEntry(SigfoxKVMapping mapping, String key, String strVal) { DataValueTransformer transformer = mapping.getTransformer(); if (transformer != null) { try { switch (mapping.getType().getDataType()) { case STRING: return new StringDataEntry(key, transformer.transformToString(strVal)); case BOOLEAN: return new BooleanDataEntry(key, transformer.transformToBoolean(strVal)); case DOUBLE: return new DoubleDataEntry(key, transformer.transformToDouble(strVal)); case LONG: return new LongDataEntry(key, transformer.transformToLong(strVal)); } } catch (Exception e) { log.error("Transformer [{}] can't be applied to field with key [{}] and value [{}]", transformer.getName(), key, strVal); throw e; } } else { switch (mapping.getType().getDataType()) { case STRING: return new StringDataEntry(key, strVal); case BOOLEAN: return new BooleanDataEntry(key, Boolean.valueOf(strVal)); case DOUBLE: return new DoubleDataEntry(key, Double.valueOf(strVal)); case LONG: return new LongDataEntry(key, Long.valueOf(strVal)); } } log.error("No mapping found for data type [{}]", mapping.getType().getDataType()); throw new IllegalArgumentException("No mapping found for data type [" + mapping.getType().getDataType() + "]"); } }