Browse Source

Merge pull request #14287 from dashevchenko/keysWithCommaFix

Fixed telemetry deletion for keys with comma
pull/14325/head
Viacheslav Klimov 10 months ago
committed by GitHub
parent
commit
91f1d1885c
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
  2. 80
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  3. 15
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  4. 60
      application/src/test/java/org/thingsboard/server/controller/TelemetryControllerTest.java
  5. 81
      ui-ngx/src/app/core/http/attribute.service.ts
  6. 31
      ui-ngx/src/app/core/http/http-utils.ts

6
application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java

@ -1630,11 +1630,13 @@ public class ControllerConstants {
protected static final String ENTITY_VIEW_INFO_DESCRIPTION = "Entity Views Info extends the Entity View with customer title and 'is public' flag. " + ENTITY_VIEW_DESCRIPTION;
protected static final String ATTRIBUTES_SCOPE_DESCRIPTION = "A string value representing the attributes scope. For example, 'SERVER_SCOPE'.";
protected static final String ATTRIBUTES_KEYS_DESCRIPTION = "A string value representing the comma-separated list of attributes keys. For example, 'active,inactivityAlarmTime'.";
protected static final String ATTRIBUTES_KEYS_DESCRIPTION = "A string value representing the comma-separated list of attributes keys. For example, 'active,inactivityAlarmTime'. " +
"If attribute keys contain comma, duplicate 'key' parameter for each key, for example '?key=my,key&key=my,second,key";
protected static final String ATTRIBUTES_JSON_REQUEST_DESCRIPTION = "A string value representing the json object. For example, '{\"key\":\"value\"}'. See API call description for more details.";
protected static final String TELEMETRY_KEYS_BASE_DESCRIPTION = "A string value representing the comma-separated list of telemetry keys.";
protected static final String TELEMETRY_KEYS_DESCRIPTION = TELEMETRY_KEYS_BASE_DESCRIPTION + " If keys are not selected, the result will return all latest time series. For example, 'temperature,humidity'.";
protected static final String TELEMETRY_KEYS_DESCRIPTION = TELEMETRY_KEYS_BASE_DESCRIPTION + " If keys are not selected, the result will return all latest time series. For example, 'temperature,humidity'. " +
"If telemetry keys contain comma, duplicate 'key' parameter for each key, for example '?key=my,key&key=my,second,key";
protected static final String TELEMETRY_SCOPE_DESCRIPTION = "Value is deprecated, reserved for backward compatibility and not used in the API call implementation. Specify any scope for compatibility";
protected static final String TELEMETRY_JSON_REQUEST_DESCRIPTION = "A JSON with the telemetry values. See API call description for more details.";

80
application/src/main/java/org/thingsboard/server/controller/TelemetryController.java

@ -36,6 +36,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@ -208,10 +209,12 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> getAttributes(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
List<String> keys = getKeys(keysStr, params);
SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr));
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keys));
}
@ -231,10 +234,12 @@ public class TelemetryController extends BaseController {
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope") AttributeScope scope,
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
List<String> keys = getKeys(keysStr, params);
SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr));
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keys));
}
@ApiOperation(value = "Get time series keys (getTimeseriesKeys)",
@ -270,10 +275,12 @@ public class TelemetryController extends BaseController {
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = TELEMETRY_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
@Parameter(description = STRICT_DATA_TYPES_DESCRIPTION)
@RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException {
@RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes,
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
List<String> keys = getKeys(keysStr, params);
SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
(result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes));
(result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keys, useStrictDataTypes));
}
@ApiOperation(value = "Get time series data (getTimeseries)",
@ -291,7 +298,7 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> getTimeseries(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = TELEMETRY_KEYS_BASE_DESCRIPTION, required = true) @RequestParam(name = "keys") String keys,
@Parameter(description = TELEMETRY_KEYS_BASE_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
@Parameter(description = "A long value representing the start timestamp of the time range in milliseconds, UTC.")
@RequestParam(name = "startTs") Long startTs,
@Parameter(description = "A long value representing the end timestamp of the time range in milliseconds, UTC.")
@ -312,9 +319,11 @@ public class TelemetryController extends BaseController {
@Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy,
@Parameter(description = STRICT_DATA_TYPES_DESCRIPTION)
@RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException {
@RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes,
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
List<String> keys = getKeys(keysStr, params);
DeferredResult<ResponseEntity> response = new DeferredResult<>();
Futures.addCallback(tbTelemetryService.getTimeseries(EntityIdFactory.getByTypeAndId(entityType, entityIdStr), toKeysList(keys), startTs, endTs,
Futures.addCallback(tbTelemetryService.getTimeseries(EntityIdFactory.getByTypeAndId(entityType, entityIdStr), keys, startTs, endTs,
intervalType, interval, timeZone, limit, Aggregation.valueOf(aggStr), orderBy, useStrictDataTypes, getCurrentUser()),
getTsKvListCallback(response, useStrictDataTypes), MoreExecutors.directExecutor());
return response;
@ -466,7 +475,7 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> deleteEntityTimeseries(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = TELEMETRY_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr,
@Parameter(description = TELEMETRY_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
@Parameter(description = "A boolean value to specify if should be deleted all data for selected keys or only data that are in the selected time range.")
@RequestParam(name = "deleteAllDataForKeys", defaultValue = "false") boolean deleteAllDataForKeys,
@Parameter(description = "A long value representing the start timestamp of removal time range in milliseconds.")
@ -476,16 +485,17 @@ public class TelemetryController extends BaseController {
@Parameter(description = "If the parameter is set to true, the latest telemetry can be removed, otherwise, in case that parameter is set to false the latest value will not removed.")
@RequestParam(name = "deleteLatest", required = false, defaultValue = "true") boolean deleteLatest,
@Parameter(description = "If the parameter is set to true, the latest telemetry will be rewritten in case that current latest value was removed, otherwise, in case that parameter is set to false the new latest value will not set.")
@RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException {
@RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted,
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
List<String> keys = getKeys(keysStr, params);
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted, deleteLatest);
return deleteTimeseries(entityId, keys, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted, deleteLatest);
}
private DeferredResult<ResponseEntity> deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys,
private DeferredResult<ResponseEntity> deleteTimeseries(EntityId entityIdStr, List<String> keys, boolean deleteAllDataForKeys,
Long startTs, Long endTs, boolean rewriteLatestIfDeleted, boolean deleteLatest) throws ThingsboardException {
List<String> keys = toKeysList(keysStr);
if (keys.isEmpty()) {
return getImmediateDeferredResult("Empty keys: " + keysStr, HttpStatus.BAD_REQUEST);
return getImmediateDeferredResult("Empty keys: " + keys, HttpStatus.BAD_REQUEST);
}
SecurityUser user = getCurrentUser();
@ -547,9 +557,11 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> deleteDeviceAttributes(
@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(DEVICE_ID) String deviceIdStr,
@Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope") AttributeScope scope,
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException {
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
List<String> keys = getKeys(keysStr, params);
EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
return deleteAttributes(entityId, scope, keysStr);
return deleteAttributes(entityId, scope, keys);
}
@ApiOperation(value = "Delete entity attributes (deleteEntityAttributes)",
@ -570,15 +582,20 @@ public class TelemetryController extends BaseController {
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"})) @PathVariable("scope") AttributeScope scope,
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException {
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
@RequestParam MultiValueMap<String, String> params) throws ThingsboardException {
List<String> keys = getKeys(keysStr, params);
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return deleteAttributes(entityId, scope, keysStr);
return deleteAttributes(entityId, scope, keys);
}
private List<String> getKeys(String keysStr, MultiValueMap<String, String> params) {
return params.get("key") != null ? params.get("key") : toKeysList(keysStr);
}
private DeferredResult<ResponseEntity> deleteAttributes(EntityId entityIdSrc, AttributeScope scope, String keysStr) throws ThingsboardException {
List<String> keys = toKeysList(keysStr);
private DeferredResult<ResponseEntity> deleteAttributes(EntityId entityIdSrc, AttributeScope scope, List<String> keys) throws ThingsboardException {
if (keys.isEmpty()) {
return getImmediateDeferredResult("Empty keys: " + keysStr, HttpStatus.BAD_REQUEST);
return getImmediateDeferredResult("Empty keys: " + keys, HttpStatus.BAD_REQUEST);
}
SecurityUser user = getCurrentUser();
@ -709,30 +726,29 @@ public class TelemetryController extends BaseController {
});
}
private void getLatestTimeseriesValuesCallback(@Nullable DeferredResult<ResponseEntity> result, SecurityUser user, EntityId entityId, String keys, Boolean useStrictDataTypes) {
private void getLatestTimeseriesValuesCallback(@Nullable DeferredResult<ResponseEntity> result, SecurityUser user, EntityId entityId, List<String> keys, Boolean useStrictDataTypes) {
ListenableFuture<List<TsKvEntry>> future;
if (StringUtils.isEmpty(keys)) {
if (keys.isEmpty()) {
future = tsService.findAllLatest(user.getTenantId(), entityId);
} else {
future = tsService.findLatest(user.getTenantId(), entityId, toKeysList(keys));
future = tsService.findLatest(user.getTenantId(), entityId, keys);
}
Futures.addCallback(future, getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor());
}
private void getAttributeValuesCallback(@Nullable DeferredResult<ResponseEntity> result, SecurityUser user, EntityId entityId, AttributeScope scope, String keys) {
List<String> keyList = toKeysList(keys);
FutureCallback<List<AttributeKvEntry>> callback = getAttributeValuesToResponseCallback(result, user, scope, entityId, keyList);
private void getAttributeValuesCallback(@Nullable DeferredResult<ResponseEntity> result, SecurityUser user, EntityId entityId, AttributeScope scope, List<String> keys) {
FutureCallback<List<AttributeKvEntry>> callback = getAttributeValuesToResponseCallback(result, user, scope, entityId, keys);
if (scope != null) {
if (keyList != null && !keyList.isEmpty()) {
Futures.addCallback(attributesService.find(user.getTenantId(), entityId, scope, keyList), callback, MoreExecutors.directExecutor());
if (keys != null && !keys.isEmpty()) {
Futures.addCallback(attributesService.find(user.getTenantId(), entityId, scope, keys), callback, MoreExecutors.directExecutor());
} else {
Futures.addCallback(attributesService.findAll(user.getTenantId(), entityId, scope), callback, MoreExecutors.directExecutor());
}
} else {
List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
for (AttributeScope tmpScope : AttributeScope.values()) {
if (keyList != null && !keyList.isEmpty()) {
futures.add(attributesService.find(user.getTenantId(), entityId, tmpScope, keyList));
if (keys != null && !keys.isEmpty()) {
futures.add(attributesService.find(user.getTenantId(), entityId, tmpScope, keys));
} else {
futures.add(attributesService.findAll(user.getTenantId(), entityId, tmpScope));
}

15
application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java

@ -749,6 +749,12 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
return doPost("/api/device-with-credentials", request, Device.class);
}
protected ResultActions doGetAsync(String urlTemplate, MultiValueMap<String, String> params) throws Exception {
MockHttpServletRequestBuilder getRequest = get(urlTemplate).params(params);
setJwtToken(getRequest);
return mockMvc.perform(asyncDispatch(mockMvc.perform(getRequest).andExpect(request().asyncStarted()).andReturn()));
}
protected ResultActions doGet(String urlTemplate, Object... urlVariables) throws Exception {
MockHttpServletRequestBuilder getRequest = get(urlTemplate, urlVariables);
setJwtToken(getRequest);
@ -948,6 +954,15 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
return mockMvc.perform(deleteRequest);
}
protected ResultActions doDeleteAsync(String urlTemplate, MultiValueMap<String, String> params) throws Exception {
MockHttpServletRequestBuilder deleteRequest = delete(urlTemplate)
.params(params);
setJwtToken(deleteRequest);
MvcResult result = mockMvc.perform(deleteRequest).andReturn();
result.getAsyncResult(DEFAULT_TIMEOUT);
return mockMvc.perform(asyncDispatch(result));
}
protected ResultActions doDeleteAsync(String urlTemplate, Long timeout, String... params) throws Exception {
MockHttpServletRequestBuilder deleteRequest = delete(urlTemplate, params);
setJwtToken(deleteRequest);

60
application/src/test/java/org/thingsboard/server/controller/TelemetryControllerTest.java

@ -19,7 +19,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.test.context.TestPropertySource;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
@ -232,6 +235,63 @@ public class TelemetryControllerTest extends AbstractControllerTest {
doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", invalidRequestBody2, String.class, status().isBadRequest());
}
@Test
public void testDeleteTelemetryByKeyWithComma() throws Exception {
loginTenantAdmin();
Device device = createDevice();
String tsKey = "key1,key2";
String testBody = JacksonUtil.newObjectNode()
.put(tsKey, "value")
.toString();
doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", testBody, String.class, status().isOk());
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("key", tsKey);
params.add("deleteAllDataForKeys", "true");
ObjectNode tsData = readResponse(doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries", params), ObjectNode.class);
assertThat(tsData.get("key1,key2").get(0).get("value").asText()).isEqualTo("value");
doDeleteAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/delete", params);
ObjectNode tsDataAfterDeletion = readResponse(doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries", params), ObjectNode.class);
Assert.assertTrue(tsDataAfterDeletion.get("key1,key2").get(0).get("value").isNull());
}
@Test
public void testDeleteTelemetryByKeysWithComma() throws Exception {
loginTenantAdmin();
Device device = createDevice();
String keyWithComma = "key1,key2";
String testBody = JacksonUtil.newObjectNode()
.put(keyWithComma, "value")
.toString();
doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", testBody, String.class, status().isOk());
String key = "key3";
String testBody2 = JacksonUtil.newObjectNode()
.put(key, "value")
.toString();
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("key", keyWithComma);
params.add("key", key);
params.add("deleteAllDataForKeys", "true");
doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", testBody2, String.class, status().isOk());
ObjectNode tsData = readResponse(doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries", params), ObjectNode.class);
assertThat(tsData.get("key1,key2").get(0).get("value").asText()).isEqualTo("value");
assertThat(tsData.get("key3").get(0).get("value").asText()).isEqualTo("value");
doDeleteAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/delete", params);
ObjectNode tsDataAfterDeletion = readResponse(doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries", params), ObjectNode.class);
Assert.assertTrue(tsDataAfterDeletion.get("key1,key2").get(0).get("value").isNull());
Assert.assertTrue(tsDataAfterDeletion.get("key3").get(0).get("value").isNull());
}
private Device createDevice() throws Exception {
String testToken = "TEST_TOKEN";

81
ui-ngx/src/app/core/http/attribute.service.ts

@ -15,7 +15,7 @@
///
import { Injectable } from '@angular/core';
import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
import { defaultHttpOptionsFromConfig, defaultHttpOptionsFromParams, RequestConfig } from './http-utils';
import { forkJoin, Observable, of } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { EntityId } from '@shared/models/id/entity-id';
@ -35,47 +35,37 @@ export class AttributeService {
public getEntityAttributes(entityId: EntityId, attributeScope?: AttributeScope,
keys?: Array<string>, config?: RequestConfig): Observable<Array<AttributeData>> {
let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/attributes`;
let queryParams: object = null;
if (attributeScope) {
url += `/${attributeScope}`;
}
if (keys && keys.length) {
url += `?keys=${keys.join(',')}`;
queryParams = {key: keys};
}
return this.http.get<Array<AttributeData>>(url, defaultHttpOptionsFromConfig(config));
return this.http.get<Array<AttributeData>>(url, defaultHttpOptionsFromParams(queryParams, config));
}
public deleteEntityAttributes(entityId: EntityId, attributeScope: AttributeScope, attributes: Array<AttributeData>,
config?: RequestConfig): Observable<any> {
const keys = attributes.map(attribute => encodeURIComponent(attribute.key)).join(',');
return this.http.delete(`/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/${attributeScope}` +
`?keys=${keys}`,
defaultHttpOptionsFromConfig(config));
return this.http.delete(`/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/${attributeScope}`,
defaultHttpOptionsFromParams({key: attributes.map(attribute => attribute.key)}, config));
}
public deleteEntityTimeseries(entityId: EntityId, timeseries: Array<AttributeData>, deleteAllDataForKeys = false,
startTs?: number, endTs?: number, rewriteLatestIfDeleted = false, deleteLatest = true,
config?: RequestConfig): Observable<any> {
const keys = timeseries.map(attribute => encodeURIComponent(attribute.key)).join(',');
let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/timeseries/delete?keys=${keys}`;
if (isDefinedAndNotNull(deleteAllDataForKeys)) {
url += `&deleteAllDataForKeys=${deleteAllDataForKeys}`;
}
if (isDefinedAndNotNull(rewriteLatestIfDeleted)) {
url += `&rewriteLatestIfDeleted=${rewriteLatestIfDeleted}`;
}
if (isDefinedAndNotNull(deleteLatest)) {
url += `&deleteLatest=${deleteLatest}`;
}
if (isDefinedAndNotNull(startTs)) {
url += `&startTs=${startTs}`;
}
if (isDefinedAndNotNull(endTs)) {
url += `&endTs=${endTs}`;
}
return this.http.delete(url, defaultHttpOptionsFromConfig(config));
const queryParams = {
key: timeseries.map(key => key.key),
deleteAllDataForKeys: deleteAllDataForKeys,
rewriteLatestIfDeleted: rewriteLatestIfDeleted,
deleteLatest: deleteLatest,
startTs: startTs,
endTs: endTs
};
return this.http.delete(`/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/timeseries/delete`,
defaultHttpOptionsFromParams(queryParams, config));
}
public saveEntityAttributes(entityId: EntityId, attributeScope: AttributeScope, attributes: Array<AttributeData>,
@ -138,32 +128,29 @@ export class AttributeService {
limit: number = 100, agg: AggregationType = AggregationType.NONE, interval?: number,
orderBy: DataSortOrder = DataSortOrder.DESC, useStrictDataTypes: boolean = false,
config?: RequestConfig): Observable<TimeseriesData> {
let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/timeseries?keys=${keys.join(',')}&startTs=${startTs}&endTs=${endTs}`;
if (isDefinedAndNotNull(limit)) {
url += `&limit=${limit}`;
}
if (isDefinedAndNotNull(agg)) {
url += `&agg=${agg}`;
}
if (isDefinedAndNotNull(interval)) {
url += `&interval=${interval}`;
}
if (isDefinedAndNotNull(orderBy)) {
url += `&orderBy=${orderBy}`;
}
if (isDefinedAndNotNull(useStrictDataTypes)) {
url += `&useStrictDataTypes=${useStrictDataTypes}`;
}
return this.http.get<TimeseriesData>(url, defaultHttpOptionsFromConfig(config));
const queryParams = {
key: keys,
startTs: startTs,
endTs: endTs,
limit: limit,
agg: agg,
interval: interval,
orderBy: orderBy,
useStrictDataTypes: useStrictDataTypes
}
return this.http.get<TimeseriesData>(`/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/timeseries`,
defaultHttpOptionsFromParams(queryParams, config));
}
public getEntityTimeseriesLatest(entityId: EntityId, keys?: Array<string>,
useStrictDataTypes = false, config?: RequestConfig): Observable<TimeseriesData> {
let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/timeseries?useStrictDataTypes=${useStrictDataTypes}`;
const queryParams: Record<string, any> = {
useStrictDataTypes: useStrictDataTypes
}
if (isDefinedAndNotNull(keys) && keys.length) {
url += `&keys=${keys.join(',')}`;
queryParams.key = keys;
}
return this.http.get<TimeseriesData>(url, defaultHttpOptionsFromConfig(config));
return this.http.get<TimeseriesData>(`/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/timeseries`,
defaultHttpOptionsFromParams(queryParams, config));
}
}

31
ui-ngx/src/app/core/http/http-utils.ts

@ -38,7 +38,10 @@ export function createDefaultHttpOptions(queryParamsOrConfig?: QueryParams | Req
if (hasRequestConfig(queryParamsOrConfig)) {
return defaultHttpOptionsFromConfig(queryParamsOrConfig as RequestConfig);
}
const queryParams = queryParamsOrConfig as QueryParams;
return defaultHttpOptionsFromParams(queryParamsOrConfig as QueryParams, config);
}
export function defaultHttpOptionsFromParams(queryParams?: QueryParams, config?: RequestConfig) {
const finalConfig = {
...config,
...(queryParams && { queryParams }),
@ -57,9 +60,11 @@ export function defaultHttpOptions(ignoreLoading: boolean = false,
ignoreErrors: boolean = false,
resendRequest: boolean = false,
queryParams?: QueryParams) {
const cleanedParams = cleanQueryParams(queryParams);
return {
headers: new HttpHeaders({'Content-Type': 'application/json'}),
params: new InterceptorHttpParams(new InterceptorConfig(ignoreLoading, ignoreErrors, resendRequest), queryParams)
params: new InterceptorHttpParams(new InterceptorConfig(ignoreLoading, ignoreErrors, resendRequest), cleanedParams)
};
}
@ -67,7 +72,27 @@ export function defaultHttpUploadOptions(ignoreLoading: boolean = false,
ignoreErrors: boolean = false,
resendRequest: boolean = false,
queryParams?: QueryParams) {
const cleanedParams = cleanQueryParams(queryParams);
return {
params: new InterceptorHttpParams(new InterceptorConfig(ignoreLoading, ignoreErrors, resendRequest), queryParams)
params: new InterceptorHttpParams(new InterceptorConfig(ignoreLoading, ignoreErrors, resendRequest), cleanedParams)
};
}
function cleanQueryParams(params?: QueryParams): QueryParams | undefined {
if (!params) {
return undefined;
}
const entries = Object.entries(params);
const cleanedEntries = entries.filter(
([_, value]) => value !== null && value !== undefined
);
if (!cleanedEntries.length) {
return undefined;
}
return Object.fromEntries(cleanedEntries);
}

Loading…
Cancel
Save