Commit 65ad86bedb3713d896707edb86a83dfaf1bad51d

Authored by Andrii Shvaika
1 parent 940c81b0

RPC v2 to switch from 409 to 504 error code

application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java renamed from application/src/main/java/org/thingsboard/server/controller/RpcController.java
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at 6 * You may obtain a copy of the License at
7 * 7 *
8 - * http://www.apache.org/licenses/LICENSE-2.0 8 + * http://www.apache.org/licenses/LICENSE-2.0
9 * 9 *
10 * Unless required by applicable law or agreed to in writing, software 10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, 11 * distributed under the License is distributed on an "AS IS" BASIS,
@@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
19 -import com.fasterxml.jackson.databind.ObjectMapper;  
20 import com.google.common.util.concurrent.FutureCallback; 19 import com.google.common.util.concurrent.FutureCallback;
21 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
22 import org.springframework.beans.factory.annotation.Autowired; 21 import org.springframework.beans.factory.annotation.Autowired;
@@ -26,13 +25,12 @@ import org.springframework.http.ResponseEntity; @@ -26,13 +25,12 @@ import org.springframework.http.ResponseEntity;
26 import org.springframework.security.access.prepost.PreAuthorize; 25 import org.springframework.security.access.prepost.PreAuthorize;
27 import org.springframework.util.StringUtils; 26 import org.springframework.util.StringUtils;
28 import org.springframework.web.bind.annotation.PathVariable; 27 import org.springframework.web.bind.annotation.PathVariable;
29 -import org.springframework.web.bind.annotation.RequestBody;  
30 import org.springframework.web.bind.annotation.RequestMapping; 28 import org.springframework.web.bind.annotation.RequestMapping;
31 import org.springframework.web.bind.annotation.RequestMethod; 29 import org.springframework.web.bind.annotation.RequestMethod;
32 import org.springframework.web.bind.annotation.RequestParam; 30 import org.springframework.web.bind.annotation.RequestParam;
33 import org.springframework.web.bind.annotation.ResponseBody; 31 import org.springframework.web.bind.annotation.ResponseBody;
34 -import org.springframework.web.bind.annotation.RestController;  
35 import org.springframework.web.context.request.async.DeferredResult; 32 import org.springframework.web.context.request.async.DeferredResult;
  33 +import org.thingsboard.common.util.JacksonUtil;
36 import org.thingsboard.rule.engine.api.RpcError; 34 import org.thingsboard.rule.engine.api.RpcError;
37 import org.thingsboard.server.common.data.DataConstants; 35 import org.thingsboard.server.common.data.DataConstants;
38 import org.thingsboard.server.common.data.audit.ActionType; 36 import org.thingsboard.server.common.data.audit.ActionType;
@@ -59,20 +57,15 @@ import org.thingsboard.server.service.security.permission.Operation; @@ -59,20 +57,15 @@ import org.thingsboard.server.service.security.permission.Operation;
59 import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity; 57 import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity;
60 58
61 import javax.annotation.Nullable; 59 import javax.annotation.Nullable;
62 -import java.io.IOException;  
63 import java.util.Optional; 60 import java.util.Optional;
64 import java.util.UUID; 61 import java.util.UUID;
65 62
66 /** 63 /**
67 * Created by ashvayka on 22.03.18. 64 * Created by ashvayka on 22.03.18.
68 */ 65 */
69 -@RestController  
70 @TbCoreComponent 66 @TbCoreComponent
71 -@RequestMapping(TbUrlConstants.RPC_URL_PREFIX)  
72 @Slf4j 67 @Slf4j
73 -public class RpcController extends BaseController {  
74 -  
75 - protected final ObjectMapper jsonMapper = new ObjectMapper(); 68 +public abstract class AbstractRpcController extends BaseController {
76 69
77 @Autowired 70 @Autowired
78 private TbCoreDeviceRpcService deviceRpcService; 71 private TbCoreDeviceRpcService deviceRpcService;
@@ -81,75 +74,15 @@ public class RpcController extends BaseController { @@ -81,75 +74,15 @@ public class RpcController extends BaseController {
81 private AccessValidator accessValidator; 74 private AccessValidator accessValidator;
82 75
83 @Value("${server.rest.server_side_rpc.min_timeout:5000}") 76 @Value("${server.rest.server_side_rpc.min_timeout:5000}")
84 - private long minTimeout; 77 + protected long minTimeout;
85 78
86 @Value("${server.rest.server_side_rpc.default_timeout:10000}") 79 @Value("${server.rest.server_side_rpc.default_timeout:10000}")
87 - private long defaultTimeout;  
88 -  
89 - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")  
90 - @RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)  
91 - @ResponseBody  
92 - public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {  
93 - return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody);  
94 - }  
95 -  
96 - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")  
97 - @RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST)  
98 - @ResponseBody  
99 - public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {  
100 - return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody);  
101 - }  
102 -  
103 - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")  
104 - @RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.GET)  
105 - @ResponseBody  
106 - public Rpc getPersistedRpc(@PathVariable("rpcId") String strRpc) throws ThingsboardException {  
107 - checkParameter("RpcId", strRpc);  
108 - try {  
109 - RpcId rpcId = new RpcId(UUID.fromString(strRpc));  
110 - return checkRpcId(rpcId, Operation.READ);  
111 - } catch (Exception e) {  
112 - throw handleException(e);  
113 - }  
114 - }  
115 -  
116 - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")  
117 - @RequestMapping(value = "/persistent/device/{deviceId}", method = RequestMethod.GET)  
118 - @ResponseBody  
119 - public PageData<Rpc> getPersistedRpcByDevice(@PathVariable("deviceId") String strDeviceId,  
120 - @RequestParam int pageSize,  
121 - @RequestParam int page,  
122 - @RequestParam RpcStatus rpcStatus,  
123 - @RequestParam(required = false) String textSearch,  
124 - @RequestParam(required = false) String sortProperty,  
125 - @RequestParam(required = false) String sortOrder) throws ThingsboardException {  
126 - checkParameter("DeviceId", strDeviceId);  
127 - try {  
128 - TenantId tenantId = getCurrentUser().getTenantId();  
129 - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);  
130 - DeviceId deviceId = new DeviceId(UUID.fromString(strDeviceId));  
131 - return checkNotNull(rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink));  
132 - } catch (Exception e) {  
133 - throw handleException(e);  
134 - }  
135 - }  
136 -  
137 - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")  
138 - @RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.DELETE)  
139 - @ResponseBody  
140 - public void deleteResource(@PathVariable("rpcId") String strRpc) throws ThingsboardException {  
141 - checkParameter("RpcId", strRpc);  
142 - try {  
143 - rpcService.deleteRpc(getTenantId(), new RpcId(UUID.fromString(strRpc)));  
144 - } catch (Exception e) {  
145 - throw handleException(e);  
146 - }  
147 - } 80 + protected long defaultTimeout;
148 81
149 - private DeferredResult<ResponseEntity> handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody) throws ThingsboardException { 82 + protected DeferredResult<ResponseEntity> handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody, HttpStatus timeoutStatus) throws ThingsboardException {
150 try { 83 try {
151 - JsonNode rpcRequestBody = jsonMapper.readTree(requestBody);  
152 - ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(rpcRequestBody.get("method").asText(), jsonMapper.writeValueAsString(rpcRequestBody.get("params"))); 84 + JsonNode rpcRequestBody = JacksonUtil.toJsonNode(requestBody);
  85 + ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(rpcRequestBody.get("method").asText(), JacksonUtil.toString(rpcRequestBody.get("params")));
153 SecurityUser currentUser = getCurrentUser(); 86 SecurityUser currentUser = getCurrentUser();
154 TenantId tenantId = currentUser.getTenantId(); 87 TenantId tenantId = currentUser.getTenantId();
155 final DeferredResult<ResponseEntity> response = new DeferredResult<>(); 88 final DeferredResult<ResponseEntity> response = new DeferredResult<>();
@@ -157,7 +90,7 @@ public class RpcController extends BaseController { @@ -157,7 +90,7 @@ public class RpcController extends BaseController {
157 long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout); 90 long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout);
158 UUID rpcRequestUUID = rpcRequestBody.has("requestUUID") ? UUID.fromString(rpcRequestBody.get("requestUUID").asText()) : UUID.randomUUID(); 91 UUID rpcRequestUUID = rpcRequestBody.has("requestUUID") ? UUID.fromString(rpcRequestBody.get("requestUUID").asText()) : UUID.randomUUID();
159 boolean persisted = rpcRequestBody.has(DataConstants.PERSISTENT) && rpcRequestBody.get(DataConstants.PERSISTENT).asBoolean(); 92 boolean persisted = rpcRequestBody.has(DataConstants.PERSISTENT) && rpcRequestBody.get(DataConstants.PERSISTENT).asBoolean();
160 - accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<DeferredResult<ResponseEntity>>() { 93 + accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<>() {
161 @Override 94 @Override
162 public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) { 95 public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) {
163 ToDeviceRpcRequest rpcRequest = new ToDeviceRpcRequest(rpcRequestUUID, 96 ToDeviceRpcRequest rpcRequest = new ToDeviceRpcRequest(rpcRequestUUID,
@@ -168,7 +101,7 @@ public class RpcController extends BaseController { @@ -168,7 +101,7 @@ public class RpcController extends BaseController {
168 body, 101 body,
169 persisted 102 persisted
170 ); 103 );
171 - deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse), currentUser); 104 + deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse, timeoutStatus), currentUser);
172 } 105 }
173 106
174 @Override 107 @Override
@@ -184,12 +117,12 @@ public class RpcController extends BaseController { @@ -184,12 +117,12 @@ public class RpcController extends BaseController {
184 } 117 }
185 })); 118 }));
186 return response; 119 return response;
187 - } catch (IOException ioe) { 120 + } catch (IllegalArgumentException ioe) {
188 throw new ThingsboardException("Invalid request body", ioe, ThingsboardErrorCode.BAD_REQUEST_PARAMS); 121 throw new ThingsboardException("Invalid request body", ioe, ThingsboardErrorCode.BAD_REQUEST_PARAMS);
189 } 122 }
190 } 123 }
191 124
192 - public void reply(LocalRequestMetaData rpcRequest, FromDeviceRpcResponse response) { 125 + public void reply(LocalRequestMetaData rpcRequest, FromDeviceRpcResponse response, HttpStatus timeoutStatus) {
193 Optional<RpcError> rpcError = response.getError(); 126 Optional<RpcError> rpcError = response.getError();
194 DeferredResult<ResponseEntity> responseWriter = rpcRequest.getResponseWriter(); 127 DeferredResult<ResponseEntity> responseWriter = rpcRequest.getResponseWriter();
195 if (rpcError.isPresent()) { 128 if (rpcError.isPresent()) {
@@ -197,13 +130,13 @@ public class RpcController extends BaseController { @@ -197,13 +130,13 @@ public class RpcController extends BaseController {
197 RpcError error = rpcError.get(); 130 RpcError error = rpcError.get();
198 switch (error) { 131 switch (error) {
199 case TIMEOUT: 132 case TIMEOUT:
200 - responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT)); 133 + responseWriter.setResult(new ResponseEntity<>(timeoutStatus));
201 break; 134 break;
202 case NO_ACTIVE_CONNECTION: 135 case NO_ACTIVE_CONNECTION:
203 responseWriter.setResult(new ResponseEntity<>(HttpStatus.CONFLICT)); 136 responseWriter.setResult(new ResponseEntity<>(HttpStatus.CONFLICT));
204 break; 137 break;
205 default: 138 default:
206 - responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT)); 139 + responseWriter.setResult(new ResponseEntity<>(timeoutStatus));
207 break; 140 break;
208 } 141 }
209 } else { 142 } else {
@@ -212,8 +145,8 @@ public class RpcController extends BaseController { @@ -212,8 +145,8 @@ public class RpcController extends BaseController {
212 String data = responseData.get(); 145 String data = responseData.get();
213 try { 146 try {
214 logRpcCall(rpcRequest, rpcError, null); 147 logRpcCall(rpcRequest, rpcError, null);
215 - responseWriter.setResult(new ResponseEntity<>(jsonMapper.readTree(data), HttpStatus.OK));  
216 - } catch (IOException e) { 148 + responseWriter.setResult(new ResponseEntity<>(JacksonUtil.toJsonNode(data), HttpStatus.OK));
  149 + } catch (IllegalArgumentException e) {
217 log.debug("Failed to decode device response: {}", data, e); 150 log.debug("Failed to decode device response: {}", data, e);
218 logRpcCall(rpcRequest, rpcError, e); 151 logRpcCall(rpcRequest, rpcError, e);
219 responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE)); 152 responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE));
  1 +package org.thingsboard.server.controller;
  2 +
  3 +import lombok.extern.slf4j.Slf4j;
  4 +import org.springframework.http.HttpStatus;
  5 +import org.springframework.http.ResponseEntity;
  6 +import org.springframework.security.access.prepost.PreAuthorize;
  7 +import org.springframework.web.bind.annotation.PathVariable;
  8 +import org.springframework.web.bind.annotation.RequestBody;
  9 +import org.springframework.web.bind.annotation.RequestMapping;
  10 +import org.springframework.web.bind.annotation.RequestMethod;
  11 +import org.springframework.web.bind.annotation.ResponseBody;
  12 +import org.springframework.web.bind.annotation.RestController;
  13 +import org.springframework.web.context.request.async.DeferredResult;
  14 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  15 +import org.thingsboard.server.common.data.id.DeviceId;
  16 +import org.thingsboard.server.queue.util.TbCoreComponent;
  17 +
  18 +import java.util.UUID;
  19 +
  20 +@RestController
  21 +@TbCoreComponent
  22 +@RequestMapping(TbUrlConstants.RPC_V1_URL_PREFIX)
  23 +@Slf4j
  24 +public class RpcV1Controller extends AbstractRpcController {
  25 +
  26 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  27 + @RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
  28 + @ResponseBody
  29 + public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
  30 + return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.REQUEST_TIMEOUT);
  31 + }
  32 +
  33 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  34 + @RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST)
  35 + @ResponseBody
  36 + public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
  37 + return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.REQUEST_TIMEOUT);
  38 + }
  39 +
  40 +}
  1 +package org.thingsboard.server.controller;
  2 +
  3 +import lombok.extern.slf4j.Slf4j;
  4 +import org.springframework.http.HttpStatus;
  5 +import org.springframework.http.ResponseEntity;
  6 +import org.springframework.security.access.prepost.PreAuthorize;
  7 +import org.springframework.web.bind.annotation.PathVariable;
  8 +import org.springframework.web.bind.annotation.RequestBody;
  9 +import org.springframework.web.bind.annotation.RequestMapping;
  10 +import org.springframework.web.bind.annotation.RequestMethod;
  11 +import org.springframework.web.bind.annotation.RequestParam;
  12 +import org.springframework.web.bind.annotation.ResponseBody;
  13 +import org.springframework.web.bind.annotation.RestController;
  14 +import org.springframework.web.context.request.async.DeferredResult;
  15 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  16 +import org.thingsboard.server.common.data.id.DeviceId;
  17 +import org.thingsboard.server.common.data.id.RpcId;
  18 +import org.thingsboard.server.common.data.id.TenantId;
  19 +import org.thingsboard.server.common.data.page.PageData;
  20 +import org.thingsboard.server.common.data.page.PageLink;
  21 +import org.thingsboard.server.common.data.rpc.Rpc;
  22 +import org.thingsboard.server.common.data.rpc.RpcStatus;
  23 +import org.thingsboard.server.queue.util.TbCoreComponent;
  24 +import org.thingsboard.server.service.security.permission.Operation;
  25 +
  26 +import java.util.UUID;
  27 +
  28 +@RestController
  29 +@TbCoreComponent
  30 +@RequestMapping(TbUrlConstants.RPC_V2_URL_PREFIX)
  31 +@Slf4j
  32 +public class RpcV2Controller extends AbstractRpcController {
  33 +
  34 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  35 + @RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
  36 + @ResponseBody
  37 + public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
  38 + return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.GATEWAY_TIMEOUT);
  39 + }
  40 +
  41 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  42 + @RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST)
  43 + @ResponseBody
  44 + public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
  45 + return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.GATEWAY_TIMEOUT);
  46 + }
  47 +
  48 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  49 + @RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.GET)
  50 + @ResponseBody
  51 + public Rpc getPersistedRpc(@PathVariable("rpcId") String strRpc) throws ThingsboardException {
  52 + checkParameter("RpcId", strRpc);
  53 + try {
  54 + RpcId rpcId = new RpcId(UUID.fromString(strRpc));
  55 + return checkRpcId(rpcId, Operation.READ);
  56 + } catch (Exception e) {
  57 + throw handleException(e);
  58 + }
  59 + }
  60 +
  61 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  62 + @RequestMapping(value = "/persistent/device/{deviceId}", method = RequestMethod.GET)
  63 + @ResponseBody
  64 + public PageData<Rpc> getPersistedRpcByDevice(@PathVariable("deviceId") String strDeviceId,
  65 + @RequestParam int pageSize,
  66 + @RequestParam int page,
  67 + @RequestParam RpcStatus rpcStatus,
  68 + @RequestParam(required = false) String textSearch,
  69 + @RequestParam(required = false) String sortProperty,
  70 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  71 + checkParameter("DeviceId", strDeviceId);
  72 + try {
  73 + TenantId tenantId = getCurrentUser().getTenantId();
  74 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  75 + DeviceId deviceId = new DeviceId(UUID.fromString(strDeviceId));
  76 + return checkNotNull(rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink));
  77 + } catch (Exception e) {
  78 + throw handleException(e);
  79 + }
  80 + }
  81 +
  82 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  83 + @RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.DELETE)
  84 + @ResponseBody
  85 + public void deleteResource(@PathVariable("rpcId") String strRpc) throws ThingsboardException {
  86 + checkParameter("RpcId", strRpc);
  87 + try {
  88 + rpcService.deleteRpc(getTenantId(), new RpcId(UUID.fromString(strRpc)));
  89 + } catch (Exception e) {
  90 + throw handleException(e);
  91 + }
  92 + }
  93 +}
@@ -20,5 +20,6 @@ package org.thingsboard.server.controller; @@ -20,5 +20,6 @@ package org.thingsboard.server.controller;
20 */ 20 */
21 public class TbUrlConstants { 21 public class TbUrlConstants {
22 public static final String TELEMETRY_URL_PREFIX = "/api/plugins/telemetry"; 22 public static final String TELEMETRY_URL_PREFIX = "/api/plugins/telemetry";
23 - public static final String RPC_URL_PREFIX = "/api/plugins/rpc"; 23 + public static final String RPC_V1_URL_PREFIX = "/api/plugins/rpc";
  24 + public static final String RPC_V2_URL_PREFIX = "/api/rpc";
24 } 25 }
@@ -43,7 +43,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab @@ -43,7 +43,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab
43 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}"; 43 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}";
44 String deviceId = savedDevice.getId().getId().toString(); 44 String deviceId = savedDevice.getId().getId().toString();
45 45
46 - doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409), 46 + doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(504),
47 asyncContextTimeoutToUseRpcPlugin); 47 asyncContextTimeoutToUseRpcPlugin);
48 } 48 }
49 49
@@ -52,7 +52,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab @@ -52,7 +52,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab
52 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}"; 52 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}";
53 String nonExistentDeviceId = Uuids.timeBased().toString(); 53 String nonExistentDeviceId = Uuids.timeBased().toString();
54 54
55 - String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, 55 + String result = doPostAsync("/api/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,
56 status().isNotFound()); 56 status().isNotFound());
57 Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); 57 Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
58 } 58 }
@@ -62,7 +62,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab @@ -62,7 +62,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab
62 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}"; 62 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}";
63 String deviceId = savedDevice.getId().getId().toString(); 63 String deviceId = savedDevice.getId().getId().toString();
64 64
65 - doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409), 65 + doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(504),
66 asyncContextTimeoutToUseRpcPlugin); 66 asyncContextTimeoutToUseRpcPlugin);
67 } 67 }
68 68
@@ -71,7 +71,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab @@ -71,7 +71,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab
71 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}"; 71 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}";
72 String nonExistentDeviceId = Uuids.timeBased().toString(); 72 String nonExistentDeviceId = Uuids.timeBased().toString();
73 73
74 - String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class, 74 + String result = doPostAsync("/api/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,
75 status().isNotFound()); 75 status().isNotFound());
76 Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); 76 Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
77 } 77 }
@@ -71,7 +71,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC @@ -71,7 +71,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
71 71
72 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; 72 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
73 String deviceId = savedDevice.getId().getId().toString(); 73 String deviceId = savedDevice.getId().getId().toString();
74 - String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk()); 74 + String result = doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
75 75
76 latch.await(3, TimeUnit.SECONDS); 76 latch.await(3, TimeUnit.SECONDS);
77 77
@@ -99,14 +99,14 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC @@ -99,14 +99,14 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
99 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}"; 99 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
100 String deviceId = savedDevice.getId().getId().toString(); 100 String deviceId = savedDevice.getId().getId().toString();
101 101
102 - String actualResult = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); 102 + String actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
103 latch.await(3, TimeUnit.SECONDS); 103 latch.await(3, TimeUnit.SECONDS);
104 104
105 validateTwoWayStateChangedNotification(callback, 1, expectedResponseResult, actualResult); 105 validateTwoWayStateChangedNotification(callback, 1, expectedResponseResult, actualResult);
106 106
107 latch = new CountDownLatch(1); 107 latch = new CountDownLatch(1);
108 108
109 - actualResult = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); 109 + actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
110 latch.await(3, TimeUnit.SECONDS); 110 latch.await(3, TimeUnit.SECONDS);
111 111
112 validateTwoWayStateChangedNotification(callback, 2, expectedResponseResult, actualResult); 112 validateTwoWayStateChangedNotification(callback, 2, expectedResponseResult, actualResult);
@@ -46,7 +46,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab @@ -46,7 +46,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab
46 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}"; 46 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}";
47 String deviceId = savedDevice.getId().getId().toString(); 47 String deviceId = savedDevice.getId().getId().toString();
48 48
49 - doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409), 49 + doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(504),
50 asyncContextTimeoutToUseRpcPlugin); 50 asyncContextTimeoutToUseRpcPlugin);
51 } 51 }
52 52
@@ -55,7 +55,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab @@ -55,7 +55,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab
55 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}"; 55 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}";
56 String nonExistentDeviceId = Uuids.timeBased().toString(); 56 String nonExistentDeviceId = Uuids.timeBased().toString();
57 57
58 - String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, 58 + String result = doPostAsync("/api/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,
59 status().isNotFound()); 59 status().isNotFound());
60 Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); 60 Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
61 } 61 }
@@ -65,7 +65,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab @@ -65,7 +65,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab
65 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}"; 65 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}";
66 String deviceId = savedDevice.getId().getId().toString(); 66 String deviceId = savedDevice.getId().getId().toString();
67 67
68 - doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409), 68 + doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(504),
69 asyncContextTimeoutToUseRpcPlugin); 69 asyncContextTimeoutToUseRpcPlugin);
70 } 70 }
71 71
@@ -74,7 +74,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab @@ -74,7 +74,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab
74 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}"; 74 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}";
75 String nonExistentDeviceId = Uuids.timeBased().toString(); 75 String nonExistentDeviceId = Uuids.timeBased().toString();
76 76
77 - String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class, 77 + String result = doPostAsync("/api/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,
78 status().isNotFound()); 78 status().isNotFound());
79 Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); 79 Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
80 } 80 }
@@ -69,7 +69,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM @@ -69,7 +69,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
69 69
70 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; 70 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
71 String deviceId = savedDevice.getId().getId().toString(); 71 String deviceId = savedDevice.getId().getId().toString();
72 - String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk()); 72 + String result = doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
73 Assert.assertTrue(StringUtils.isEmpty(result)); 73 Assert.assertTrue(StringUtils.isEmpty(result));
74 latch.await(3, TimeUnit.SECONDS); 74 latch.await(3, TimeUnit.SECONDS);
75 assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); 75 assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
@@ -95,7 +95,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM @@ -95,7 +95,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
95 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}"; 95 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
96 String deviceId = savedDevice.getId().getId().toString(); 96 String deviceId = savedDevice.getId().getId().toString();
97 97
98 - String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); 98 + String result = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
99 String expected = "{\"value1\":\"A\",\"value2\":\"B\"}"; 99 String expected = "{\"value1\":\"A\",\"value2\":\"B\"}";
100 latch.await(3, TimeUnit.SECONDS); 100 latch.await(3, TimeUnit.SECONDS);
101 Assert.assertEquals(expected, result); 101 Assert.assertEquals(expected, result);
@@ -130,7 +130,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM @@ -130,7 +130,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
130 130
131 String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}"; 131 String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}";
132 String deviceId = savedDevice.getId().getId().toString(); 132 String deviceId = savedDevice.getId().getId().toString();
133 - String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk()); 133 + String result = doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
134 Assert.assertTrue(StringUtils.isEmpty(result)); 134 Assert.assertTrue(StringUtils.isEmpty(result));
135 latch.await(3, TimeUnit.SECONDS); 135 latch.await(3, TimeUnit.SECONDS);
136 assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); 136 assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
@@ -156,7 +156,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM @@ -156,7 +156,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
156 156
157 String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}"; 157 String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}";
158 String deviceId = savedDevice.getId().getId().toString(); 158 String deviceId = savedDevice.getId().getId().toString();
159 - String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); 159 + String result = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
160 latch.await(3, TimeUnit.SECONDS); 160 latch.await(3, TimeUnit.SECONDS);
161 String expected = "{\"success\":true}"; 161 String expected = "{\"success\":true}";
162 assertEquals(expected, result); 162 assertEquals(expected, result);
@@ -131,7 +131,7 @@ public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends Abst @@ -131,7 +131,7 @@ public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends Abst
131 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}"; 131 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
132 String deviceId = savedDevice.getId().getId().toString(); 132 String deviceId = savedDevice.getId().getId().toString();
133 133
134 - String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); 134 + String result = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
135 String expected = "{\"payload\":\"{\\\"value1\\\":\\\"A\\\",\\\"value2\\\":\\\"B\\\"}\"}"; 135 String expected = "{\"payload\":\"{\\\"value1\\\":\\\"A\\\",\\\"value2\\\":\\\"B\\\"}\"}";
136 latch.await(3, TimeUnit.SECONDS); 136 latch.await(3, TimeUnit.SECONDS);
137 Assert.assertEquals(expected, result); 137 Assert.assertEquals(expected, result);
@@ -267,7 +267,7 @@ public class MqttClientTest extends AbstractContainerTest { @@ -267,7 +267,7 @@ public class MqttClientTest extends AbstractContainerTest {
267 ListenableFuture<ResponseEntity> future = service.submit(() -> { 267 ListenableFuture<ResponseEntity> future = service.submit(() -> {
268 try { 268 try {
269 return restClient.getRestTemplate() 269 return restClient.getRestTemplate()
270 - .postForEntity(HTTPS_URL + "/api/plugins/rpc/twoway/{deviceId}", 270 + .postForEntity(HTTPS_URL + "/api/rpc/twoway/{deviceId}",
271 mapper.readTree(serverRpcPayload.toString()), String.class, 271 mapper.readTree(serverRpcPayload.toString()), String.class,
272 device.getId()); 272 device.getId());
273 } catch (IOException e) { 273 } catch (IOException e) {
@@ -263,7 +263,7 @@ public class MqttGatewayClientTest extends AbstractContainerTest { @@ -263,7 +263,7 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
263 ListenableFuture<ResponseEntity> future = service.submit(() -> { 263 ListenableFuture<ResponseEntity> future = service.submit(() -> {
264 try { 264 try {
265 return restClient.getRestTemplate() 265 return restClient.getRestTemplate()
266 - .postForEntity(HTTPS_URL + "/api/plugins/rpc/twoway/{deviceId}", 266 + .postForEntity(HTTPS_URL + "/api/rpc/twoway/{deviceId}",
267 mapper.readTree(serverRpcPayload.toString()), String.class, 267 mapper.readTree(serverRpcPayload.toString()), String.class,
268 createdDevice.getId()); 268 createdDevice.getId());
269 } catch (IOException e) { 269 } catch (IOException e) {
@@ -1811,12 +1811,12 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { @@ -1811,12 +1811,12 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable {
1811 } 1811 }
1812 1812
1813 public void handleOneWayDeviceRPCRequest(DeviceId deviceId, JsonNode requestBody) { 1813 public void handleOneWayDeviceRPCRequest(DeviceId deviceId, JsonNode requestBody) {
1814 - restTemplate.postForLocation(baseURL + "/api/plugins/rpc/oneway/{deviceId}", requestBody, deviceId.getId()); 1814 + restTemplate.postForLocation(baseURL + "/api/rpc/oneway/{deviceId}", requestBody, deviceId.getId());
1815 } 1815 }
1816 1816
1817 public JsonNode handleTwoWayDeviceRPCRequest(DeviceId deviceId, JsonNode requestBody) { 1817 public JsonNode handleTwoWayDeviceRPCRequest(DeviceId deviceId, JsonNode requestBody) {
1818 return restTemplate.exchange( 1818 return restTemplate.exchange(
1819 - baseURL + "/api/plugins/rpc/twoway/{deviceId}", 1819 + baseURL + "/api/rpc/twoway/{deviceId}",
1820 HttpMethod.POST, 1820 HttpMethod.POST,
1821 new HttpEntity<>(requestBody), 1821 new HttpEntity<>(requestBody),
1822 new ParameterizedTypeReference<JsonNode>() { 1822 new ParameterizedTypeReference<JsonNode>() {
@@ -130,11 +130,11 @@ export class DeviceService { @@ -130,11 +130,11 @@ export class DeviceService {
130 } 130 }
131 131
132 public sendOneWayRpcCommand(deviceId: string, requestBody: any, config?: RequestConfig): Observable<any> { 132 public sendOneWayRpcCommand(deviceId: string, requestBody: any, config?: RequestConfig): Observable<any> {
133 - return this.http.post<Device>(`/api/plugins/rpc/oneway/${deviceId}`, requestBody, defaultHttpOptionsFromConfig(config)); 133 + return this.http.post<Device>(`/api/rpc/oneway/${deviceId}`, requestBody, defaultHttpOptionsFromConfig(config));
134 } 134 }
135 135
136 public sendTwoWayRpcCommand(deviceId: string, requestBody: any, config?: RequestConfig): Observable<any> { 136 public sendTwoWayRpcCommand(deviceId: string, requestBody: any, config?: RequestConfig): Observable<any> {
137 - return this.http.post<Device>(`/api/plugins/rpc/twoway/${deviceId}`, requestBody, defaultHttpOptionsFromConfig(config)); 137 + return this.http.post<Device>(`/api/rpc/twoway/${deviceId}`, requestBody, defaultHttpOptionsFromConfig(config));
138 } 138 }
139 139
140 public findByQuery(query: DeviceSearchQuery, 140 public findByQuery(query: DeviceSearchQuery,
@@ -47,7 +47,7 @@ export class GlobalHttpInterceptor implements HttpInterceptor { @@ -47,7 +47,7 @@ export class GlobalHttpInterceptor implements HttpInterceptor {
47 47
48 private internalUrlPrefixes = [ 48 private internalUrlPrefixes = [
49 '/api/auth/token', 49 '/api/auth/token',
50 - '/api/plugins/rpc' 50 + '/api/rpc'
51 ]; 51 ];
52 52
53 private activeRequests = 0; 53 private activeRequests = 0;
@@ -142,7 +142,7 @@ export class GlobalHttpInterceptor implements HttpInterceptor { @@ -142,7 +142,7 @@ export class GlobalHttpInterceptor implements HttpInterceptor {
142 } 142 }
143 } else if (errorResponse.status === 0 || errorResponse.status === -1) { 143 } else if (errorResponse.status === 0 || errorResponse.status === -1) {
144 this.showError('Unable to connect'); 144 this.showError('Unable to connect');
145 - } else if (!req.url.startsWith('/api/plugins/rpc')) { 145 + } else if (!req.url.startsWith('/api/plugins/rpc') && !req.url.startsWith('/api/rpc')) {
146 if (errorResponse.status === 404) { 146 if (errorResponse.status === 404) {
147 if (!ignoreErrors) { 147 if (!ignoreErrors) {
148 this.showError(req.method + ': ' + req.url + '<br/>' + 148 this.showError(req.method + ': ' + req.url + '<br/>' +