Browse Source

REST API call node: refactor to avoid boolean flag

pull/14540/head
Dmytro Skarzhynets 9 months ago
parent
commit
df795e252b
No known key found for this signature in database GPG Key ID: 2B51652F224037DF
  1. 12
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java
  2. 22
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java
  3. 2
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java
  4. 35
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java

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

@ -217,7 +217,7 @@ public class TbHttpClient {
String endpointUrl = TbNodeUtils.processPattern(config.getRestEndpointUrlPattern(), msg); String endpointUrl = TbNodeUtils.processPattern(config.getRestEndpointUrlPattern(), msg);
List<QueryParam> processedQueryParams; List<QueryParam> processedQueryParams;
if (config.isUseNewEncoding()) { if (config.getQueryParams() != null) {
processedQueryParams = config.getQueryParams().stream() processedQueryParams = config.getQueryParams().stream()
.map(param -> { .map(param -> {
var processedParamName = TbNodeUtils.processPattern(param.name(), msg); var processedParamName = TbNodeUtils.processPattern(param.name(), msg);
@ -312,9 +312,9 @@ public class TbHttpClient {
queryParams.forEach(param -> builder.addParameter(param.name(), param.value())); queryParams.forEach(param -> builder.addParameter(param.name(), param.value()));
return builder.build(); return builder.build();
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
throw new IllegalArgumentException(""" throw new IllegalArgumentException(
Invalid request URL: '%s'. The URL must be valid and properly encoded. "Invalid request URL: '" + endpointUrl + "'. The URL must be valid and properly encoded. " +
If URL contains special characters (e.g., spaces), they must be percent-encoded (e.g., '/my file/' should be '/my%%20file/').""".formatted(endpointUrl), e); "If URL contains special characters (e.g., spaces), they must be percent-encoded (e.g., '/my device/' should be '/my%20device/').", e);
} }
} }
@ -327,10 +327,10 @@ public class TbHttpClient {
* Pre-encoding the URL (e.g., {@code email=user%2Btag@test.com}) doesn't help * Pre-encoding the URL (e.g., {@code email=user%2Btag@test.com}) doesn't help
* because {@code .encode()} will double-encode it to {@code email=user%252Btag@test.com}. * because {@code .encode()} will double-encode it to {@code email=user%252Btag@test.com}.
* <p> * <p>
* Use {@link #buildEncodedUriNew} with a separate query params map, * Use {@link #buildEncodedUriNew} with a separate query params list,
* where values are encoded exactly once: {@code +} → {@code %2B}, {@code @} → {@code %40}. * where values are encoded exactly once: {@code +} → {@code %2B}, {@code @} → {@code %40}.
*/ */
@Deprecated(since = "4.2.1.2", forRemoval = true) // fixme: remove in major 5.0 @Deprecated(since = "4.2.1.2", forRemoval = true)
private URI buildEncodedUriLegacy(String endpointUrl) { private URI buildEncodedUriLegacy(String endpointUrl) {
return UriComponentsBuilder.fromUriString(endpointUrl).build().encode().toUri(); return UriComponentsBuilder.fromUriString(endpointUrl).build().encode().toUri();
} }

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

@ -15,10 +15,8 @@
*/ */
package org.thingsboard.rule.engine.rest; package org.thingsboard.rule.engine.rest;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
@ -37,7 +35,6 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration<TbRestA
private String restEndpointUrlPattern; private String restEndpointUrlPattern;
private String requestMethod; private String requestMethod;
private boolean useNewEncoding;
private List<@NotNull @Valid QueryParam> queryParams; private List<@NotNull @Valid QueryParam> queryParams;
private Map<String, String> headers; private Map<String, String> headers;
private boolean useSimpleClientHttpFactory; private boolean useSimpleClientHttpFactory;
@ -55,31 +52,12 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration<TbRestA
private boolean ignoreRequestBody; private boolean ignoreRequestBody;
private int maxInMemoryBufferSizeInKb; private int maxInMemoryBufferSizeInKb;
@JsonIgnore
@AssertTrue(message = "query parameters must be non-null if new encoding is used")
public boolean isNewEncodingConfigValid() {
if (useNewEncoding) {
return queryParams != null;
}
return true;
}
@JsonIgnore
@AssertTrue(message = "query parameters must be null if old encoding is used")
public boolean isLegacyEncodingConfigValid() {
if (!useNewEncoding) {
return queryParams == null;
}
return true;
}
@Override @Override
public TbRestApiCallNodeConfiguration defaultConfiguration() { public TbRestApiCallNodeConfiguration defaultConfiguration() {
TbRestApiCallNodeConfiguration configuration = new TbRestApiCallNodeConfiguration(); TbRestApiCallNodeConfiguration configuration = new TbRestApiCallNodeConfiguration();
configuration.setRestEndpointUrlPattern("http://localhost/api"); configuration.setRestEndpointUrlPattern("http://localhost/api");
configuration.setRequestMethod("POST"); configuration.setRequestMethod("POST");
configuration.setHeaders(Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)); configuration.setHeaders(Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE));
configuration.setUseNewEncoding(true);
configuration.setQueryParams(Collections.emptyList()); configuration.setQueryParams(Collections.emptyList());
configuration.setUseSimpleClientHttpFactory(false); configuration.setUseSimpleClientHttpFactory(false);
configuration.setReadTimeoutMs(0); configuration.setReadTimeoutMs(0);

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

@ -144,7 +144,6 @@ public class TbHttpClientTest {
config.setRestEndpointUrlPattern("http://localhost:" + server.getPort() + path); config.setRestEndpointUrlPattern("http://localhost:" + server.getPort() + path);
config.setRequestMethod("GET"); config.setRequestMethod("GET");
config.setUseSimpleClientHttpFactory(true); config.setUseSimpleClientHttpFactory(true);
config.setUseNewEncoding(true);
config.setQueryParams(List.of( config.setQueryParams(List.of(
new QueryParam("email", "${userEmail}"), // ${} from metadata new QueryParam("email", "${userEmail}"), // ${} from metadata
new QueryParam("device", "${deviceName}"), // ${} from metadata new QueryParam("device", "${deviceName}"), // ${} from metadata
@ -192,7 +191,6 @@ public class TbHttpClientTest {
var config = new TbRestApiCallNodeConfiguration().defaultConfiguration(); var config = new TbRestApiCallNodeConfiguration().defaultConfiguration();
config.setRestEndpointUrlPattern("http://localhost:" + server.getPort() + path + "?data=" + paramValue); config.setRestEndpointUrlPattern("http://localhost:" + server.getPort() + path + "?data=" + paramValue);
config.setRequestMethod("GET"); config.setRequestMethod("GET");
config.setUseNewEncoding(false);
config.setQueryParams(null); config.setQueryParams(null);
var msg = TbMsg.newMsg() var msg = TbMsg.newMsg()

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

@ -51,6 +51,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@ -59,7 +60,6 @@ import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
@ -123,7 +123,6 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
public void shouldNotAllowNullQueryParamNames() { public void shouldNotAllowNullQueryParamNames() {
// GIVEN // GIVEN
var config = new TbRestApiCallNodeConfiguration().defaultConfiguration(); var config = new TbRestApiCallNodeConfiguration().defaultConfiguration();
config.setUseNewEncoding(true);
config.setQueryParams(List.of(new QueryParam(null, "value"))); config.setQueryParams(List.of(new QueryParam(null, "value")));
// WHEN-THEN // WHEN-THEN
@ -139,7 +138,6 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
public void shouldNotAllowNullQueryParamValues() { public void shouldNotAllowNullQueryParamValues() {
// GIVEN // GIVEN
var config = new TbRestApiCallNodeConfiguration().defaultConfiguration(); var config = new TbRestApiCallNodeConfiguration().defaultConfiguration();
config.setUseNewEncoding(true);
config.setQueryParams(List.of(new QueryParam("key", null))); config.setQueryParams(List.of(new QueryParam("key", null)));
// WHEN-THEN // WHEN-THEN
@ -152,33 +150,22 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
} }
@Test @Test
public void shouldAllowOnlyNullQueryParamsIfOldEncodingIsUsed() { public void shouldNotAllowNullQueryParamEntries() {
// GIVEN // GIVEN
var config = new TbRestApiCallNodeConfiguration().defaultConfiguration(); var config = new TbRestApiCallNodeConfiguration().defaultConfiguration();
config.setUseNewEncoding(false);
config.setQueryParams(List.of(new QueryParam("key", "value")));
// WHEN-THEN var queryParams = new ArrayList<QueryParam>();
assertThatThrownBy(() -> new TbRestApiCallNode().init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) queryParams.add(new QueryParam("key", "value"));
.isInstanceOf(TbNodeException.class) queryParams.add(null);
.hasRootCauseInstanceOf(DataValidationException.class) config.setQueryParams(queryParams);
.hasRootCauseMessage("'" + ruleNode.getName() + "' node configuration is invalid: query parameters must be null if old encoding is used")
.matches(e -> ((TbNodeException) e).isUnrecoverable());
}
@Test
public void shouldNotAllowNullQueryParamsIfNewEncodingIsUsed() {
// GIVEN
var config = new TbRestApiCallNodeConfiguration().defaultConfiguration();
config.setUseNewEncoding(true);
config.setQueryParams(null);
// WHEN-THEN // WHEN-THEN
assertThatThrownBy(() -> new TbRestApiCallNode().init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) assertThatThrownBy(() -> new TbRestApiCallNode().init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))))
.isInstanceOf(TbNodeException.class) .isInstanceOf(TbNodeException.class)
.hasRootCauseInstanceOf(DataValidationException.class) .matches(e -> ((TbNodeException) e).isUnrecoverable())
.hasRootCauseMessage("'" + ruleNode.getName() + "' node configuration is invalid: query parameters must be non-null if new encoding is used") .rootCause()
.matches(e -> ((TbNodeException) e).isUnrecoverable()); .isInstanceOf(DataValidationException.class)
.hasMessageContaining("must not be null");
} }
@Test @Test
@ -187,7 +174,6 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
var defaultConfig = new TbRestApiCallNodeConfiguration().defaultConfiguration(); var defaultConfig = new TbRestApiCallNodeConfiguration().defaultConfiguration();
// THEN // THEN
assertTrue(defaultConfig.isUseNewEncoding());
assertEquals(Collections.emptyList(), defaultConfig.getQueryParams()); assertEquals(Collections.emptyList(), defaultConfig.getQueryParams());
} }
@ -224,7 +210,6 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
var config = JacksonUtil.fromString(configJson, TbRestApiCallNodeConfiguration.class); var config = JacksonUtil.fromString(configJson, TbRestApiCallNodeConfiguration.class);
// THEN // THEN
assertFalse(config.isUseNewEncoding());
assertNull(config.getQueryParams()); assertNull(config.getQueryParams());
} }

Loading…
Cancel
Save