Browse Source

Merge pull request #9534 from AndriiLandiak/fix/rest-api-node-trim-quotes

Fix trim double quotes for RestApiCallNode
pull/9583/head
Andrew Shvayka 3 years ago
committed by GitHub
parent
commit
d2d32c5add
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java
  2. 23
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java
  3. 4
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java
  4. 45
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java
  5. 23
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java

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

@ -194,7 +194,7 @@ public class TbHttpClient {
config.isIgnoreRequestBody()) {
entity = new HttpEntity<>(headers);
} else {
entity = new HttpEntity<>(getData(msg), headers);
entity = new HttpEntity<>(getData(msg, config.isIgnoreRequestBody(), config.isParseToPlainText()), headers);
}
URI uri = buildEncodedUri(endpointUrl);
@ -242,12 +242,19 @@ public class TbHttpClient {
return uri;
}
private String getData(TbMsg msg) {
String data = msg.getData();
private String getData(TbMsg tbMsg, boolean ignoreBody, boolean parseToPlainText) {
if (!ignoreBody && parseToPlainText) {
return parseJsonStringToPlainText(tbMsg.getData());
}
return tbMsg.getData();
}
if (config.isTrimDoubleQuotes()) {
protected String parseJsonStringToPlainText(String data) {
if (data.startsWith("\"") && data.endsWith("\"") && data.length() >= 2) {
final String dataBefore = data;
data = data.replaceAll("^\"|\"$", "");
try {
data = JacksonUtil.fromString(data, String.class);
} catch (Exception ignored) {}
log.trace("Trimming double quotes. Before trim: [{}], after trim: [{}]", dataBefore, data);
}

23
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java

@ -15,6 +15,8 @@
*/
package org.thingsboard.rule.engine.rest;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbContext;
@ -23,6 +25,7 @@ import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.rule.engine.external.TbAbstractExternalNode;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
@Slf4j
@ -30,6 +33,7 @@ import org.thingsboard.server.common.msg.TbMsg;
type = ComponentType.EXTERNAL,
name = "rest api call",
configClazz = TbRestApiCallNodeConfiguration.class,
version = 1,
nodeDescription = "Invoke REST API calls to external REST server",
nodeDetails = "Will invoke REST API call <code>GET | POST | PUT | DELETE</code> to external REST server. " +
"Message payload added into Request body. Configured attributes can be added into Headers from Message Metadata." +
@ -45,6 +49,8 @@ import org.thingsboard.server.common.msg.TbMsg;
)
public class TbRestApiCallNode extends TbAbstractExternalNode {
static final String PARSE_TO_PLAIN_TEXT = "parseToPlainText";
static final String TRIM_DOUBLE_QUOTES = "trimDoubleQuotes";
protected TbHttpClient httpClient;
@Override
@ -72,4 +78,21 @@ public class TbRestApiCallNode extends TbAbstractExternalNode {
}
}
@Override
public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
boolean hasChanges = false;
switch (fromVersion) {
case 0:
if (!oldConfiguration.has(PARSE_TO_PLAIN_TEXT) && oldConfiguration.has(TRIM_DOUBLE_QUOTES)) {
hasChanges = true;
((ObjectNode) oldConfiguration).put(PARSE_TO_PLAIN_TEXT, oldConfiguration.get(TRIM_DOUBLE_QUOTES).booleanValue());
((ObjectNode) oldConfiguration).remove(TRIM_DOUBLE_QUOTES);
}
break;
default:
break;
}
return new TbPair<>(hasChanges, oldConfiguration);
}
}

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

@ -37,7 +37,7 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration<TbRestA
private int readTimeoutMs;
private int maxParallelRequestsCount;
private boolean useRedisQueueForMsgPersistence;
private boolean trimDoubleQuotes;
private boolean parseToPlainText;
private boolean enableProxy;
private boolean useSystemProxyProperties;
private String proxyHost;
@ -58,7 +58,7 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration<TbRestA
configuration.setReadTimeoutMs(0);
configuration.setMaxParallelRequestsCount(0);
configuration.setUseRedisQueueForMsgPersistence(false);
configuration.setTrimDoubleQuotes(false);
configuration.setParseToPlainText(false);
configuration.setEnableProxy(false);
configuration.setCredentials(new AnonymousCredentials());
configuration.setIgnoreRequestBody(false);

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

@ -18,17 +18,19 @@ package org.thingsboard.rule.engine.rest;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import org.assertj.core.api.Assertions;
import org.awaitility.Awaitility;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.mockserver.integration.ClientAndServer;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.client.AsyncRestTemplate;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
@ -46,6 +48,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.willCallRealMethod;
import static org.mockito.Mockito.mock;
@ -61,13 +64,13 @@ public class TbHttpClientTest {
EventLoopGroup eventLoop;
TbHttpClient client;
@Before
@BeforeEach
public void setUp() throws Exception {
client = mock(TbHttpClient.class);
willCallRealMethod().given(client).getSharedOrCreateEventLoopGroup(any());
when(client.getSharedOrCreateEventLoopGroup(any())).thenCallRealMethod();
}
@After
@AfterEach
public void tearDown() throws Exception {
if (eventLoop != null) {
eventLoop.shutdownGracefully();
@ -91,7 +94,7 @@ public class TbHttpClientTest {
Mockito.when(client.buildEncodedUri(any())).thenCallRealMethod();
String url = "http://localhost:8080/";
URI uri = client.buildEncodedUri(url);
Assert.assertEquals(url, uri.toString());
Assertions.assertEquals(url, uri.toString());
}
@Test
@ -114,7 +117,7 @@ public class TbHttpClientTest {
String url = "http://192.168.1.1/data?d={\"a\": 12}";
String expected = "http://192.168.1.1/data?d=%7B%22a%22:%2012%7D";
URI uri = client.buildEncodedUri(url);
Assert.assertEquals(expected, uri.toString());
Assertions.assertEquals(expected, uri.toString());
}
@Test
@ -183,7 +186,7 @@ public class TbHttpClientTest {
verify(ctx, times(1)).tellSuccess(any());
verify(ctx, times(0)).tellFailure(any(), any());
Assert.assertEquals(successResponseBody, capturedData.getValue());
Assertions.assertEquals(successResponseBody, capturedData.getValue());
}
private ClientAndServer setUpDummyServer(String host, String path, String paramKey, String paramVal, String successResponseBody) {
@ -219,9 +222,21 @@ public class TbHttpClientTest {
Map<String, String> data = metaData.getData();
Assertions.assertThat(data).hasSize(2);
Assertions.assertThat(data.get("Content-Type")).isEqualTo("binary");
Assertions.assertThat(data.get("Set-Cookie")).isEqualTo("[\"sap-context=sap-client=075; path=/\",\"sap-token=sap-client=075; path=/\"]");
Assertions.assertEquals(2, data.size());
Assertions.assertEquals(data.get("Content-Type"), "binary");
Assertions.assertEquals(data.get("Set-Cookie"), "[\"sap-context=sap-client=075; path=/\",\"sap-token=sap-client=075; path=/\"]");
}
}
@ParameterizedTest
@ValueSource(strings = { "false", "\"", "\"\"", "\"This is a string with double quotes\"", "Path: /home/developer/test.txt",
"First line\nSecond line\n\nFourth line", "Before\rAfter", "Tab\tSeparated\tValues", "Test\bbackspace", "[]",
"[1, 2, 3]", "{\"key\": \"value\"}", "{\n\"temperature\": 25.5,\n\"humidity\": 50.2\n\"}", "Expression: (a + b) * c",
"世界", "Україна", "\u1F1FA\u1F1E6", "🇺🇦"})
public void testParseJsonStringToPlainText(String original) {
Mockito.when(client.parseJsonStringToPlainText(anyString())).thenCallRealMethod();
String serialized = JacksonUtil.toString(original);
Assertions.assertNotNull(serialized);
Assertions.assertEquals(original, client.parseJsonStringToPlainText(serialized));
}
}

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

@ -16,6 +16,7 @@
package org.thingsboard.rule.engine.rest;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.databind.JsonNode;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
@ -26,6 +27,7 @@ import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpRequestHandler;
import org.junit.After;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@ -39,6 +41,7 @@ import org.thingsboard.server.common.data.id.EntityId;
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.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
@ -91,7 +94,9 @@ public class TbRestApiCallNodeTest {
@After
public void teardown() {
server.stop();
if (server != null) {
server.stop();
}
}
@Test
@ -211,4 +216,20 @@ public class TbRestApiCallNodeTest {
assertEquals(TbMsg.EMPTY_JSON_OBJECT, dataCaptor.getValue());
}
@Test
public void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception {
var defaultConfig = new TbRestApiCallNodeConfiguration().defaultConfiguration();
var node = new TbRestApiCallNode();
String oldConfig = "{\"restEndpointUrlPattern\":\"http://localhost/api\",\"requestMethod\":\"POST\"," +
"\"useSimpleClientHttpFactory\":false,\"ignoreRequestBody\":false,\"enableProxy\":false," +
"\"useSystemProxyProperties\":false,\"proxyScheme\":null,\"proxyHost\":null,\"proxyPort\":0," +
"\"proxyUser\":null,\"proxyPassword\":null,\"readTimeoutMs\":0,\"maxParallelRequestsCount\":0," +
"\"headers\":{\"Content-Type\":\"application/json\"},\"useRedisQueueForMsgPersistence\":false," +
"\"trimQueue\":null,\"maxQueueSize\":null,\"credentials\":{\"type\":\"anonymous\"},\"trimDoubleQuotes\":true}";
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
Assertions.assertTrue(upgrade.getFirst());
Assertions.assertTrue(JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass()).isParseToPlainText());
}
}

Loading…
Cancel
Save