Merge branch 'restApiCallNodeJsonVarFix' of github.com:desoliture1/thingsboard into develop/3.4
This commit is contained in:
commit
6f78d8245a
13
pom.xml
13
pom.xml
@ -128,6 +128,7 @@
|
|||||||
<java-websocket.version>1.5.2</java-websocket.version>
|
<java-websocket.version>1.5.2</java-websocket.version>
|
||||||
<jupiter.version>5.8.2</jupiter.version> <!-- keep the same version as spring-boot-starter-test depend on jupiter-->
|
<jupiter.version>5.8.2</jupiter.version> <!-- keep the same version as spring-boot-starter-test depend on jupiter-->
|
||||||
<json-path.version>2.6.0</json-path.version>
|
<json-path.version>2.6.0</json-path.version>
|
||||||
|
<mock-server.version>5.13.1</mock-server.version>
|
||||||
<spring-test-dbunit.version>1.3.0</spring-test-dbunit.version> <!-- 2016 -->
|
<spring-test-dbunit.version>1.3.0</spring-test-dbunit.version> <!-- 2016 -->
|
||||||
<takari-cpsuite.version>1.2.7</takari-cpsuite.version> <!-- 2015 -->
|
<takari-cpsuite.version>1.2.7</takari-cpsuite.version> <!-- 2015 -->
|
||||||
<!-- BLACKBOX TEST SCOPE -->
|
<!-- BLACKBOX TEST SCOPE -->
|
||||||
@ -1877,6 +1878,18 @@
|
|||||||
<version>${zeroturnaround.version}</version>
|
<version>${zeroturnaround.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mock-server</groupId>
|
||||||
|
<artifactId>mockserver-netty</artifactId>
|
||||||
|
<version>${mock-server.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mock-server</groupId>
|
||||||
|
<artifactId>mockserver-client-java</artifactId>
|
||||||
|
<version>${mock-server.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.opensmpp</groupId>
|
<groupId>org.opensmpp</groupId>
|
||||||
<artifactId>opensmpp-core</artifactId>
|
<artifactId>opensmpp-core</artifactId>
|
||||||
|
|||||||
@ -136,6 +136,15 @@
|
|||||||
<artifactId>awaitility</artifactId>
|
<artifactId>awaitility</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mock-server</groupId>
|
||||||
|
<artifactId>mockserver-netty</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mock-server</groupId>
|
||||||
|
<artifactId>mockserver-client-java</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.cassandraunit</groupId>
|
<groupId>org.cassandraunit</groupId>
|
||||||
<artifactId>cassandra-unit</artifactId>
|
<artifactId>cassandra-unit</artifactId>
|
||||||
|
|||||||
@ -40,6 +40,7 @@ import org.springframework.util.concurrent.ListenableFuture;
|
|||||||
import org.springframework.util.concurrent.ListenableFutureCallback;
|
import org.springframework.util.concurrent.ListenableFutureCallback;
|
||||||
import org.springframework.web.client.AsyncRestTemplate;
|
import org.springframework.web.client.AsyncRestTemplate;
|
||||||
import org.springframework.web.client.HttpClientErrorException;
|
import org.springframework.web.client.HttpClientErrorException;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
import org.thingsboard.rule.engine.api.TbContext;
|
import org.thingsboard.rule.engine.api.TbContext;
|
||||||
import org.thingsboard.rule.engine.api.TbNodeException;
|
import org.thingsboard.rule.engine.api.TbNodeException;
|
||||||
import org.thingsboard.rule.engine.api.TbRelationTypes;
|
import org.thingsboard.rule.engine.api.TbRelationTypes;
|
||||||
@ -54,6 +55,7 @@ import javax.net.ssl.SSLContext;
|
|||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import java.net.Authenticator;
|
import java.net.Authenticator;
|
||||||
import java.net.PasswordAuthentication;
|
import java.net.PasswordAuthentication;
|
||||||
|
import java.net.URI;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
@ -189,8 +191,9 @@ public class TbHttpClient {
|
|||||||
entity = new HttpEntity<>(msg.getData(), headers);
|
entity = new HttpEntity<>(msg.getData(), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
URI uri = buildEncodedUri(endpointUrl);
|
||||||
ListenableFuture<ResponseEntity<String>> future = httpClient.exchange(
|
ListenableFuture<ResponseEntity<String>> future = httpClient.exchange(
|
||||||
endpointUrl, method, entity, String.class);
|
uri, method, entity, String.class);
|
||||||
future.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
|
future.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Throwable throwable) {
|
public void onFailure(Throwable throwable) {
|
||||||
@ -214,6 +217,28 @@ public class TbHttpClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public URI buildEncodedUri(String endpointUrl) {
|
||||||
|
if (endpointUrl == null) {
|
||||||
|
throw new RuntimeException("Url string cannot be null!");
|
||||||
|
}
|
||||||
|
if (endpointUrl.isEmpty()) {
|
||||||
|
throw new RuntimeException("Url string cannot be empty!");
|
||||||
|
}
|
||||||
|
|
||||||
|
URI uri = UriComponentsBuilder.fromUriString(endpointUrl).build().encode().toUri();
|
||||||
|
if (uri.getScheme() == null || uri.getScheme().isEmpty()) {
|
||||||
|
throw new RuntimeException("Transport scheme(protocol) must be provided!");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean authorityNotValid = uri.getAuthority() == null || uri.getAuthority().isEmpty();
|
||||||
|
boolean hostNotValid = uri.getHost() == null || uri.getHost().isEmpty();
|
||||||
|
if (authorityNotValid || hostNotValid) {
|
||||||
|
throw new RuntimeException("Url string is invalid!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
private TbMsg processResponse(TbContext ctx, TbMsg origMsg, ResponseEntity<String> response) {
|
private TbMsg processResponse(TbContext ctx, TbMsg origMsg, ResponseEntity<String> response) {
|
||||||
TbMsgMetaData metaData = origMsg.getMetaData();
|
TbMsgMetaData metaData = origMsg.getMetaData();
|
||||||
metaData.putValue(STATUS, response.getStatusCode().name());
|
metaData.putValue(STATUS, response.getStatusCode().name());
|
||||||
|
|||||||
@ -18,16 +18,38 @@ package org.thingsboard.rule.engine.rest;
|
|||||||
|
|
||||||
import io.netty.channel.EventLoopGroup;
|
import io.netty.channel.EventLoopGroup;
|
||||||
import io.netty.channel.nio.NioEventLoopGroup;
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
import org.awaitility.Awaitility;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockserver.integration.ClientAndServer;
|
||||||
|
import org.springframework.web.client.AsyncRestTemplate;
|
||||||
|
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.msg.TbMsg;
|
||||||
|
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.BDDMockito.willCallRealMethod;
|
import static org.mockito.BDDMockito.willCallRealMethod;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.mockserver.integration.ClientAndServer.startClientAndServer;
|
||||||
|
import static org.mockserver.model.HttpRequest.request;
|
||||||
|
import static org.mockserver.model.HttpResponse.response;
|
||||||
|
|
||||||
public class TbHttpClientTest {
|
public class TbHttpClientTest {
|
||||||
|
|
||||||
@ -58,4 +80,128 @@ public class TbHttpClientTest {
|
|||||||
eventLoop = client.getSharedOrCreateEventLoopGroup(null);
|
eventLoop = client.getSharedOrCreateEventLoopGroup(null);
|
||||||
assertThat(eventLoop, instanceOf(NioEventLoopGroup.class));
|
assertThat(eventLoop, instanceOf(NioEventLoopGroup.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuildSimpleUri() {
|
||||||
|
Mockito.when(client.buildEncodedUri(any())).thenCallRealMethod();
|
||||||
|
String url = "http://localhost:8080/";
|
||||||
|
URI uri = client.buildEncodedUri(url);
|
||||||
|
Assert.assertEquals(url, uri.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuildUriWithoutProtocol() {
|
||||||
|
Mockito.when(client.buildEncodedUri(any())).thenCallRealMethod();
|
||||||
|
String url = "localhost:8080/";
|
||||||
|
assertThatThrownBy(() -> client.buildEncodedUri(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuildInvalidUri() {
|
||||||
|
Mockito.when(client.buildEncodedUri(any())).thenCallRealMethod();
|
||||||
|
String url = "aaa";
|
||||||
|
assertThatThrownBy(() -> client.buildEncodedUri(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuildUriWithSpecialSymbols() {
|
||||||
|
Mockito.when(client.buildEncodedUri(any())).thenCallRealMethod();
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProcessMessageWithJsonInUrlVariable() throws Exception {
|
||||||
|
String host = "localhost";
|
||||||
|
String path = "/api";
|
||||||
|
String paramKey = "data";
|
||||||
|
String paramVal = "[{\"test\":\"test\"}]";
|
||||||
|
String successResponseBody = "SUCCESS";
|
||||||
|
|
||||||
|
var server = setUpDummyServer(host, path, paramKey, paramVal, successResponseBody);
|
||||||
|
|
||||||
|
String endpointUrl = String.format(
|
||||||
|
"http://%s:%d%s?%s=%s",
|
||||||
|
host, server.getPort(), path, paramKey, paramVal
|
||||||
|
);
|
||||||
|
String method = "GET";
|
||||||
|
|
||||||
|
|
||||||
|
var config = new TbRestApiCallNodeConfiguration()
|
||||||
|
.defaultConfiguration();
|
||||||
|
config.setRequestMethod(method);
|
||||||
|
config.setRestEndpointUrlPattern(endpointUrl);
|
||||||
|
config.setUseSimpleClientHttpFactory(true);
|
||||||
|
|
||||||
|
var asyncRestTemplate = new AsyncRestTemplate();
|
||||||
|
|
||||||
|
var httpClient = new TbHttpClient(config, eventLoop);
|
||||||
|
httpClient.setHttpClient(asyncRestTemplate);
|
||||||
|
|
||||||
|
var msg = TbMsg.newMsg(
|
||||||
|
"Main", "GET", new DeviceId(EntityId.NULL_UUID),
|
||||||
|
TbMsgMetaData.EMPTY, "{}"
|
||||||
|
);
|
||||||
|
var successMsg = TbMsg.newMsg(
|
||||||
|
"SUCCESS", msg.getOriginator(),
|
||||||
|
msg.getMetaData(), msg.getData()
|
||||||
|
);
|
||||||
|
|
||||||
|
var ctx = mock(TbContext.class);
|
||||||
|
when(ctx.transformMsg(
|
||||||
|
eq(msg), eq(msg.getType()),
|
||||||
|
eq(msg.getOriginator()),
|
||||||
|
eq(msg.getMetaData()),
|
||||||
|
eq(msg.getData())
|
||||||
|
)).thenReturn(successMsg);
|
||||||
|
|
||||||
|
var capturedData = ArgumentCaptor.forClass(String.class);
|
||||||
|
|
||||||
|
when(ctx.transformMsg(
|
||||||
|
eq(msg), eq(msg.getType()),
|
||||||
|
eq(msg.getOriginator()),
|
||||||
|
any(),
|
||||||
|
capturedData.capture()
|
||||||
|
)).thenReturn(successMsg);
|
||||||
|
|
||||||
|
httpClient.processMessage(ctx, msg);
|
||||||
|
|
||||||
|
Awaitility.await()
|
||||||
|
.atMost(30, TimeUnit.SECONDS)
|
||||||
|
.until(() -> {
|
||||||
|
try {
|
||||||
|
verify(ctx, times(1)).tellSuccess(any());
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
verify(ctx, times(1)).tellSuccess(any());
|
||||||
|
verify(ctx, times(0)).tellFailure(any(), any());
|
||||||
|
Assert.assertEquals(successResponseBody, capturedData.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientAndServer setUpDummyServer(String host, String path, String paramKey, String paramVal, String successResponseBody) {
|
||||||
|
var server = startClientAndServer(host, 1080);
|
||||||
|
createGetMethodExpectations(server, path, paramKey, paramVal, successResponseBody);
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createGetMethodExpectations(ClientAndServer server, String path, String paramKey, String paramVal, String successResponseBody) {
|
||||||
|
server.when(
|
||||||
|
request()
|
||||||
|
.withMethod("GET")
|
||||||
|
.withPath(path)
|
||||||
|
.withQueryStringParameter(paramKey, paramVal)
|
||||||
|
).respond(
|
||||||
|
response()
|
||||||
|
.withStatusCode(200)
|
||||||
|
.withBody(successResponseBody)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user