Commit e583b0a1d3c07bed59ac80fd0e8c2ce3186cef5f

Authored by Yevhen Bondarenko
Committed by Andrew Shvayka
1 parent 7d0661a8

Feature/rest client (#2347)

* refactored URLs

* refactored

* refactored

* refactored

* refactored

* refactored rest client

* changed executorService from RestClient

* refactored rest client and JsonConverter
... ... @@ -31,7 +31,7 @@ import org.springframework.http.client.support.HttpRequestWrapper;
31 31 import org.springframework.util.StringUtils;
32 32 import org.springframework.web.client.HttpClientErrorException;
33 33 import org.springframework.web.client.RestTemplate;
34   -import org.springframework.web.context.request.async.DeferredResult;
  34 +import org.thingsboard.client.tools.utils.RestJsonConverter;
35 35 import org.thingsboard.server.common.data.AdminSettings;
36 36 import org.thingsboard.server.common.data.ClaimRequest;
37 37 import org.thingsboard.server.common.data.Customer;
... ... @@ -57,6 +57,8 @@ import org.thingsboard.server.common.data.id.CustomerId;
57 57 import org.thingsboard.server.common.data.id.DashboardId;
58 58 import org.thingsboard.server.common.data.id.DeviceId;
59 59 import org.thingsboard.server.common.data.id.EntityId;
  60 +import org.thingsboard.server.common.data.kv.AttributeKvEntry;
  61 +import org.thingsboard.server.common.data.kv.TsKvEntry;
60 62 import org.thingsboard.server.common.data.page.TextPageData;
61 63 import org.thingsboard.server.common.data.page.TextPageLink;
62 64 import org.thingsboard.server.common.data.page.TimePageData;
... ... @@ -74,6 +76,7 @@ import org.thingsboard.server.common.data.security.model.UserPasswordPolicy;
74 76 import org.thingsboard.server.common.data.widget.WidgetType;
75 77 import org.thingsboard.server.common.data.widget.WidgetsBundle;
76 78
  79 +import java.io.Closeable;
77 80 import java.io.IOException;
78 81 import java.net.URI;
79 82 import java.util.Collections;
... ... @@ -81,19 +84,23 @@ import java.util.HashMap;
81 84 import java.util.List;
82 85 import java.util.Map;
83 86 import java.util.Optional;
  87 +import java.util.concurrent.ExecutorService;
  88 +import java.util.concurrent.Executors;
  89 +import java.util.concurrent.Future;
84 90
85 91 import static org.springframework.util.StringUtils.isEmpty;
86 92
87 93 /**
88 94 * @author Andrew Shvayka
89 95 */
90   -public class RestClient implements ClientHttpRequestInterceptor {
  96 +public class RestClient implements ClientHttpRequestInterceptor, Closeable {
91 97 private static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization";
92 98 protected final RestTemplate restTemplate;
93 99 protected final String baseURL;
94 100 private String token;
95 101 private String refreshToken;
96 102 private final ObjectMapper objectMapper = new ObjectMapper();
  103 + private ExecutorService service = Executors.newWorkStealingPool(10);
97 104
98 105
99 106 protected static final String ACTIVATE_TOKEN_REGEX = "/api/noauth/activate?activateToken=";
... ... @@ -256,6 +263,7 @@ public class RestClient implements ClientHttpRequestInterceptor {
256 263 }
257 264 return restTemplate.postForEntity(baseURL + deviceCreationUrl, device, Device.class, params).getBody();
258 265 }
  266 +
259 267 public Asset createAsset(Asset asset) {
260 268 return restTemplate.postForEntity(baseURL + "/api/asset", asset, Asset.class).getBody();
261 269 }
... ... @@ -418,17 +426,17 @@ public class RestClient implements ClientHttpRequestInterceptor {
418 426 }
419 427
420 428 public void ackAlarm(String alarmId) {
421   - restTemplate.postForObject(baseURL + "/api/alarm/{alarmId}/ack", new Object(), Object.class, alarmId);
  429 + restTemplate.postForLocation(baseURL + "/api/alarm/{alarmId}/ack", null, alarmId);
422 430 }
423 431
424 432 public void clearAlarm(String alarmId) {
425   - restTemplate.postForObject(baseURL + "/api/alarm/{alarmId}/clear", new Object(), Object.class, alarmId);
  433 + restTemplate.postForLocation(baseURL + "/api/alarm/{alarmId}/clear", null, alarmId);
426 434 }
427 435
428   - public TimePageData<AlarmInfo> getAlarms(String entityType, String entityId, String searchStatus, String status, TimePageLink pageLink, Boolean fetchOriginator) {
  436 + public TimePageData<AlarmInfo> getAlarms(EntityId entityId, String searchStatus, String status, TimePageLink pageLink, Boolean fetchOriginator) {
429 437 Map<String, String> params = new HashMap<>();
430   - params.put("entityType", entityType);
431   - params.put("entityId", entityId);
  438 + params.put("entityType", entityId.getEntityType().name());
  439 + params.put("entityId", entityId.getId().toString());
432 440 params.put("searchStatus", searchStatus);
433 441 params.put("status", status);
434 442 params.put("fetchOriginator", String.valueOf(fetchOriginator));
... ... @@ -471,10 +479,10 @@ public class RestClient implements ClientHttpRequestInterceptor {
471 479 return urlParams;
472 480 }
473 481
474   - public Optional<AlarmSeverity> getHighestAlarmSeverity(String entityType, String entityId, String searchStatus, String status) {
  482 + public Optional<AlarmSeverity> getHighestAlarmSeverity(EntityId entityId, String searchStatus, String status) {
475 483 Map<String, String> params = new HashMap<>();
476   - params.put("entityType", entityType);
477   - params.put("entityId", entityId);
  484 + params.put("entityType", entityId.getEntityType().name());
  485 + params.put("entityId", entityId.getId().toString());
478 486 params.put("searchStatus", searchStatus);
479 487 params.put("status", status);
480 488 try {
... ... @@ -597,14 +605,14 @@ public class RestClient implements ClientHttpRequestInterceptor {
597 605 return assets.getBody();
598 606 }
599 607
600   - public List<Asset> getAssetsByIds(String[] assetIds) {
  608 + public List<Asset> getAssetsByIds(List<String> assetIds) {
601 609 return restTemplate.exchange(
602 610 baseURL + "/api/assets?assetIds={assetIds}",
603 611 HttpMethod.GET,
604 612 HttpEntity.EMPTY,
605 613 new ParameterizedTypeReference<List<Asset>>() {
606 614 },
607   - String.join(",", assetIds)).getBody();
  615 + listToString(assetIds)).getBody();
608 616 }
609 617
610 618 public List<Asset> findByQuery(AssetSearchQuery query) {
... ... @@ -657,10 +665,10 @@ public class RestClient implements ClientHttpRequestInterceptor {
657 665 return auditLog.getBody();
658 666 }
659 667
660   - public TimePageData<AuditLog> getAuditLogsByEntityId(String entityType, String entityId, String actionTypes, TimePageLink pageLink) {
  668 + public TimePageData<AuditLog> getAuditLogsByEntityId(EntityId entityId, String actionTypes, TimePageLink pageLink) {
661 669 Map<String, String> params = new HashMap<>();
662   - params.put("entityType", entityType);
663   - params.put("entityId", entityId);
  670 + params.put("entityType", entityId.getEntityType().name());
  671 + params.put("entityId", entityId.getId().toString());
664 672 params.put("actionTypes", actionTypes);
665 673 addPageLinkToParam(params, pageLink);
666 674
... ... @@ -700,14 +708,14 @@ public class RestClient implements ClientHttpRequestInterceptor {
700 708 }
701 709
702 710 public void logout() {
703   - restTemplate.exchange(URI.create(baseURL + "/api/auth/logout"), HttpMethod.POST, HttpEntity.EMPTY, Object.class);
  711 + restTemplate.postForLocation(baseURL + "/api/auth/logout", null);
704 712 }
705 713
706 714 public void changePassword(String currentPassword, String newPassword) {
707 715 ObjectNode changePasswordRequest = objectMapper.createObjectNode();
708 716 changePasswordRequest.put("currentPassword", currentPassword);
709 717 changePasswordRequest.put("newPassword", newPassword);
710   - restTemplate.exchange(URI.create(baseURL + "/api/auth/changePassword"), HttpMethod.POST, new HttpEntity<>(changePasswordRequest), Object.class);
  718 + restTemplate.postForLocation(baseURL + "/api/auth/changePassword", changePasswordRequest);
711 719 }
712 720
713 721 public Optional<UserPasswordPolicy> getUserPasswordPolicy() {
... ... @@ -731,7 +739,7 @@ public class RestClient implements ClientHttpRequestInterceptor {
731 739 public void requestResetPasswordByEmail(String email) {
732 740 ObjectNode resetPasswordByEmailRequest = objectMapper.createObjectNode();
733 741 resetPasswordByEmailRequest.put("email", email);
734   - restTemplate.exchange(URI.create(baseURL + "/api/noauth/resetPasswordByEmail"), HttpMethod.POST, new HttpEntity<>(resetPasswordByEmailRequest), Object.class);
  742 + restTemplate.postForLocation(baseURL + "/api/noauth/resetPasswordByEmail", resetPasswordByEmailRequest);
735 743 }
736 744
737 745 public Optional<JsonNode> activateUser(String userId, String password) {
... ... @@ -772,14 +780,14 @@ public class RestClient implements ClientHttpRequestInterceptor {
772 780 componentType).getBody();
773 781 }
774 782
775   - public List<ComponentDescriptor> getComponentDescriptorsByTypes(String[] componentTypes) {
  783 + public List<ComponentDescriptor> getComponentDescriptorsByTypes(List<String> componentTypes) {
776 784 return restTemplate.exchange(
777 785 baseURL + "/api/components?componentTypes={componentTypes}",
778 786 HttpMethod.GET,
779 787 HttpEntity.EMPTY,
780 788 new ParameterizedTypeReference<List<ComponentDescriptor>>() {
781 789 },
782   - String.join(",", componentTypes)).getBody();
  790 + listToString(componentTypes)).getBody();
783 791 }
784 792
785 793 public Optional<Customer> getCustomerById(String customerId) {
... ... @@ -915,7 +923,7 @@ public class RestClient implements ClientHttpRequestInterceptor {
915 923 }
916 924 }
917 925
918   - public Optional<Dashboard> updateDashboardCustomers(String dashboardId, String[] customerIds) {
  926 + public Optional<Dashboard> updateDashboardCustomers(String dashboardId, List<String> customerIds) {
919 927 try {
920 928 ResponseEntity<Dashboard> dashboard = restTemplate.postForEntity(baseURL + "/api/dashboard/{dashboardId}/customers", customerIds, Dashboard.class, dashboardId);
921 929 return Optional.ofNullable(dashboard.getBody());
... ... @@ -928,7 +936,7 @@ public class RestClient implements ClientHttpRequestInterceptor {
928 936 }
929 937 }
930 938
931   - public Optional<Dashboard> addDashboardCustomers(String dashboardId, String[] customerIds) {
  939 + public Optional<Dashboard> addDashboardCustomers(String dashboardId, List<String> customerIds) {
932 940 try {
933 941 ResponseEntity<Dashboard> dashboard = restTemplate.postForEntity(baseURL + "/api/dashboard/{dashboardId}/customers/add", customerIds, Dashboard.class, dashboardId);
934 942 return Optional.ofNullable(dashboard.getBody());
... ... @@ -941,7 +949,7 @@ public class RestClient implements ClientHttpRequestInterceptor {
941 949 }
942 950 }
943 951
944   - public Optional<Dashboard> removeDashboardCustomers(String dashboardId, String[] customerIds) {
  952 + public Optional<Dashboard> removeDashboardCustomers(String dashboardId, List<String> customerIds) {
945 953 try {
946 954 ResponseEntity<Dashboard> dashboard = restTemplate.postForEntity(baseURL + "/api/dashboard/{dashboardId}/customers/remove", customerIds, Dashboard.class, dashboardId);
947 955 return Optional.ofNullable(dashboard.getBody());
... ... @@ -1135,12 +1143,12 @@ public class RestClient implements ClientHttpRequestInterceptor {
1135 1143 .getBody();
1136 1144 }
1137 1145
1138   - public List<Device> getDevicesByIds(String[] deviceIds) {
  1146 + public List<Device> getDevicesByIds(List<String> deviceIds) {
1139 1147 return restTemplate.exchange(baseURL + "/api/devices?deviceIds={deviceIds}",
1140 1148 HttpMethod.GET,
1141 1149 HttpEntity.EMPTY, new ParameterizedTypeReference<List<Device>>() {
1142 1150 },
1143   - String.join(",", deviceIds)).getBody();
  1151 + listToString(deviceIds)).getBody();
1144 1152 }
1145 1153
1146 1154 public List<Device> findByQuery(DeviceSearchQuery query) {
... ... @@ -1161,28 +1169,22 @@ public class RestClient implements ClientHttpRequestInterceptor {
1161 1169 }).getBody();
1162 1170 }
1163 1171
1164   - public DeferredResult<ResponseEntity> claimDevice(String deviceName, ClaimRequest claimRequest) {
  1172 + public JsonNode claimDevice(String deviceName, ClaimRequest claimRequest) {
1165 1173 return restTemplate.exchange(
1166 1174 baseURL + "/api/customer/device/{deviceName}/claim",
1167 1175 HttpMethod.POST,
1168 1176 new HttpEntity<>(claimRequest),
1169   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1177 + new ParameterizedTypeReference<JsonNode>() {
1170 1178 },
1171 1179 deviceName).getBody();
1172 1180 }
1173 1181
1174   - public DeferredResult<ResponseEntity> reClaimDevice(String deviceName) {
1175   - return restTemplate.exchange(
1176   - baseURL + "/api/customer/device/{deviceName}/claim",
1177   - HttpMethod.DELETE,
1178   - HttpEntity.EMPTY,
1179   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
1180   - },
1181   - deviceName).getBody();
  1182 + public void reClaimDevice(String deviceName) {
  1183 + restTemplate.delete(baseURL + "/api/customer/device/{deviceName}/claim", deviceName);
1182 1184 }
1183 1185
1184 1186 public void saveRelation(EntityRelation relation) {
1185   - restTemplate.postForEntity(baseURL + "/api/relation", relation, Object.class);
  1187 + restTemplate.postForLocation(baseURL + "/api/relation", null);
1186 1188 }
1187 1189
1188 1190 public void deleteRelation(String fromId, String fromType, String relationType, String relationTypeGroup, String toId, String toType) {
... ... @@ -1196,8 +1198,8 @@ public class RestClient implements ClientHttpRequestInterceptor {
1196 1198 restTemplate.delete(baseURL + "/api/relation?fromId={fromId}&fromType={fromType}&relationType={relationType}&relationTypeGroup={relationTypeGroup}&toId={toId}&toType={toType}", params);
1197 1199 }
1198 1200
1199   - public void deleteRelations(String entityId, String entityType) {
1200   - restTemplate.delete(baseURL + "/api/relations?entityId={entityId}&entityType={entityType}", entityId, entityType);
  1201 + public void deleteRelations(EntityId entityId) {
  1202 + restTemplate.delete(baseURL + "/api/relations?entityId={entityId}&entityType={entityType}", entityId.getId().toString(), entityId.getEntityType().name());
1201 1203 }
1202 1204
1203 1205 public Optional<EntityRelation> getRelation(String fromId, String fromType, String relationType, String relationTypeGroup, String toId, String toType) {
... ... @@ -1448,10 +1450,10 @@ public class RestClient implements ClientHttpRequestInterceptor {
1448 1450 }
1449 1451 }
1450 1452
1451   - public TimePageData<Event> getEvents(String entityType, String entityId, String eventType, String tenantId, TimePageLink pageLink) {
  1453 + public TimePageData<Event> getEvents(EntityId entityId, String eventType, String tenantId, TimePageLink pageLink) {
1452 1454 Map<String, String> params = new HashMap<>();
1453   - params.put("entityType", entityType);
1454   - params.put("entityId", entityId);
  1455 + params.put("entityType", entityId.getEntityType().name());
  1456 + params.put("entityId", entityId.getId().toString());
1455 1457 params.put("eventType", eventType);
1456 1458 params.put("tenantId", tenantId);
1457 1459 addPageLinkToParam(params, pageLink);
... ... @@ -1465,10 +1467,10 @@ public class RestClient implements ClientHttpRequestInterceptor {
1465 1467 params).getBody();
1466 1468 }
1467 1469
1468   - public TimePageData<Event> getEvents(String entityType, String entityId, String tenantId, TimePageLink pageLink) {
  1470 + public TimePageData<Event> getEvents(EntityId entityId, String tenantId, TimePageLink pageLink) {
1469 1471 Map<String, String> params = new HashMap<>();
1470   - params.put("entityType", entityType);
1471   - params.put("entityId", entityId);
  1472 + params.put("entityType", entityId.getEntityType().name());
  1473 + params.put("entityId", entityId.getId().toString());
1472 1474 params.put("tenantId", tenantId);
1473 1475 addPageLinkToParam(params, pageLink);
1474 1476
... ... @@ -1481,22 +1483,16 @@ public class RestClient implements ClientHttpRequestInterceptor {
1481 1483 params).getBody();
1482 1484 }
1483 1485
1484   - public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(String deviceId, String requestBody) {
1485   - return restTemplate.exchange(
1486   - baseURL + "/api/plugins/rpc/oneway/{deviceId}",
1487   - HttpMethod.POST,
1488   - new HttpEntity<>(requestBody),
1489   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
1490   - },
1491   - deviceId).getBody();
  1486 + public void handleOneWayDeviceRPCRequest(String deviceId, JsonNode requestBody) {
  1487 + restTemplate.postForLocation(baseURL + "/api/plugins/rpc/oneway/{deviceId}", requestBody, deviceId);
1492 1488 }
1493 1489
1494   - public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(String deviceId, String requestBody) {
  1490 + public JsonNode handleTwoWayDeviceRPCRequest(String deviceId, JsonNode requestBody) {
1495 1491 return restTemplate.exchange(
1496 1492 baseURL + "/api/plugins/rpc/twoway/{deviceId}",
1497 1493 HttpMethod.POST,
1498 1494 new HttpEntity<>(requestBody),
1499   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1495 + new ParameterizedTypeReference<JsonNode>() {
1500 1496 },
1501 1497 deviceId).getBody();
1502 1498 }
... ... @@ -1590,206 +1586,233 @@ public class RestClient implements ClientHttpRequestInterceptor {
1590 1586 }
1591 1587 }
1592 1588
1593   - public DeferredResult<ResponseEntity> getAttributeKeys(String entityType, String entityId) {
  1589 + public List<String> getAttributeKeys(EntityId entityId) {
1594 1590 return restTemplate.exchange(
1595 1591 baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/keys/attributes",
1596 1592 HttpMethod.GET,
1597 1593 HttpEntity.EMPTY,
1598   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1594 + new ParameterizedTypeReference<List<String>>() {
1599 1595 },
1600   - entityType,
1601   - entityId).getBody();
  1596 + entityId.getEntityType().name(),
  1597 + entityId.getId().toString()).getBody();
1602 1598 }
1603 1599
1604   - public DeferredResult<ResponseEntity> getAttributeKeysByScope(String entityType, String entityId, String scope) {
  1600 + public List<String> getAttributeKeysByScope(EntityId entityId, String scope) {
1605 1601 return restTemplate.exchange(
1606 1602 baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/keys/attributes/{scope}",
1607 1603 HttpMethod.GET,
1608 1604 HttpEntity.EMPTY,
1609   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1605 + new ParameterizedTypeReference<List<String>>() {
1610 1606 },
1611   - entityType,
1612   - entityId,
  1607 + entityId.getEntityType().name(),
  1608 + entityId.getId().toString(),
1613 1609 scope).getBody();
1614 1610 }
1615 1611
1616   - public DeferredResult<ResponseEntity> getAttributesResponseEntity(String entityType, String entityId, String keys) {
1617   - return restTemplate.exchange(
  1612 + public List<AttributeKvEntry> getAttributeKvEntries(EntityId entityId, List<String> keys) {
  1613 + List<JsonNode> attributes = restTemplate.exchange(
1618 1614 baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/values/attributes?keys={keys}",
1619 1615 HttpMethod.GET,
1620 1616 HttpEntity.EMPTY,
1621   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1617 + new ParameterizedTypeReference<List<JsonNode>>() {
1622 1618 },
1623   - entityType,
1624   - entityId,
1625   - keys).getBody();
  1619 + entityId.getEntityType().name(),
  1620 + entityId.getId(),
  1621 + listToString(keys)).getBody();
  1622 +
  1623 + return RestJsonConverter.toAttributes(attributes);
1626 1624 }
1627 1625
1628   - public DeferredResult<ResponseEntity> getAttributesByScope(String entityType, String entityId, String scope, String keys) {
1629   - return restTemplate.exchange(
  1626 + public Future<List<AttributeKvEntry>> getAttributeKvEntriesAsync(EntityId entityId, List<String> keys) {
  1627 + return service.submit(() -> getAttributeKvEntries(entityId, keys));
  1628 + }
  1629 +
  1630 + public List<AttributeKvEntry> getAttributesByScope(EntityId entityId, String scope, List<String> keys) {
  1631 + List<JsonNode> attributes = restTemplate.exchange(
1630 1632 baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/values/attributes/{scope}?keys={keys}",
1631 1633 HttpMethod.GET,
1632 1634 HttpEntity.EMPTY,
1633   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1635 + new ParameterizedTypeReference<List<JsonNode>>() {
1634 1636 },
1635   - entityType,
1636   - entityId,
  1637 + entityId.getEntityType().name(),
  1638 + entityId.getId().toString(),
1637 1639 scope,
1638   - keys).getBody();
  1640 + listToString(keys)).getBody();
  1641 +
  1642 + return RestJsonConverter.toAttributes(attributes);
1639 1643 }
1640 1644
1641   - public DeferredResult<ResponseEntity> getTimeseriesKeys(String entityType, String entityId) {
  1645 + public List<String> getTimeseriesKeys(EntityId entityId) {
1642 1646 return restTemplate.exchange(
1643 1647 baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/keys/timeseries",
1644 1648 HttpMethod.GET,
1645 1649 HttpEntity.EMPTY,
1646   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1650 + new ParameterizedTypeReference<List<String>>() {
1647 1651 },
1648   - entityType,
1649   - entityId).getBody();
  1652 + entityId.getEntityType().name(),
  1653 + entityId.getId().toString()).getBody();
1650 1654 }
1651 1655
1652   - public DeferredResult<ResponseEntity> getLatestTimeseries(String entityType, String entityId, String keys) {
1653   - return restTemplate.exchange(
  1656 + public List<TsKvEntry> getLatestTimeseries(EntityId entityId, List<String> keys) {
  1657 + Map<String, List<JsonNode>> timeseries = restTemplate.exchange(
1654 1658 baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/values/timeseries?keys={keys}",
1655 1659 HttpMethod.GET,
1656 1660 HttpEntity.EMPTY,
1657   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1661 + new ParameterizedTypeReference<Map<String, List<JsonNode>>>() {
1658 1662 },
1659   - entityType,
1660   - entityId,
1661   - keys).getBody();
  1663 + entityId.getEntityType().name(),
  1664 + entityId.getId().toString(),
  1665 + listToString(keys)).getBody();
  1666 +
  1667 + return RestJsonConverter.toTimeseries(timeseries);
1662 1668 }
1663 1669
1664 1670
1665   - public DeferredResult<ResponseEntity> getTimeseries(String entityType, String entityId, String keys, Long startTs, Long endTs, Long interval, Integer limit, String agg) {
  1671 + public List<TsKvEntry> getTimeseries(EntityId entityId, List<String> keys, Long startTs, Long endTs, Long interval, Integer limit, String agg) {
1666 1672 Map<String, String> params = new HashMap<>();
1667   - params.put("entityType", entityType);
1668   - params.put("entityId", entityId);
1669   - params.put("keys", keys);
  1673 + params.put("entityType", entityId.getEntityType().name());
  1674 + params.put("entityId", entityId.getId().toString());
  1675 + params.put("keys", listToString(keys));
1670 1676 params.put("startTs", startTs.toString());
1671 1677 params.put("endTs", endTs.toString());
1672 1678 params.put("interval", interval == null ? "0" : interval.toString());
1673 1679 params.put("limit", limit == null ? "100" : limit.toString());
1674 1680 params.put("agg", agg == null ? "NONE" : agg);
1675 1681
1676   - return restTemplate.exchange(
  1682 + Map<String, List<JsonNode>> timeseries = restTemplate.exchange(
1677 1683 baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/values/timeseries?keys={keys}&startTs={startTs}&endTs={endTs}&interval={interval}&limit={limit}&agg={agg}",
1678 1684 HttpMethod.GET,
1679 1685 HttpEntity.EMPTY,
1680   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1686 + new ParameterizedTypeReference<Map<String, List<JsonNode>>>() {
1681 1687 },
1682 1688 params).getBody();
  1689 +
  1690 + return RestJsonConverter.toTimeseries(timeseries);
1683 1691 }
1684 1692
1685   - public DeferredResult<ResponseEntity> saveDeviceAttributes(String deviceId, String scope, JsonNode request) {
1686   - return restTemplate.exchange(
  1693 + public List<AttributeKvEntry> saveDeviceAttributes(String deviceId, String scope, JsonNode request) {
  1694 + List<JsonNode> attributes = restTemplate.exchange(
1687 1695 baseURL + "/api/plugins/telemetry/{deviceId}/{scope}",
1688 1696 HttpMethod.POST,
1689 1697 new HttpEntity<>(request),
1690   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1698 + new ParameterizedTypeReference<List<JsonNode>>() {
1691 1699 },
1692 1700 deviceId,
1693 1701 scope).getBody();
  1702 +
  1703 + return RestJsonConverter.toAttributes(attributes);
1694 1704 }
1695 1705
1696   - public DeferredResult<ResponseEntity> saveEntityAttributesV1(String entityType, String entityId, String scope, JsonNode request) {
1697   - return restTemplate.exchange(
  1706 + public List<AttributeKvEntry> saveEntityAttributesV1(EntityId entityId, String scope, JsonNode request) {
  1707 + List<JsonNode> attributes = restTemplate.exchange(
1698 1708 baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/{scope}",
1699 1709 HttpMethod.POST,
1700 1710 new HttpEntity<>(request),
1701   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1711 + new ParameterizedTypeReference<List<JsonNode>>() {
1702 1712 },
1703   - entityType,
1704   - entityId,
  1713 + entityId.getEntityType().name(),
  1714 + entityId.getId().toString(),
1705 1715 scope).getBody();
  1716 +
  1717 + return RestJsonConverter.toAttributes(attributes);
1706 1718 }
1707 1719
1708   - public DeferredResult<ResponseEntity> saveEntityAttributesV2(String entityType, String entityId, String scope, JsonNode request) {
1709   - return restTemplate.exchange(
  1720 + public List<AttributeKvEntry> saveEntityAttributesV2(EntityId entityId, String scope, JsonNode request) {
  1721 + List<JsonNode> attributes = restTemplate.exchange(
1710 1722 baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/attributes/{scope}",
1711 1723 HttpMethod.POST,
1712 1724 new HttpEntity<>(request),
1713   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1725 + new ParameterizedTypeReference<List<JsonNode>>() {
1714 1726 },
1715   - entityType,
1716   - entityId,
  1727 + entityId.getEntityType().name(),
  1728 + entityId.getId().toString(),
1717 1729 scope).getBody();
  1730 +
  1731 + return RestJsonConverter.toAttributes(attributes);
1718 1732 }
1719 1733
1720   - public DeferredResult<ResponseEntity> saveEntityTelemetry(String entityType, String entityId, String scope, String requestBody) {
1721   - return restTemplate.exchange(
  1734 + public List<TsKvEntry> saveEntityTelemetry(EntityId entityId, String scope, String requestBody) {
  1735 + Map<String, List<JsonNode>> timeseries = restTemplate.exchange(
1722 1736 baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/timeseries/{scope}",
1723 1737 HttpMethod.POST,
1724 1738 new HttpEntity<>(requestBody),
1725   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1739 + new ParameterizedTypeReference<Map<String, List<JsonNode>>>() {
1726 1740 },
1727   - entityType,
1728   - entityId,
  1741 + entityId.getEntityType().name(),
  1742 + entityId.getId().toString(),
1729 1743 scope).getBody();
  1744 +
  1745 + return RestJsonConverter.toTimeseries(timeseries);
1730 1746 }
1731 1747
1732   - public DeferredResult<ResponseEntity> saveEntityTelemetryWithTTL(String entityType, String entityId, String scope, Long ttl, String requestBody) {
1733   - return restTemplate.exchange(
  1748 + public List<TsKvEntry> saveEntityTelemetryWithTTL(EntityId entityId, String scope, Long ttl, String requestBody) {
  1749 + Map<String, List<JsonNode>> timeseries = restTemplate.exchange(
1734 1750 baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/timeseries/{scope}/{ttl}",
1735 1751 HttpMethod.POST,
1736 1752 new HttpEntity<>(requestBody),
1737   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1753 + new ParameterizedTypeReference<Map<String, List<JsonNode>>>() {
1738 1754 },
1739   - entityType,
1740   - entityId,
  1755 + entityId.getEntityType().name(),
  1756 + entityId.getId().toString(),
1741 1757 scope,
1742 1758 ttl).getBody();
  1759 +
  1760 + return RestJsonConverter.toTimeseries(timeseries);
1743 1761 }
1744 1762
1745   - public DeferredResult<ResponseEntity> deleteEntityTimeseries(String entityType,
1746   - String entityId,
1747   - String keys,
1748   - boolean deleteAllDataForKeys,
1749   - Long startTs,
1750   - Long endTs,
1751   - boolean rewriteLatestIfDeleted) {
  1763 + public List<TsKvEntry> deleteEntityTimeseries(EntityId entityId,
  1764 + List<String> keys,
  1765 + boolean deleteAllDataForKeys,
  1766 + Long startTs,
  1767 + Long endTs,
  1768 + boolean rewriteLatestIfDeleted) {
1752 1769 Map<String, String> params = new HashMap<>();
1753   - params.put("entityType", entityType);
1754   - params.put("entityId", entityId);
1755   - params.put("keys", keys);
  1770 + params.put("entityType", entityId.getEntityType().name());
  1771 + params.put("entityId", entityId.getId().toString());
  1772 + params.put("keys", listToString(keys));
1756 1773 params.put("deleteAllDataForKeys", String.valueOf(deleteAllDataForKeys));
1757 1774 params.put("startTs", startTs.toString());
1758 1775 params.put("endTs", endTs.toString());
1759 1776 params.put("rewriteLatestIfDeleted", String.valueOf(rewriteLatestIfDeleted));
1760 1777
1761   - return restTemplate.exchange(
  1778 + Map<String, List<JsonNode>> timeseries = restTemplate.exchange(
1762 1779 baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/timeseries/delete?keys={keys}&deleteAllDataForKeys={deleteAllDataForKeys}&startTs={startTs}&endTs={endTs}&rewriteLatestIfDeleted={rewriteLatestIfDeleted}",
1763 1780 HttpMethod.DELETE,
1764 1781 HttpEntity.EMPTY,
1765   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1782 + new ParameterizedTypeReference<Map<String, List<JsonNode>>>() {
1766 1783 },
1767 1784 params).getBody();
  1785 +
  1786 + return RestJsonConverter.toTimeseries(timeseries);
1768 1787 }
1769 1788
1770   - public DeferredResult<ResponseEntity> deleteEntityAttributes(String deviceId, String scope, String keys) {
1771   - return restTemplate.exchange(
  1789 + public List<AttributeKvEntry> deleteEntityAttributes(String deviceId, String scope, List<String> keys) {
  1790 + List<JsonNode> attributes = restTemplate.exchange(
1772 1791 baseURL + "/api/plugins/telemetry/{deviceId}/{scope}?keys={keys}",
1773 1792 HttpMethod.DELETE,
1774 1793 HttpEntity.EMPTY,
1775   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1794 + new ParameterizedTypeReference<List<JsonNode>>() {
1776 1795 },
1777 1796 deviceId,
1778 1797 scope,
1779   - keys).getBody();
  1798 + listToString(keys)).getBody();
  1799 +
  1800 + return RestJsonConverter.toAttributes(attributes);
1780 1801 }
1781 1802
1782   - public DeferredResult<ResponseEntity> deleteEntityAttributes(String entityType, String entityId, String scope, String keys) {
1783   - return restTemplate.exchange(
  1803 + public List<AttributeKvEntry> deleteEntityAttributes(EntityId entityId, String scope, List<String> keys) {
  1804 + List<JsonNode> attributes = restTemplate.exchange(
1784 1805 baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/{scope}?keys={keys}",
1785 1806 HttpMethod.DELETE,
1786 1807 HttpEntity.EMPTY,
1787   - new ParameterizedTypeReference<DeferredResult<ResponseEntity>>() {
  1808 + new ParameterizedTypeReference<List<JsonNode>>() {
1788 1809 },
1789   - entityType,
1790   - entityId,
  1810 + entityId.getEntityType().name(),
  1811 + entityId.getId().toString(),
1791 1812 scope,
1792   - keys).getBody();
  1813 + listToString(keys)).getBody();
  1814 +
  1815 + return RestJsonConverter.toAttributes(attributes);
1793 1816 }
1794 1817
1795 1818 public Optional<Tenant> getTenantById(String tenantId) {
... ... @@ -1860,7 +1883,7 @@ public class RestClient implements ClientHttpRequestInterceptor {
1860 1883 }
1861 1884
1862 1885 public void sendActivationEmail(String email) {
1863   - restTemplate.postForEntity(baseURL + "/api/user/sendActivationMail?email={email}", null, Object.class, email);
  1886 + restTemplate.postForLocation(baseURL + "/api/user/sendActivationMail?email={email}", null, email);
1864 1887 }
1865 1888
1866 1889 public String getActivationLink(String userId) {
... ... @@ -1900,10 +1923,9 @@ public class RestClient implements ClientHttpRequestInterceptor {
1900 1923 }
1901 1924
1902 1925 public void setUserCredentialsEnabled(String userId, boolean userCredentialsEnabled) {
1903   - restTemplate.postForEntity(
  1926 + restTemplate.postForLocation(
1904 1927 baseURL + "/api/user/{userId}/userCredentialsEnabled?serCredentialsEnabled={serCredentialsEnabled}",
1905 1928 null,
1906   - Object.class,
1907 1929 userId,
1908 1930 userCredentialsEnabled);
1909 1931 }
... ... @@ -2030,4 +2052,15 @@ public class RestClient implements ClientHttpRequestInterceptor {
2030 2052 params.put("textOffset", pageLink.getTextOffset());
2031 2053 }
2032 2054 }
  2055 +
  2056 + private String listToString(List<String> list) {
  2057 + return String.join(",", list);
  2058 + }
  2059 +
  2060 + @Override
  2061 + public void close() {
  2062 + if (service != null) {
  2063 + service.shutdown();
  2064 + }
  2065 + }
2033 2066 }
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.client.tools.utils;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import org.springframework.util.CollectionUtils;
  20 +import org.thingsboard.server.common.data.kv.AttributeKvEntry;
  21 +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
  22 +import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
  23 +import org.thingsboard.server.common.data.kv.BooleanDataEntry;
  24 +import org.thingsboard.server.common.data.kv.DoubleDataEntry;
  25 +import org.thingsboard.server.common.data.kv.KvEntry;
  26 +import org.thingsboard.server.common.data.kv.LongDataEntry;
  27 +import org.thingsboard.server.common.data.kv.StringDataEntry;
  28 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  29 +
  30 +import java.util.ArrayList;
  31 +import java.util.Collections;
  32 +import java.util.List;
  33 +import java.util.Map;
  34 +import java.util.stream.Collectors;
  35 +
  36 +public class RestJsonConverter {
  37 + private static final String KEY = "key";
  38 + private static final String VALUE = "value";
  39 + private static final String LAST_UPDATE_TS = "lastUpdateTs";
  40 + private static final String TS = "ts";
  41 +
  42 + private static final String CAN_T_PARSE_VALUE = "Can't parse value: ";
  43 +
  44 + public static List<AttributeKvEntry> toAttributes(List<JsonNode> attributes) {
  45 + if (!CollectionUtils.isEmpty(attributes)) {
  46 + return attributes.stream().map(attr -> {
  47 + KvEntry entry = parseValue(attr.get(KEY).asText(), attr.get(VALUE));
  48 + return new BaseAttributeKvEntry(entry, attr.get(LAST_UPDATE_TS).asLong());
  49 + }
  50 + ).collect(Collectors.toList());
  51 + } else {
  52 + return Collections.emptyList();
  53 + }
  54 + }
  55 +
  56 + public static List<TsKvEntry> toTimeseries(Map<String, List<JsonNode>> timeseries) {
  57 + if (!CollectionUtils.isEmpty(timeseries)) {
  58 + List<TsKvEntry> result = new ArrayList<>();
  59 + timeseries.forEach((key, values) ->
  60 + result.addAll(values.stream().map(ts -> {
  61 + KvEntry entry = parseValue(key, ts.get(VALUE));
  62 + return new BasicTsKvEntry(ts.get(TS).asLong(), entry);
  63 + }
  64 + ).collect(Collectors.toList()))
  65 + );
  66 + return result;
  67 + } else {
  68 + return Collections.emptyList();
  69 + }
  70 + }
  71 +
  72 + private static KvEntry parseValue(String key, JsonNode value) {
  73 + if (!value.isObject()) {
  74 + if (value.isBoolean()) {
  75 + return new BooleanDataEntry(key, value.asBoolean());
  76 + } else if (value.isDouble()) {
  77 + return new DoubleDataEntry(key, value.asDouble());
  78 + } else if (value.isLong()) {
  79 + return new LongDataEntry(key, value.asLong());
  80 + } else {
  81 + return new StringDataEntry(key, value.asText());
  82 + }
  83 + } else {
  84 + throw new RuntimeException(CAN_T_PARSE_VALUE + value);
  85 + }
  86 + }
  87 +}
... ...