Browse Source

Refactor `QueryParam` into general-purpose `KeyValueEntry`

pull/14540/head
Dmytro Skarzhynets 8 months ago
parent
commit
0f288a1379
No known key found for this signature in database GPG Key ID: 2B51652F224037DF
  1. 18
      common/data/src/main/java/org/thingsboard/server/common/data/util/KeyValueEntry.java
  2. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java
  3. 29
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java
  4. 11
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java
  5. 13
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java

18
common/data/src/main/java/org/thingsboard/server/common/data/util/KeyValueEntry.java

@ -0,0 +1,18 @@
/**
* Copyright © 2016-2025 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.util;
public record KeyValueEntry<K, V>(K key, V value) {}

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java

@ -216,7 +216,7 @@ public class TbHttpClient {
if (config.getQueryParams() != null) {
processedQueryParams = config.getQueryParams().stream()
.map(param -> {
var processedParamName = TbNodeUtils.processPattern(param.name(), msg);
var processedParamName = TbNodeUtils.processPattern(param.key(), msg);
var processedParamValue = TbNodeUtils.processPattern(param.value(), msg);
return new QueryParam(processedParamName, processedParamValue);
})

29
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java

@ -15,7 +15,8 @@
*/
package org.thingsboard.rule.engine.rest;
import jakarta.validation.Valid;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.http.HttpHeaders;
@ -23,10 +24,12 @@ import org.springframework.http.MediaType;
import org.thingsboard.rule.engine.api.NodeConfiguration;
import org.thingsboard.rule.engine.credentials.AnonymousCredentials;
import org.thingsboard.rule.engine.credentials.ClientCredentials;
import org.thingsboard.server.common.data.util.KeyValueEntry;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Data
public class TbRestApiCallNodeConfiguration implements NodeConfiguration<TbRestApiCallNodeConfiguration> {
@ -35,7 +38,7 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration<TbRestA
private String requestMethod;
// null for legacy configs - triggers different URL encoding logic in TbHttpClient to preserve backward compatibility
// all new/modified configs since introduction of this property are forced to have non-null value by UI (and use new encoding logic)
private List<@NotNull @Valid QueryParam> queryParams;
private List<@NotNull KeyValueEntry<String, String>> queryParams;
private Map<String, String> headers;
private int readTimeoutMs;
private int maxParallelRequestsCount;
@ -50,9 +53,23 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration<TbRestA
private boolean ignoreRequestBody;
private int maxInMemoryBufferSizeInKb;
@JsonIgnore
@AssertTrue(message = "query parameter names and values must be non-null")
public boolean isValid() {
if (queryParams == null) {
return true; // @NotNull will handle this
}
for (KeyValueEntry<String, String> queryParam : queryParams) {
if (queryParam == null || queryParam.key() == null || queryParam.value() == null) {
return false;
}
}
return true;
}
@Override
public TbRestApiCallNodeConfiguration defaultConfiguration() {
TbRestApiCallNodeConfiguration configuration = new TbRestApiCallNodeConfiguration();
var configuration = new TbRestApiCallNodeConfiguration();
configuration.setRestEndpointUrlPattern("http://localhost/api");
configuration.setRequestMethod("POST");
configuration.setHeaders(Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE));
@ -68,11 +85,7 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration<TbRestA
}
public ClientCredentials getCredentials() {
if (this.credentials == null) {
return new AnonymousCredentials();
} else {
return this.credentials;
}
return Objects.requireNonNullElseGet(credentials, AnonymousCredentials::new);
}
}

11
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java

@ -31,6 +31,7 @@ import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.util.KeyValueEntry;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
@ -144,11 +145,11 @@ public class TbHttpClientTest {
config.setRestEndpointUrlPattern("http://localhost:" + server.getPort() + path);
config.setRequestMethod("GET");
config.setQueryParams(List.of(
new QueryParam("email", "${userEmail}"), // ${} from metadata
new QueryParam("device", "${deviceName}"), // ${} from metadata
new QueryParam("${dynamicParam}", "${dynamicValue}"), // ${} in both key and value
new QueryParam("temp", "$[temperature]"), // $[] from data
new QueryParam("location", "$[sensor.location]") // $[] from nested data
new KeyValueEntry<>("email", "${userEmail}"), // ${} from metadata
new KeyValueEntry<>("device", "${deviceName}"), // ${} from metadata
new KeyValueEntry<>("${dynamicParam}", "${dynamicValue}"), // ${} in both key and value
new KeyValueEntry<>("temp", "$[temperature]"), // $[] from data
new KeyValueEntry<>("location", "$[sensor.location]") // $[] from nested data
));
var metaData = new TbMsgMetaData();

13
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java

@ -45,6 +45,7 @@ import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.data.util.KeyValueEntry;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
@ -123,7 +124,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
public void shouldNotAllowNullQueryParamNames() {
// GIVEN
var config = new TbRestApiCallNodeConfiguration().defaultConfiguration();
config.setQueryParams(List.of(new QueryParam(null, "value")));
config.setQueryParams(List.of(new KeyValueEntry<>(null, "value")));
// WHEN-THEN
assertThatThrownBy(() -> new TbRestApiCallNode().init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))))
@ -131,14 +132,14 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
.matches(e -> ((TbNodeException) e).isUnrecoverable())
.rootCause()
.isInstanceOf(DataValidationException.class)
.hasMessageContaining("query parameter names must be non-null");
.hasMessageContaining("query parameter names and values must be non-null");
}
@Test
public void shouldNotAllowNullQueryParamValues() {
// GIVEN
var config = new TbRestApiCallNodeConfiguration().defaultConfiguration();
config.setQueryParams(List.of(new QueryParam("key", null)));
config.setQueryParams(List.of(new KeyValueEntry<>("key", null)));
// WHEN-THEN
assertThatThrownBy(() -> new TbRestApiCallNode().init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))))
@ -146,7 +147,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
.matches(e -> ((TbNodeException) e).isUnrecoverable())
.rootCause()
.isInstanceOf(DataValidationException.class)
.hasMessageContaining("query parameter values must be non-null");
.hasMessageContaining("query parameter names and values must be non-null");
}
@Test
@ -154,8 +155,8 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
// GIVEN
var config = new TbRestApiCallNodeConfiguration().defaultConfiguration();
var queryParams = new ArrayList<QueryParam>();
queryParams.add(new QueryParam("key", "value"));
var queryParams = new ArrayList<KeyValueEntry<String, String>>();
queryParams.add(new KeyValueEntry<>("key", "value"));
queryParams.add(null);
config.setQueryParams(queryParams);

Loading…
Cancel
Save